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?
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.
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 :