Close

Pew! Pew! Pew! Pew!​

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 06/28/2026 at 01:050 Comments

There is a hard and fast game creation rule:

"When you create something that can shoot, you must immediately make it shoot."

Well maybe not :-), but for sure it's what I do next.

In the original RIP OFF game there are two ways to defend the precious fuel capsules. You can:

  1. Crash into the enemy tanks that are trying to steal your fuel, explosively destroying both them and yourself. Any nearby fuel capsules will be unharmed. The penalty for this action is the few seconds that you are offline and defenseless until you respawn.
  2. Shoot the enemy with the tank's main gun. There can only be a maximum of four shells active at any given time.

Since the game is limited to four shells, I didn't have to get too fancy with the implementation. I started by setting aside some memory to hold the shell's  data.

/ Tank shells. Only 4 concurrent tank shells allowed.
s1,    0            / Shell 1. Life,x,y,dx,dy.
x1,    0
y1,    0
dx1,   0
dy1,   0
s2,    0            / Shell 2. Life,x,y,dx,dy.
x2,    0
y2,    0
dx2,   0
dy2,   0
s3,    0            / Shell 2. Life,x,y,dx,dy.
x3,    0
y3,    0
dx3,   0
dy3,   0
s4,    0            / Shell 4. Life,x,y,dx,dy.
x4,    0
y4,    0
dx4,   0
dy4,   0

slf,   -77          / Initial shell time to live.

Each shell has:

Some code was added to detect when the fire button on the controller was pressed. A fire flag is set only when the button state goes from off to on. That is to say the button must be released before the next shell can be launched.

        lac pin        / Check fire button off in previous read.
        and (040000      
        sza            / Is button off?
        jmp nfr        /   No - Skip checking for fire bit.
    
        ril 1s         / Parse fire input.
        spi            / Is the fire bit set?
        stf 4          /  Yes - Set flag 4 for fire.
nfr,

A section of code was added to "launch" a shell if the fire flag is set. This is called from the main loop.

/ Check to see if a shell has been fired.
pop,    dap pox                 / Set return address.
        szf i 4                 / Is flag 4 set? 
        jmp pox                 /   No - Return.
        clf 4                   / Clear the fire flag.
    
        lac \sn                 / Calculate shell vector x.
        sar 6s
        cma    
        dac \fdx

        lac \cs                 / Calculate shell vector y.
        sar 6s    
        dac \fdy

cs1,    lac s1                  /  Yes - Check shell 1. 
        sza                     / Is shell 1 available?
        jmp cs2                 /   No - Check shell 2.    
        fire s1,x1,y1,dx1,dy1   / Start shell in slot 1.
        jmp pox                 / Exit.

cs2,    lac s2                  /  Yes - Check shell 2. 
        sza                     / Is shell 2 available?
        jmp cs3                 /   No - Check shell 2.    
        fire s2,x2,y2,dx2,dy2   / Start shell in slot 2.
        jmp pox                 / Exit.

cs3,    lac s3                  /  Yes - Check shell 3. 
        sza                     / Is shell 3 available?
        jmp cs4                 /   No - Check shell 4.    
        fire s3,x3,y3,dx3,dy3   / Start shell in slot 3.
        jmp pox                 / Exit.

cs4,    lac s4                  /  Yes - Check shell 4. 
        sza                     / Is shell 4 available?
        jmp pox                 /   No - Return.    
        fire s4,x4,y4,dx4,dy4   / Start shell in slot 4.
pox,    jmp .                   / Return.

If the fire flag is set it is cleared and a vector (dx, dy) is calculated that controls the shell's speed and direction based on the current orientation of the tank. Then a search begins for a shell "slot" that is not being used based on the time to live being 0.  If an unused slot is found it is activated via the "fire" macro.

/ Fire a shell!
define fire LIFE,X,Y,DX,DY
    lac slf                / Get the shell time to live.
    dac LIFE               / Set the shell life.
    lac \fx                / Set the starting x,y coordinates.
    dac X
    lac \fy
    dac Y
    lac \fdx               / Set the direction vector.
    dac DX
    lac \fdy
    dac DY
    term 

Here the time to live, x,y coordinates, and dx,dy deltas are set. 

To move the shells another subroutine was written that gets called each time through the main loop of the game.

/ Move all active shells.
mov,  dap mvx                   / Set return address.
      lac s1                    / Check shell 1. 
      sma                       / Is shell 1 active?
      jmp mv2                   /   No - Check shell 2.    
      move s1,x1,y1,dx1,dy1     /  Yes - Move shell 1 one position.

mv2,  lac s2                    / Check shell 2. 
      sma                       / Is shell 2 active?
      jmp mv3                   /   No - Check shell 3.    
      move s2,x2,y2,dx2,dy2     /  Yes - Move shell 2 one position.

mv3,  lac s3                    / Check shell 3. 
      sma                       / Is shell 3 active?
      jmp mv4                   /   No - Check shell 4.    
      move s3,x3,y3,dx3,dy3     /  Yes - Move shell 3 one position.

mv4,  lac s4                    / Check shell 4. 
      sma                       / Is shell 4 active?
      jmp mvx                   /   No - Return.    
      move s4,x4,y4,dx4,dy4     /  Yes - Move shell 4 one position.
mvx,  jmp .

Each shell with a non-zero time to live is advanced one position based on the dx,dy values via the "move" macro.

/ Move the shell one step on the screen!
define move LIFE,X,Y,DX,DY
    lac LIFE         / Reduce time to live by 1.
    add (1            
    dac LIFE

    lac X            / Show the shell on screen.
    lio Y
    dpy-i 300

    add DX           / Increment the X coordinate.
    dac X

    swap             / Increment the Y coordinate.
    add DY
    dac Y

    term 

The TTL is decremented by one, the shell is displayed,  and dx and dy are added to the x and y coordinates for the next loop.  It's not fancy but it gets the job done. 

My main loop so far.

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

        idx \frc          / Increment frame counter.
        jsp tnk           / Draw and control the player's tank.
        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.

 Here is what it looks like.

Discussions