Close
0%
0%

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.

Similar projects worth following
Back in the early 80s I was at the University of Waterloo. The Campus Center (now called the Student Life Center) had a pub called the BOMBSHELTER, which in turn had a stand up arcade version of RIP OFF. Needless to say I dropped just a "few" quarters into that machine. Today I have a stand up RIP OFF game in my basement workspace at home. I am going to try to recreate some of that arcade magic on a PDP-1 reproduction from 1960.

The Game

If you are interested in how to play RIP OFF, here is a pretty good YouTube video demonstrating single player mode.

It gets frantic in the more advanced levels.

I guess because of Spacewar!, Asteroids, and Lunar Lander I always thought of the players and enemies in RIP OFF as "space ships".  Even when the player I was controlling in the game came to an immediate halt when I stopped pressing the "FORWARD" button, I didn't clue into the fact that I was actually controlling futuristic tanks.

RIP OFF Arcade Cabinet Controls

I didn't realize this until I was reading some reviews of RIP OFF for this project.  To make matters worse here is the cabinet art.

Cabinet Side Panel Artwork

Tank treads! What an idiot I am. That was like a forty plus year brain fart on my part.

Inspiration

So aside from the fact that I like the game RIP OFF, there are a few other reasons that I think it might be a good fit for the PDP-1:

  • Plain black background.
  • Small number of simplistic white "sprites".
  • Little text.
  • One or two players (cooperative in two player mode).
  • Only four button controls for each player left, right, thrust, and fire (perfect for the Spacewar! controllers).

My concerns are:

  • The original game is very fast paced (leading to persistence issues).
  • Converting the original vector graphics to bitmaps will be difficult.
  • Sound adds a lot to the gameplay. It will be missed.

As with my Lunar Lander for the PDP-1 I will have to simplify everything quite a bit and hope that it doesn't affect the game play too much.

  • Something to Steal

    Michael Gardi07/06/2026 at 21:36 0 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.
         ...
    Read more »

  • Pew! Pew! Pew! Pew!​

    Michael Gardi06/28/2026 at 01:05 0 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:

    • A time to live (TTL) value which is 0 when a shell is inactive and set to a fixed negative value (slf) when the shell is "fired".   TTL is incremented for each step the shell takes across the screen until it hits 0 again.
    • The x, y screen coordinates used to display the shell.
    • dx, dy deltas to apply to the x, y screen coordinates for each step the shell traverses. 

    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...
    Read more »

  • Tanks for the Code

    Michael Gardi06/20/2026 at 16:39 0 comments

    After a few weeks of procrastination (enjoying the warmer weather up here in the great white north) I finally got back at this project. My initial goal was to get the player's tank showing up on the screen and moving around. Easier said than done.

    With my Lunar Lander for the PDP-1 project I was able to create the LEM as a series of five discrete bitmaps, one for each of it's possible orientations [degrees] (left [0], left-up [45], up [90], right-up [135], and right [180]). Each bitmap fit nicely into an 18 x 18 bit grid which worked perfectly with the PDP-1's 18-bit word architecture.

    The tanks in RIP OFF are a completely different animal. If you look at the video in the project description, you see that the tanks can seemingly rotate and move in any arbitrary direction (angle).  Yikes!

    I will be the first to admit that left to my own devices, I would have been hard pressed to come up with code to implement this behavior (especially on a PDP-1). Fortunately for me this problem was solved about 55 years ago when the original Spacewar! game came out since both of the space ships can do this.

    I could have gone back to the original Spacewar! source to figure this out, but even there I had a leg up. Norbert Landsteiner used this approach when he wrote his Retrochallenge 2016/10: Ironic Computer Space Simulator (ICSS)  implementation of Computer Space. In Episode 3 of the writeup (both text description and source code) of his ICSS effort, he describes the technique in great detail, to the point where even I could mostly understand it.

    Please refer to Norbert's Episode 3 description for the gory details (and code) as I did, but the basic flow is:

    1. Apply any rotational change based on player inputs.
    2. Calculate a "unit vector" for the new rotation using using sine and cosine routines.
    3. Calculate scaled versions of the unit vector in 1, 2, and 4 steps (simple shifts).
    4. Find the top most point of the "sprite" outline and apply the delta x and delta y representing the tank's current position.
    5. Starting from that top most point of the sprite outline, apply the differentials as scaled vectors  between each point and plot the result. 

    In the above flow, calculations for steps 1-4 are performed for each frame of the game.  Step 5 is efficiently performed by "static" code that was generated based on the differentials between points.

    To generate the code for step 5 I wrote a small Python program that takes the coordinates of the sprite outline points as input.

    Note that only one side is of the sprite is defined because after that side is "drawn" the transformation "matrix" can be adjusted and a second pass can be run to generate the other side.  

    Here is the code.

    # Define the points that form the left side of the sprite.
    # Tank.
    left_side_points=((0,0),(1,1),(3,1),(5,1),(6,6),(6,8),(6,4),
                      (7,1),(8,3),(9,8),(10,4),(13,8),(13,4),
                      (17,4),(17,8),(20,4),(21,8),(22,3),(23,1),
                      (24,4),(24,6),(24,8))
    
    # Emit add or sub commands based on the delta value passed. 
    # Maps vectors to combinations of the 1, 2, and 4 vectors.
    def show_command(command:str, delta:int):
        if delta == 0:
            return
        elif delta in (1,2,4):
            print("\t",end='')
            print(command + str(delta))
        elif delta == 3:
            print("\t",end='')
            print(command+"2")
            print("\t",end='')
            print(command+"1")
        elif delta == 5:
            print("\t",end='')
            print(command+"4")
            print("\t",end='')
            print(command+"1")
        elif delta == 6:
            print("\t",end='')
            print(command+"4")
            print("\t",end='')
            print(command+"2")
        elif delta == 8:
            print("\t",end='')
            print(command+"4")
            print("\t",end='')
            print(command+"4")
        elif delta == 10:
            print("\t",end='')
            print(command+"4")
            print("\t",end='')
            print(command+"4")
            print("\t",end='')
            print(command+"2"...
    Read more »

View all 3 project logs

Enjoy this project?

Share

Discussions

Gord Payne wrote 05/14/2026 at 15:37 point

Hi Michael:

The VoytiBoyd is sitting on my work table all ready for a grand-nephew to enjoy when he's a bit older :-). Thanks for the compliments.

Build On!

  Are you sure? yes | no

Gord Payne wrote 05/13/2026 at 13:24 point

Class of '89 BES. Retiring from career in IT (14 years) followed by 22 years as High School Math/CompSci teacher. Lots more time for Arduino/ESP32 coming !

  Are you sure? yes | no

Michael Gardi wrote 05/13/2026 at 16:24 point

Congrats to you. I retired in 2019 and haven't looked back. I love the look of your VoytiBoyd build. I thought of doing something like this a few years back. I have a grandson now and think that he might inspire me to make one.

  Are you sure? yes | no

Gord Payne wrote 05/12/2026 at 17:45 point

Congrats on your project from another Early 1980s Waterloo grad :-)

  Are you sure? yes | no

Michael Gardi wrote 05/12/2026 at 19:47 point

Thanks Gord! Ya class of '82 BMath. I just started this project so congrats might be a bit premature, I see a few speed bumps ahead.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates