Close

Moving The LEM

A project log for Lunar Lander for the PDP-1

My PDP-1 Replica (PiDP-1) from Obsolescence Guaranteed has arrived and I want to do something cool with it.

michael-gardiMichael Gardi 12/23/2025 at 22:090 Comments

Getting the LEM to show up on the screen was cool but pretty boring. I decided that my next task was to get the LEM moving around a little. Since this game is supposed to represent a moon landing I thought gravity might be a good place to start.

Acceleration due to gravity on the moon is 1.625 meters per second squared (m/s²).  It's pretty straight forward.

Time (t) in seconds Velocity (V) in m/s
00.00
11.625
23.25
34.875
......

For each second the velocity of the LEM increases by 1.625 meters (m/s). So I will have to have a "one second" loop. Each time through the loop I would increase the velocity by the acceleration, then move the LEM downward by "velocity" meters. Simple right? Only three problems:

  1. How do you implement a timing loop on a PDP-1 with no built-in timer circuits or hardware assistance for general purpose timing?
  2. What does a "meter" mean in the game?
  3. How do you express 1.625 on a system that has no floating point numbers?

Game Space

I'll start with 2 first. A meter in game space is whatever I want it to be. My first thought was to let each "pixel" on the screen represent a 10 x 10 meter square. Since the screen has 1024 x 1024 pixels, the game space would then be 10,240 x 10,240 meters square or about 10 km square.  It felt about right to have the LEM make its final decent from a 10 km height. Then I realized that converting from game space to screen space would be an issue with no HW division so I adjusted my pixel "size" to be 8 x 8 meters so that converting to screen from game space could be accomplished with a simple "shift right 3" (divide by 8) operation.  

The only elephant in the room with this scheme is that the LEM, which is being drawn right now in about a 30 x 30 pixel grid, would be 240 meters x 240 meters in game space. An actual LEM is only 7 meters tall and 9.4 meters wide or about 1 pixel in screen space, not very interesting. At this point it really sinks in that this is just a GAME. As such I can simply state that the LEM on screen is being viewed through a telescope. There fixed. I'm sure there will be many other tweaks to "reality" made in order to get Lunar Lander working on a PDP-1.  

Timing Loop

The "standard" PDP-1 did not have a default timer interrupt; this function required an optional hardware module. Since I am writing towards a basic PDP-1, I will need another way to create a "one second" timing loop. 

A timing loop on the PDP-1 computer with no HW timer relies on the fixed execution times of instructions, which are multiples of the basic 5-microsecond memory cycle. You can create a fairly accurate delay loop of any length using this technique, but the problem is that It's hard to do anything else while the delay is running. Not very useful for a high speed interactive game.

It should be no surprise that I will adopt Norbert Landsteiner's approach in the ICSS code. Based on his code (copied, cough, cough) my loop looks something like:

define load A,B
     lio (B              / Load the constant B into the IO register.
     A                   / Save that value into A.
     term

define count A,B
     isp A               / Increase the value of A by 1 and skip
     jmp B               /  next instruction of result >= 0.
     term

sec, 12                  / Initialize to 10 loops per second.

/ Main loop. 
fr0, load \ict, -1       / Will loop about every 100 milliseconds.
    
     lac sec             / Get the second count.
     sub (1              / Reduce by one.
     sza i               / Not Zero?
     jmp dsc             /  No - Do the one second code.
     dac sec             / Yes - Save the new second count.
     jmp acu             / Continue with acceleration calculation.

dsc, lac (12             / Reset the second counter.
     dac sec             

/ Put second code here.

/ Velocity calculations.
acu, lac vy              / Increase velocity by the acceleration of moon's gravity.
     add acc
     dac vy

acu, ...                 / Rest of main loop goes here.

cnt, count \ict, .       / Use up rest of time of main loop.
    
     jmp fr0             / Next frame.

Note that at the beginning of the loop a variable ict is being set to -1. At the end there is a corresponding count macro instruction which increases ict in place by 1  until ict reaches 0 then exits. In between the code for the game loop is being executed.

Having such a structure allows you to "tune" the loop's duration by changing the value of ict. At this point in my development I have no idea how long my game code will take to execute each loop because its not done yet. So for now I have ict set to -1, no delay. When the game is closer to completion I will adjust ict to give me roughly a 100 ms loop time. I say roughly because there will be variations in the flow of execution each loop. Time will tell how well this scheme works out.

Fixed Point

Emulating floating point on a system with just integers has a well known solution, fixed-point.  Fixed-point works by representing floating-point numbers using otherwise normal integer data types and arbitrarily dividing the bits into whole number (integer) and decimal (fractional) parts.

For example, if we imagine the bits in an 18-bit integer like the PDP-1's:

Whole Number     Fraction 
000000000001      101000

The above shows the 12:6 fixed-point representation of the number 1.625. This is how I will represent 1.625 for the moon's gravity and is another number that will have to be tuned when the game is closer to completion. 

Let There Be Gravity

So with these three issues at least understood I added an initial cut at implementing a gravity effect. 

It seems to be working as expected. The LEM starts out at "a height of 8 km" with zero velocity and begins to fall with the velocity smoothly increasing as it makes its way downward.  I love the comet like contrail which gets bigger as velocity increases thanks to the long persistence of the Type 30 phosphor (expertly rendered by the PiDP-1 virtual screen).

Discussions