Close

Something to Steal

A project log for RIP OFF for the PDP-1

RIP OFF was released as an arcade video game by Cinematronics in 1980. I am going to make a version of RIP OFF for the PDP-1.

michael-gardiMichael Gardi 07/06/2026 at 21:360 Comments

I was sorely tempted to move onto creating the enemy tanks so I would have something to shoot at (followed by explosions), but instead I opted to do something simpler and created the "fuel cells". In the original game there are eight of them placed as follows at the beginning of a game.

Since I was making the tanks a little different, I thought I would change the shape of the fuel cells too. I tried circles first thinking they would represent an overhead view of "oil drums", but I did not like the look of the circles at the scale required. On a raster display it's hard to get them to look sufficiently round. I also tried square and hex shapes, but at the end of the day the triangles just looked the best. I still might change but for now it's triangles.

I used the same technique as with tank drawing and generated code to efficiently render the triangles based on a central x,y coordinate.

/ Offsets in screen coordinates.
one,    000400            / 1 pixel.
two,    001000            / 2 pixels
tre,    001400            / 3 pixels.
for,    002000            / 4 pixels.
fiv,    002400            / 5 pixels.
six,    003000            / 6 pixels.

/ Draw triangle center coordinates.
tx,    0
ty,    0

/ Draw a right pointing triangle. Center: X in tx, Y in ty.
dtr,   dap drx

       lac tx
       lio ty

       sub six
       sub two
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       add six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       add six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       add six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       sub six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       sub six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       sub six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       ioh
drx,   jmp .

/ Draw a left pointing triangle. Center: X in tx, Y in ty.
dtl,   dap dlx

       lac tx
       lio ty

       add six
       add two
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       sub six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       sub six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       sub six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       add six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       add six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       add six
       swap
       sub six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       swap
       add six
       swap
       ioh
       dpy-i 4200

       ioh
dlx,   jmp .

 I setup some constants with the coordinates of the fuel cells at the start of the game.

/ Initial fuel cell coordinates.
fcx,    746777
        000000
        031000
        760777
        017000
        746777
        000000
        031000
        000001            / End of data marker.

fcy,    036000
        024000
        036000
        000000
        000000
        741777
        753777
        741777

In anticipation of having these cells move around the screen during the game I created "active" fuel cell tables.

/ Active fuel cell coordinates.
acx,    0
        0
        0
        0
        0
        0
        0
        0
        0    

acy,    0
        0
        0
        0
        0
        0
        0
        0

At the beginning of a game I copy the initial coordinates to the active coordinates.

        init cfi,fcx     / Setup the coordinate table pointers.    
        init cfo,acx 
        lac (21          / Copy 17 initial cell coordinates to active.
        dac \tmp
cfi,    lio .            / Get the next initial coordinate.
cfo,    dio .            / Set the next active coordinate.
        lac \tmp
        sub (1
        sza i            / Is the value zero?
        jmp fr0          /  Yes - Go to main loop.
        dac \tmp         /   No - Save counter.
        idx cfi          / Increment table pointers.
        idx cfo
        jmp cfi          / Next coordinate.

fr0,

A couple of things about this. With the PDP-1, it is often the case that the only way to do a task efficiently is to use self modifying code.  Since the PDP-1 did not have any indexed addressing modes for instance, the only way to "copy" these tables is to modify the addresses of the specific "load" (lio) and "save" (dio) instructions. 

First the init macro gets the address at a specific label then inserts that address into an instruction at another label.

define init A,B
        law B
        dap A
        term

So the address of the initial coordinate table (fcx)  is inserted into the lio instruction at cfi, and the address of the active coordinates (acx) is inserted into the dio instruction at cfo.

Furthermore, each time through the copy loop, the addresses at cfi and cfo are incremented by 1 via an idx instruction.

Easy peezy. 

With the active fuel cell table updated a new subroutine was added to draw the fuel cells on the screen. It uses the same technique to step through the active cells.

/ Draw the fuel cells.
dfc,    dap dfx

        init in1,acx     / Setup pointers to the fuel cell tables.
        init in2,acy
        init ex1,fcd

in1,    lac .            / Get the X center.
        sub (000001      / Check for end of file marker.
        sza i            / Is the value zero?
        jmp dfx          /  Yes - Done.
        add (000001
        dac tx           /   No - Set X center.
in2,    lio .            / Get the Y center.
        dio ty           / Set the Y center.
ex1,    xct .            / Show the triangle as left or right facing.
    
        idx in1          / Advance to the next fuel cell.
        idx in2
        idx ex1

        jmp in1          / Draw the next fuel cell.
dfx,    jmp .

One more trick here that involves the "ex1,  xct ." line of code.  That line is initialized with the address of the following table ...

/ Fuel cells have either a left or right facing orientation.
fcd,    jsp dtl
        jsp dtl
        jsp dtr
        jsp dtl
        jsp dtr
        jsp dtl
        jsp dtr
        jsp dtr    

... which contains a list of subroutine calls (jsp) to either the left or right facing drawing routines. The PDP-1 xct instruction executes the instruction that it's address points to as if it were "inline" at the point xct is executed. 

So the fcd table's address is set into the xct instruction and for each fuel cell the appropriate drawing routine is called. 

Ya this stuff makes my head hurt a little too, but it's really cool when it works.

Finally here is my main loop now with the call to draw the triangles.

fr0,    load \ict, -4500    / Initial instruction budget (delay).

        idx \frc            / Increment frame counter.
        jsp tnk             / Draw and control the player's tank.
        jsp dfc             / Draw fuel cells.
        jsp pop             / Check to see if a shell has been fired.
        jsp mov             / Move any active shells.

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

Discussions