600 lines really is a luxury!

I’ve seen a couple of posts , the first by Jeff Atwood about what can you build in  600 lines of code and a response from Charles Petzold commenting that 600 lines is a luxury.

Heh. 600 Lines. That really was a luxury. Back when I first started out in the games industry, for fun and a bit of friendly competition we used to write games that must be close to 256 BYTES as possible.

So, the other night I took a quick trip down memory lane and dug out my old source code, and by old I mean old. When I unzipped the code, I swear the monitor turned sepia. This stuff is from the days when 4Mhz PC’s cost more than most cars,CD’s were still a twinkle in Philips’s eye, and Norton Tools was indispensable. Ok, so it was only 1989, but it still feels old!

Anyway, sifting through the files I could only find two examples of my 256 Byte games (and one from a friend) but I’ll present them below for your amusement.

They’re written in 8086 assembly, yep, those are real 16 bit registers 😉 And the tool of choice was TASM, back when Borland produced real tools, rather than the bloated Java crap they’re peddling today.

 

First up is Pacman. Running these relics in a window means the code to sync to the vertical refresh doesn’t work too well! I had to fire up a VM because they wont run at all on my Vista64 install. Anyway, the ghosts move randomly rather than tracking you, but there is collision detection against the walls. Come on, what do you expect for 256 bytes?

pac.com

And here is the code, a whopping 207 lines , but that does include blanks, comments and labels!

Pac.asm

   1:         IDEAL
   2:         WARN
   3:         MODEL    TINY
   4:         CODESEG
   5:         org    0100h
   6: Maze                 = 0
   7: Ghost1Offset         = 20
   8: Ghost2Offset         = 22
   9: Ghost3Offset         = 24
  10: Ghost4Offset         = 26
  11: PlayerOffset         = 28
  12: Ghost1Dir            = 30
  13: Ghost2Dir            = 32
  14: Ghost3Dir            = 34
  15: Ghost4Dir            = 36
  16: PlayerDir            = 38
  17: TimerPtr             = 40
  18:  
  19: Start:    
  20:         mov        al,3
  21:         int        10h
  22:  
  23:         mov        di,OFFSET    Screen
  24:         mov        cx,80*25
  25:         xor        ax,ax
  26:         push    di
  27:         rep        stosw
  28:         pop        di
  29:  
  30:         mov        si,OFFSET    Screen+(19*160)
  31:  
  32:         mov        bp,OFFSET VARS
  33:  
  34:         mov        dx,10
  35: @@Lines:
  36:         mov        bx,2
  37: @@Again:
  38:         mov        cx,16
  39: @@Draw:
  40: @@sm1:        rol        [WORD bp],1
  41:         mov        ax,05dbh
  42:         jc        SHORT @@Store
  43:         mov        ax,0307h
  44: @@Store:
  45:         mov        [si],ax                ;bottom left
  46:         mov        [si+2],ax            ;bottom left
  47:         movsw                        ;Top Left
  48:         movsw                        ;Top Left
  49:         loop    @@Draw
  50:         xor        [BYTE cs:@@sm1+1],8            ;from rol to ror
  51:         dec        bx
  52:         jnz        @@again
  53:         sub        si,160+128
  54:         add        di,160-128
  55:         inc        bp
  56:         inc        bp
  57:         dec        dx
  58:         jnz        @@Lines
  59:                     
  60:         mov        ax,0b800h
  61:         mov        es,ax
  62:  
  63: MainLoop:
  64:  
  65: tofwait:
  66:           mov        dx,03dah
  67: @@b3:        
  68:         in         al,dx
  69:         test       al,8      ;already in flyback ????
  70:         jz         SHORT @@b3
  71:  
  72:         push    cx
  73:         mov        si,OFFSET screen        
  74:         xor        di,di
  75:         mov        cx,80*25*2
  76:         rep        movsw
  77:  
  78:  
  79:         ;move ghosts
  80:  
  81:         mov        si,OFFSET Gh1o
  82:         mov        di,OFFSET G1Dir
  83:         mov        cl,4                                ;4 ghosts
  84:         inc        [BYTE TimeToMove]
  85: @@Lp:
  86:         mov        bl,[di]                    ;get cur dir
  87:         and        bx,3
  88: ;        add        bx,OFFSET DirTab
  89:  
  90:         mov        ax,[WORD DirTab+bx]                    ;current Direction addval
  91:         cbw
  92:         shl        ax,1
  93:  
  94:         mov        bx,[si]
  95:  
  96:         and        [BYTE TimeToMove],7
  97:         jnz        @@JustPrint
  98:  
  99:         add        bx,ax
 100:  
 101:         test        [BYTE Screen+bx],0ffh
 102:         jns        @@Move
 103:  
 104:         ror        [BYTE Gw1MP],1                ;WORD bp],1    
 105:  
 106: @@DirChange:
 107:         inc        [BYTE di]
 108:         jc        @@Right
 109:  
 110:         ;try left
 111:  
 112:         dec        [BYTE di]
 113:         dec        [BYTE di]
 114: @@Right:
 115:         jmp        SHORT    @@Lp
 116:  
 117: @@Move:
 118:         ror        [WORD RanCh],1                ;BYTE bp+2],1    
 119:         jc        @@DirChange
 120:         mov        [si],bx                    ;move in that dir.
 121: @@JustPrint:
 122:         mov        [WORD es:bx],02f0fh    
 123:         inc        si
 124:         inc        si
 125:         inc        di
 126:         loop    @@Lp
 127:  
 128:  
 129:         mov        ah,1
 130:         int        16h
 131:         mov        di,[WORD ManPos]
 132:         jz        @@NoManMove
 133:         xor        ah,ah
 134:         int        16h
 135:         cmp        al,'Q'
 136:         je        Quit
 137:  
 138:         MOV        BX,-2
 139:         cmp        al,'Z'
 140:         je        @@CheckMoveOk
 141:  
 142:         neg        bx
 143:         cmp        al,'X'
 144:         je        @@CheckMoveOk
 145:  
 146:         mov        bx,-160
 147:         cmp        al,'P'
 148:         je        @@CheckMoveOk
 149:  
 150:         neg        bx
 151:         cmp        al,'L'
 152:         jne        @@NoManMove
 153:  
 154: @@CheckMoveOk:
 155:         test        [BYTE es:di+bx],0ffh
 156:         js        @@NoManMove
 157:         add        di,bx
 158: @@NoManMove:
 159:         mov        al,1
 160:         xchg        al,[es:di]
 161:         mov        [WORD ManPos],di
 162:         mov        [BYTE Screen+di],0
 163: @@NoMove:
 164:         jmp        MainLoop
 165: Quit:
 166:         int        20h
 167:  
 168: VARS:
 169:  
 170: Mz:        dw    1111111111111111b    
 171:         dw    1000010000100000b    
 172:         dw    1011000110101111b    
 173:         dw    1011100110000000b    
 174:         dw    1000010000101000b    
 175:         dw    1001010110101011b    
 176:         dw    1010010110001011b    
 177:         dw    1010000000100000b    
 178: DirTab:        db    1,-80        ;1000111101101111b    
 179:         db    -1,80        ;1100010000100011b    
 180:  
 181:  
 182: ;ghosts pos,inc
 183: Gh1o:    dw    1*160+2*2
 184: Gh2o:    dw    7*160+56*2
 185: Gh3o:    dw    18*160+44*2
 186: Gh4o:    dw    15*160+15*2
 187: ManPos:dw    160+4                        ;payer O
 188:  
 189: Gw1Mp:    db    11001010b
 190: RanCh:    dw  10000000b
 191: G1Dir:    db    1
 192: G2Dir:    db    2
 193: G3Dir:    db    3
 194: G4Dir:    db    0
 195:  
 196:  
 197: ;Timer_Ptr    dw    6ch,40h
 198:  
 199: ;DirTab:    db    1
 200: ;            db    -80
 201: ;            db    -1
 202: ;            db    80
 203:  
 204: Screen:        dw    80*25*2 dup(?)            ;80*25
 205: TimeToMove:    db    ?
 206:  
 207:         END Start

Next is Invaders, much prettier than Pacman I think, and was quite a challenge too.

invaders

Here is the code for Space invaders, weighing in at a hefty 151 lines and exactly 256 bytes.

Invaders.asm  

   1:         IDEAL
   2:         WARN
   3:         MODEL    TINY
   4:         CODESEG
   5:         org    0100h
   6: INVOff        =    0
   7: ManOff        =   4
   8: BullOff        =    6
   9: DIRECTION    =    8
  10: Scroll        =    9                        ;InvCount    =    100                        ;in middle of bss
  11: InvaderChar        equ    0ech
  12: InvaderBullet    equ    18h
  13: Start:    
  14:         mov        al,3
  15:         int        10h
  16:         mov        bp,OFFSET VARS
  17:         mov        ax,0700h OR InvaderChar
  18:         les        di,[bp+INVOFF]                ;DWORD InvPos]
  19:         push    es
  20:         pop        ds
  21:         add        di,20
  22:         mov        bl,5
  23: @@Lines:
  24:         mov        cx,20
  25: @@Cols:
  26:         stosw
  27:         add        di,4
  28:         loop    @@Cols
  29:         dec        ax
  30:         dec        ah
  31:         add        di,40+160
  32:         dec        bx
  33:         jnz        @@Lines
  34: ;Draw 4 bases
  35:         mov        bl,4
  36:         mov        di,160*19+20
  37:         mov        al,0dbh
  38: @@Loop:
  39:         push    di
  40:         mov        cl,4
  41:         rep        stosw
  42:         add        di,160-5*2
  43:         mov        cl,6
  44:         rep        stosw
  45:         pop        di
  46:         add        di,36
  47:         dec        bx
  48:         jnz        SHORT @@loop
  49: MainLoop:
  50:         mov        di,[WORD bp+ManOff]                ;WORD ManPos]
  51:         mov        [BYTE di],20h                        ;erase man
  52:         ;Erase Bullet
  53:         mov        di,[WORD bp+BullOff]            ;erase Bullet
  54:         mov        [BYTE di],20h
  55:         call    MoveInvaders
  56:         ;check for ANY invaders on bottom line
  57:         mov        di,160*23
  58:         mov        cl,80
  59:         mov        ax,0720h                    ;xor        ax,ax
  60:         repe    scasw
  61:         jcxz    NotGameOver
  62: Done:
  63:         int        20h
  64: NotGameOver:
  65: ;Check Keys for man Move
  66:         mov        ah,2
  67:         int        16h
  68:         mov        di,[bp+manOff]
  69:         test    al,01h
  70:         jz        @@NotRight
  71:         inc        di
  72:         inc        di
  73: @@NotRight:
  74:         test    al,02h
  75:         jz        SHORT @@NotLeft
  76:         dec        di
  77:         dec        di
  78: @@NotLeft:
  79:         test    al,04h
  80:         jz        SHORT @@NotFire
  81:         mov        [WORD bp+BullOff],di
  82: @@NotFire:
  83:         mov        [WORD bp+ManOff],di
  84:         test    al,08h
  85:         jne        SHORT Done
  86: ;Draw Man
  87:         mov        [BYTE di],1eh                ;draw man char
  88: ;Draw Bullet
  89:         sub        [WORD bp+BullOff],160
  90:         js        SHORT @@NoDrawBullet        
  91:         mov        di,[WORD bp+BullOff]
  92:         mov        al,18h
  93:         xchg    [BYTE di],al
  94:         cmp        al,20h
  95:         je        SHORT @@NoDrawBullet
  96:         mov        [BYTE di],20h
  97:         mov        [BYTE bp+BullOff+1],0ffh
  98: @@NoDrawBullet:
  99: ;Wait TOF
 100:           mov        dx,03dah
 101: @@b3:        
 102:         in         al,dx
 103:         test       al,8      ;already in flyback ????
 104:         jz         SHORT @@b3
 105:         jmp        SHORT    MainLoop
 106:         ;edge scan for invaders to turn round
 107: MoveInvaders:
 108:         ror        [WORD bp+scroll],1
 109:         jnc        Retq              
 110:         mov        di,158
 111:         mov        cl,25
 112: @@Scan:
 113:         mov        al,[BYTE di]            ;RHS        ;mov        cmp        [BYTE di],128
 114:         or        al,[BYTE di+4]            ;LHS
 115:         js        SHORT @@Found
 116:         add        di,160
 117:         loop    @@Scan
 118:         jmp        SHORT    @@moveit
 119: @@found:
 120:         not        [BYTE bp+Direction]
 121:         mov        si,8*160+160
 122:         mov        di,8*160+160+160
 123:         mov        cl,80                            ;1 extra line
 124:         call    Movem1                        ;move down 1 line
 125:         add        [WORD bp+InvOff],160        
 126:         ;cx will aready be zero beacuse only STOSW's are ever done on it
 127: @@MoveIt:
 128:         mov        si,2
 129:         xor        di,di
 130:         test    [BYTE bp+Direction],0ffh
 131:         je        SHORT MoveInvRight
 132:         mov        si,8*160+160-2-2
 133:         mov        di,8*160+160-2
 134: Movem1:
 135:         std
 136: MoveInvRight:
 137: Movem:
 138:         add        si,[WORD bp+Invoff]
 139:         add        di,[WORD bp+Invoff]
 140:         add        cx,80*9
 141:         rep        movsw
 142:         cld
 143: Retq:    ret
 144: VARS:
 145: InvPos:    dw    160*2,0b800h            ;20 = start xpos of invaders
 146: ManPos:    dw    160*23+4
 147: Bullpos:    dw    0fffeh
 148: dir:        db    0
 149: scrl:        dw    08000h
 150: BSS:
 151:         END Start

If you have an old DOS machine, the executables and the source code are here :

This entry was posted in General. Bookmark the permalink.

Leave a Reply