rev |
line source |
pascal@180
|
1 ;
|
pascal@180
|
2 ; F-bird text game in a bootsector
|
pascal@180
|
3 ;
|
pascal@180
|
4 ; by Oscar Toledo G.
|
pascal@180
|
5 ; http://nanochess.org/
|
pascal@180
|
6 ;
|
pascal@180
|
7 ; Creation date: Jun/04/2017. A messy unoptimized thing.
|
pascal@180
|
8 ; Revision date: Jun/05/2017. Better usage of graphic charset.
|
pascal@180
|
9 ; Removed a non-8088 long jump. Added
|
pascal@180
|
10 ; sound. Solved bug when overwriting
|
pascal@180
|
11 ; previous score.
|
pascal@180
|
12 ;
|
pascal@180
|
13
|
pascal@186
|
14 %define MDA_SUPPORT
|
pascal@186
|
15
|
pascal@180
|
16 use16
|
pascal@180
|
17 cpu 8086
|
pascal@180
|
18
|
pascal@186
|
19 %ifdef FAT_BOOT
|
pascal@186
|
20 jmp bootbr
|
pascal@186
|
21 nop
|
pascal@186
|
22 times 0x3B db 0
|
pascal@186
|
23 bootbr:
|
pascal@186
|
24 %endif
|
pascal@186
|
25 cld ; Reset direction flag (so stosw increments registers)
|
pascal@186
|
26 sti ; Allow interrupts
|
pascal@186
|
27 push cs
|
pascal@186
|
28 pop ss
|
pascal@186
|
29 mov sp,0xb800 ; Point to video segment
|
pascal@186
|
30 SetSegments:
|
pascal@186
|
31 mov ds,sp ; Both the source (common access)
|
pascal@186
|
32 mov es,sp ; and target segments
|
pascal@180
|
33 ;
|
pascal@180
|
34 ; Game restart
|
pascal@180
|
35 ;
|
pascal@186
|
36 fb21:
|
pascal@186
|
37 mov ax,0x0002 ; Set 80x25 text mode
|
pascal@186
|
38 int 0x10 ; Call BIOS
|
pascal@186
|
39 mov di,next ; Init variables in video segment (saves big bytes)
|
pascal@186
|
40 %ifdef MDA_SUPPORT
|
pascal@186
|
41 mov sp,0xb000
|
pascal@186
|
42 inc byte [di]
|
pascal@186
|
43 je SetSegments
|
pascal@186
|
44 %endif
|
pascal@186
|
45 mov ax,0x60a0
|
pascal@186
|
46 stosw ; next, bird
|
pascal@186
|
47 xor ax,ax
|
pascal@186
|
48 stosw ; grav, pipe
|
pascal@186
|
49 stosb
|
pascal@186
|
50
|
pascal@186
|
51 ; Disable text mode cursor
|
pascal@186
|
52 ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor
|
pascal@186
|
53 mov ah, 0x01
|
pascal@186
|
54 mov ch, 0x3f
|
pascal@186
|
55 int 0x10
|
pascal@180
|
56
|
pascal@180
|
57 mov di,0x004a ; Game title
|
pascal@186
|
58 call print_string
|
pascal@186
|
59 db 'F-BIRD'
|
pascal@182
|
60 mov bp,80 ; Introduce 80 columns of scenery
|
pascal@182
|
61 fb1: call scroll_scenery
|
pascal@186
|
62 %define SI(n) [si+n-0x0fa2]
|
pascal@182
|
63 dec bp
|
pascal@182
|
64 jnz fb1
|
pascal@180
|
65
|
pascal@180
|
66 fb23: mov ah,0x01 ; Check if key pressed
|
pascal@180
|
67 int 0x16
|
pascal@180
|
68 pushf
|
pascal@180
|
69 xor ax,ax ; Wait for a key
|
pascal@180
|
70 int 0x16
|
pascal@180
|
71 popf
|
pascal@180
|
72 jnz fb23 ; Jump if key was accumulated, if not already waited for key ;)
|
pascal@180
|
73
|
pascal@180
|
74 ;
|
pascal@180
|
75 ; Main loop
|
pascal@180
|
76 ;
|
pascal@186
|
77 fb12: mov ax,[bird] ; Bird falls...
|
pascal@186
|
78 add al,ah ; ...because of gravity...
|
pascal@180
|
79 mov [bird],al ; ...into new position.
|
pascal@180
|
80 and al,0xf8 ; Row is a 5.3 fraction, nullify fraction
|
pascal@180
|
81 mov ah,0x14 ; Given integer is x8, multiply by 20 to get 160 per line
|
pascal@180
|
82 mul ah ; Row into screen
|
pascal@180
|
83 add ax,$0020 ; Fixed column
|
pascal@180
|
84 xchg ax,di ; Pass to DI (AX cannot be used as pointer)
|
pascal@186
|
85 mov dx,$0d1f ; Draw body
|
pascal@186
|
86 mov ax,bp ; [frame]
|
pascal@186
|
87 mov bx,-160
|
pascal@180
|
88 and al,4 ; Wing movement each 4 frames
|
pascal@180
|
89 jz fb15
|
pascal@186
|
90 mov ax,$0d1e ; Draw upper wing
|
pascal@186
|
91 xchg ax,[bx+di] ; Get character below
|
pascal@180
|
92 jmp short fb16
|
pascal@180
|
93 ;
|
pascal@180
|
94 ; Stars and game over
|
pascal@180
|
95 ;
|
pascal@186
|
96 fb19: mov al,$2a ; '*' Asterisks to indicate crashing
|
pascal@186
|
97 stosb
|
pascal@186
|
98 inc di
|
pascal@186
|
99 stosb
|
pascal@180
|
100 mov di,0x07CA
|
pascal@186
|
101 call print_string
|
pascal@186
|
102 db 'BONK!'
|
pascal@186
|
103 mov bp,-100 ; Wait 100 frames
|
pascal@182
|
104 fb20: call wait_frame
|
pascal@182
|
105 jnz fb20
|
pascal@180
|
106 jmp fb21 ; Restart
|
pascal@180
|
107
|
pascal@186
|
108 fb4: mov al,[bird]
|
pascal@186
|
109 cmp al,0x18 ; Make sure the bird doesn't fly free outside screen
|
pascal@180
|
110 jb fb18
|
pascal@186
|
111 sub al,0x10 ; Move bird two rows upward
|
pascal@186
|
112 fb18: cbw ; Reset gravity
|
pascal@180
|
113 mov [bird],ax
|
pascal@180
|
114 mov al,0xb6 ; Flap sound
|
pascal@180
|
115 out (0x43),al
|
pascal@180
|
116 mov al,0x90
|
pascal@180
|
117 out (0x42),al
|
pascal@180
|
118 mov al,0x4a
|
pascal@180
|
119 out (0x42),al
|
pascal@180
|
120 in al,(0x61)
|
pascal@180
|
121 or al,0x03 ; Turn on sound
|
pascal@180
|
122 out (0x61),al
|
pascal@180
|
123 fb26: jmp fb12
|
pascal@180
|
124
|
pascal@186
|
125 fb16: mov dl,$14 ; Draw body
|
pascal@186
|
126 fb15: or al,[di] ; Add another character below
|
pascal@186
|
127 mov [di],dx ; Draw body
|
pascal@186
|
128 mov dl,$10 ; Draw head
|
pascal@186
|
129 xchg dx,[di+2] ; Get character below head
|
pascal@186
|
130 add al,dl
|
pascal@186
|
131 cmp al,0x40 ; Collision with scenery?
|
pascal@186
|
132 jnz fb19
|
pascal@186
|
133 call wait_frame ; Wait for frame
|
pascal@186
|
134 mov ax,bp ; [frame]
|
pascal@186
|
135 and al,7 ; 8 frames have passed?
|
pascal@186
|
136 jnz fb17 ; No, jump
|
pascal@186
|
137 inc byte SI(grav) ; Increase gravity
|
pascal@186
|
138 fb17:
|
pascal@186
|
139 mov al,$20
|
pascal@186
|
140 mov [bx+di],al ; Delete bird from screen
|
pascal@186
|
141 stosb
|
pascal@186
|
142 inc di
|
pascal@186
|
143 stosb
|
pascal@186
|
144 call scroll_scenery ; Scroll scenery
|
pascal@186
|
145 call scroll_scenery ; Scroll scenery
|
pascal@186
|
146 mov di,bx
|
pascal@186
|
147 mov al,0xb0
|
pascal@186
|
148 scasb ; Passed a column?
|
pascal@186
|
149 jz fb27
|
pascal@186
|
150 inc di
|
pascal@186
|
151 scasb ; Passed a column?
|
pascal@186
|
152 jnz fb24
|
pascal@186
|
153 fb27: mov di,0x008e ; Show current score
|
pascal@186
|
154 fb25: dec di
|
pascal@186
|
155 dec di
|
pascal@186
|
156 mov ax,0x0c30 ; Convert remaining 0-9 to ASCII, also put color
|
pascal@186
|
157 xchg [di], ax
|
pascal@186
|
158 or al, 0x30
|
pascal@186
|
159 cmp al, '9'
|
pascal@186
|
160 je fb25
|
pascal@186
|
161 inc ax
|
pascal@186
|
162 stosb
|
pascal@186
|
163 fb24: mov ah,0x01 ; Any key pressed?
|
pascal@186
|
164 int 0x16
|
pascal@186
|
165 jz fb26 ; No, go to main loop
|
pascal@186
|
166 mov ah,0x00
|
pascal@186
|
167 int 0x16 ; Get key
|
pascal@186
|
168 dec ah ; Escape key?
|
pascal@186
|
169 jne fb4 ; No, jump
|
pascal@186
|
170 quit: mov al,3
|
pascal@186
|
171 int 0x10
|
pascal@186
|
172 int 0x20 ; Exit to DOS or to oblivion (boot sector)
|
pascal@186
|
173 int 0x19
|
pascal@186
|
174
|
pascal@180
|
175 ;
|
pascal@180
|
176 ; Scroll scenery one column at a time
|
pascal@180
|
177 ;
|
pascal@180
|
178 scroll_scenery:
|
pascal@180
|
179 ;
|
pascal@180
|
180 ; Move whole screen
|
pascal@180
|
181 ;
|
pascal@180
|
182 mov si,0x00a2 ; Point to row 1, column 1 in SI
|
pascal@180
|
183 mov di,0x00a0 ; Point to row 1, column 0 in DI
|
pascal@186
|
184 mov bx,di
|
pascal@180
|
185 fb2: mov cx,79 ; 79 columns
|
pascal@180
|
186 repz ; Scroll!!!
|
pascal@180
|
187 movsw
|
pascal@180
|
188 mov ax,0x0e20 ; Clean last character
|
pascal@180
|
189 stosw
|
pascal@180
|
190 lodsw ; Advance source to keep pair source/target
|
pascal@180
|
191 cmp si,0x0fa2 ; All scrolled?
|
pascal@180
|
192 jnz fb2 ; No, jump
|
pascal@180
|
193 ;
|
pascal@180
|
194 ; Insert houses
|
pascal@180
|
195 ;
|
pascal@186
|
196 mov word SI(0x0f9e),0x02df ; Terrain
|
pascal@180
|
197 in al,(0x40) ; Get "random" number
|
pascal@186
|
198 test al,0x70
|
pascal@180
|
199 jz fb5
|
pascal@186
|
200 mov dx,0x0408 ; House of one floor
|
pascal@180
|
201 mov di,0x0e5e
|
pascal@186
|
202 mov [bx+di],dx
|
pascal@186
|
203 test al,0x20 ; Check "random" number
|
pascal@180
|
204 jz fb3
|
pascal@186
|
205 mov [di],dx ; House of two floors
|
pascal@186
|
206 sub di,bx
|
pascal@180
|
207 fb3: mov word [di],0x091e ; Add roof
|
pascal@180
|
208 ;
|
pascal@180
|
209 ; Check if it's time to insert a column
|
pascal@180
|
210 ;
|
pascal@186
|
211 fb5: dec byte SI(next) ; Decrease time (column really) for next pipe
|
pascal@186
|
212 mov dh,SI(next)
|
pascal@186
|
213 mov dl,0xb1 ; Leftmost
|
pascal@186
|
214 mov cl,3
|
pascal@186
|
215 cmp dh,cl ; bl = 3,2,1,0 for the four columns making the pipe
|
pascal@186
|
216 ja fb6 ; No yet, jump
|
pascal@186
|
217 jne fb8 ; No leftmost, jump
|
pascal@186
|
218 and al,0x07 ; Between 0 and 7
|
pascal@180
|
219 add al,0x04 ; Between 4 and 11
|
pascal@186
|
220 mov SI(tall),al ; This will tell how tall the pipe is
|
pascal@186
|
221 jmp fb7 ; Leftmost, jump
|
pascal@186
|
222
|
pascal@186
|
223 fb8: mov dl,0xdb
|
pascal@186
|
224 or dh,dh ; Rightmost?
|
pascal@186
|
225 jnz fb7 ; No, jump
|
pascal@180
|
226 mov dl,0xb0
|
pascal@186
|
227 dec word SI(pipe) ; Increase total pipes shown
|
pascal@186
|
228 or byte SI(pipe+1),0xfe
|
pascal@186
|
229 mov ax,SI(pipe)
|
pascal@186
|
230 sar ax,cl
|
pascal@186
|
231 add al,0x50 ; Decrease distance between pipes
|
pascal@186
|
232 mov SI(next),al ; Time for next pipe
|
pascal@180
|
233 fb7: mov di,0x013e ; Start from top of screen
|
pascal@186
|
234 xchg ax,dx
|
pascal@180
|
235 mov ah,0x0a
|
pascal@186
|
236 push ax
|
pascal@180
|
237 mov dx,0x009e
|
pascal@186
|
238 mov cl,SI(tall)
|
pascal@180
|
239 fb9: stosw
|
pascal@180
|
240 add di,dx
|
pascal@180
|
241 loop fb9
|
pascal@180
|
242 mov al,0xc4
|
pascal@180
|
243 stosw
|
pascal@180
|
244 add di,0x009e*6+10
|
pascal@180
|
245 mov al,0xdf
|
pascal@180
|
246 stosw
|
pascal@186
|
247 pop ax
|
pascal@186
|
248 fb10: add di,dx
|
pascal@180
|
249 stosw
|
pascal@186
|
250 cmp di,0x0ea0
|
pascal@180
|
251 jb fb10
|
pascal@180
|
252 fb6: ret
|
pascal@180
|
253
|
pascal@180
|
254 ;
|
pascal@180
|
255 ; Wait for a frame
|
pascal@180
|
256 ;
|
pascal@180
|
257 wait_frame:
|
pascal@182
|
258 fb14: hlt ; Wait for clock interrupt
|
pascal@182
|
259 mov ah,0x00 ; Use base clock tick
|
pascal@180
|
260 int 0x1a
|
pascal@186
|
261 cmp dx,[tick]
|
pascal@180
|
262 jz fb14
|
pascal@186
|
263 mov [tick],dx
|
pascal@180
|
264 in al,(0x61)
|
pascal@186
|
265 and al,0xfc ; Turn off sound
|
pascal@180
|
266 out (0x61),al
|
pascal@186
|
267 inc bp ; Increase frame count
|
pascal@180
|
268 ret
|
pascal@180
|
269
|
pascal@186
|
270 ;
|
pascal@186
|
271 ; Print a string
|
pascal@186
|
272 ;
|
pascal@186
|
273 print_string:
|
pascal@186
|
274 pop si
|
pascal@186
|
275 mov ah, 0xf ; white
|
pascal@186
|
276 db 0x3C ; cmp al,0xab
|
pascal@186
|
277 pr1: stosw
|
pascal@186
|
278 cs lodsb
|
pascal@186
|
279 cmp al,0xbd
|
pascal@186
|
280 jne pr1
|
pascal@186
|
281 dec si
|
pascal@186
|
282 push si
|
pascal@186
|
283 ret
|
pascal@180
|
284
|
pascal@186
|
285 times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes
|
pascal@180
|
286
|
pascal@180
|
287 db 0x55,0xaa ; Bootable signature
|
pascal@180
|
288
|
pascal@186
|
289 tick: equ 0x0fa0 ; word
|
pascal@186
|
290
|
pascal@186
|
291 next: equ tick+2 ; byte
|
pascal@186
|
292 bird: equ tick+3 ; byte
|
pascal@186
|
293 grav: equ bird+1 ; byte
|
pascal@186
|
294 pipe: equ tick+5 ; word
|
pascal@186
|
295
|
pascal@186
|
296 tall: equ tick+7 ; byte
|