wok-tiny rev 186
Add bootlife
author | Pascal Bellard <pascal.bellard@slitaz.org> |
---|---|
date | Sun Feb 04 18:02:38 2024 +0000 (2 months ago) |
parents | 61c76233911e |
children | d1451fc0bc92 |
files | boot-man/stuff/boot-man.asm bootbricks/stuff/bricks.asm bootfbird/stuff/fbird.asm bootlife/receipt bootlife/stuff/life.asm bootmine/stuff/mine.asm bootris/stuff/tetranglix.asm bootsokoban/stuff/sokoban.asm x86test/receipt x86test/stuff/patch.S |
line diff
1.1 --- a/boot-man/stuff/boot-man.asm Wed Oct 04 13:13:17 2023 +0000 1.2 +++ b/boot-man/stuff/boot-man.asm Sun Feb 04 18:02:38 2024 +0000 1.3 @@ -53,36 +53,79 @@ 1.4 ; Boot-Man runs off BIOS clock. It should therefore run at the same speed on all PCs. The code is quite heavily 1.5 ; optimized for size, so code quality is questionable at best, and downright atrocious at worst. 1.6 1.7 -;%define WaitForAnyKey 1.8 - 1.9 -org 0x0600 ; The code will move to a well known position to allow some patches. 1.10 +;%define WaitForAnyKey ; +9 bytes 1.11 +%define MDAsupport ; +7 bytes 1.12 +%define MDAinverse ; +5 bytes 1.13 +%define MDA_CGA40 ; +17 bytes 1.14 1.15 cpu 8086 ; Boot-Man runs on the original IBM PC with a CGA card. 1.16 bits 16 ; Boot-Man runs in Real Mode. I am assuming that the BIOS leaves the CPU is Real Mode. 1.17 ; This is true for the vast majority of PC systems. If your system's BIOS 1.18 ; switches to Protected Mode or Long Mode during the boot process, Boot-Man 1.19 ; won't run on your machine. 1.20 +org 0x600 1.21 1.22 +%define COL40 40 1.23 +%define COL80 80 1.24 + 1.25 start: 1.26 call copy ; Can run as bootsector or DOS COM file 1.27 1.28 moved: 1.29 push cs 1.30 pop ss 1.31 - mov sp, cx ; Set up the stack. 1.32 + xor sp, sp ; Set up the stack. 1.33 1.34 - xchg ax, cx 1.35 - inc ax ; int 0x10 / ah = 0: Switch video mode. Switch mode to 40x25 characters (al = 1). 1.36 + mov ax, 1 ; int 0x10 / ah = 0: Switch video mode. Switch mode to 40x25 characters (al = 1). 1.37 int 0x10 ; In this mode, characters are approximately square, which means that horizontal 1.38 ; and vertical movement speeds are almost the same. 1.39 - 1.40 - mov cx, 0xb800 1.41 - mov es, cx ; Set up the es segment to point to video RAM 1.42 + mov ax, 0xb800 1.43 +%ifdef MDAsupport 1.44 + %define LeftCorner (COL80*2*24+COL80-32) 1.45 + %define PreviousLine (COL80*2+32) 1.46 + %define Columns COL80 1.47 + %define Margin ((COL80-32)/2) 1.48 + %ifdef MDA_CGA40 1.49 + mov dx, PreviousLine 1.50 + %undef PreviousLine 1.51 + %define PreviousLine dx 1.52 + %endif 1.53 +%else 1.54 + %define LeftCorner (COL40*2*24+COL40-32) 1.55 + %define PreviousLine (COL40*2+32) 1.56 + %define Columns COL40 1.57 + %define Margin ((COL40-32)/2) 1.58 +%endif 1.59 +initES: 1.60 + mov es, ax ; Set up the es segment to point to video RAM 1.61 + mov si, maze ; The first byte of the bit array containing the maze 1.62 +%define ghost_n_incr 0x2f10 1.63 +%ifdef MDAsupport 1.64 + mov ah, 0xb0 1.65 + %ifdef MDAinverse 1.66 + %define ghost_attr 0x70 1.67 + %define incr_attr 0x80 1.68 + %undef ghost_n_incr 1.69 + %define ghost_n_incr (ghost_attr*256 + incr_attr) 1.70 + xor word [si + ghost_attr_patch - maze], ghost_n_incr^0x2f10 1.71 + %endif 1.72 + %ifdef MDA_CGA40 1.73 +; xor dl, (COL80*2+32)^(COL40*2+32) 1.74 + xor dl, ah 1.75 + xor word [si + LeftCorner_patch - maze], LeftCorner^(COL40*2*24+COL40-32) 1.76 + xor byte [si + Columns_patch - maze], Columns^COL40 1.77 + xor byte [si + Margin_patch - maze], Margin^((COL40-32)/2) 1.78 + %endif 1.79 + scasw 1.80 + jb initES 1.81 +%endif 1.82 1.83 mov ax, 0x0101 ; int 0x10 / ah = 1: Determine shape of hardware cursor. al = 1 avoid AMI BIOS bug. 1.84 mov ch, 0x20 ; With cx = 0x2000, this removes the hardware cursor from the screen altogether 1.85 int 0x10 1.86 1.87 +%define ghost_color 0x2fec 1.88 +%define color_increment 0x10 1.89 1.90 1.91 ;----------------------------------------------------------------------------------------------------- 1.92 @@ -96,38 +139,35 @@ 1.93 ; while the right part is drawn right to left. For efficiency reasons, the entire maze 1.94 ; is built from the bottom up. Therefore, the maze is stored upside down in memory 1.95 ;----------------------------------------------------------------------------------------------------- 1.96 -buildmaze: 1.97 - mov di, 0x0788 ; Lower left corner of maze in video ram 1.98 - mov si, maze ; The first byte of the bit array containing the maze 1.99 - mov dx, 0x05fa ; Address in video ram of the lower left powerpill 1.100 +;buildmaze: 1.101 + mov di, LeftCorner ; Lower left corner of maze in video ram 1.102 +LeftCorner_patch equ $ - 2 1.103 + mov cx, 34 ; Lines count to the lower left powerpill 1.104 .maze_outerloop: 1.105 - mov cx, 0x003c ; The distance between a character in the maze and its 1.106 + mov bx, 0x003c ; The distance between a character in the maze and its 1.107 ; symmetric counterpart. Also functions as loop counter 1.108 lodsw ; Read 16 bits from the bit array, which represents one 1.109 - ; 32 character-wide row of the maze 1.110 + xchg ax, bp ; 32 character-wide row of the maze 1.111 .maze_innerloop: 1.112 - shl ax, 1 ; shift out a single bit to determine whether a wall or dot must be shown 1.113 - push ax 1.114 + add bp, bp ; shift out a single bit to determine whether a wall or dot must be shown 1.115 mov ax, 0x01db ; Assume it is a wall character (0x01: blue; 0xdb: full solid block) 1.116 jc .draw ; Draw the character if a 1 was shifted out 1.117 mov ax, 0x0ff9 ; otherwise, assume a food character (0x0f: white; x0f9: bullet) 1.118 - cmp di, dx ; See if instead of food we need to draw a power pill 1.119 - jnz .draw 1.120 - mov dh, 0x00 ; Update powerpill address to draw remaining powerpills 1.121 + loop .draw ; See if instead of food we need to draw a power pill 1.122 + mov cl, 125 ; Update powerpill address to draw remaining powerpills 1.123 mov al, 0x04 ; powerpill character (0x04: diamond - no need to set up colour again) 1.124 .draw: 1.125 stosw ; Store character + colour in video ram 1.126 - push di 1.127 - add di, cx ; Go to its symmetric counterpart 1.128 - stosw ; and store it as well 1.129 - pop di 1.130 - pop ax 1.131 - sub cx, 4 ; Update the distance between the two sides of the maze 1.132 + mov [es:bx+di], ax ; Go to its symmetric counterpart and store it as well 1.133 + sub bx, 4 ; Update the distance between the two sides of the maze 1.134 jns .maze_innerloop ; As long as the distance between the two halves is positive, we continue 1.135 1.136 - sub di, 0x70 ; Go to the previous line on the screen in video RAM. 1.137 + sub di, PreviousLine ; Go to the previous line on the screen in video RAM. 1.138 jns .maze_outerloop ; Keep going as long as this line is on screen. 1.139 1.140 + ;mov si, bootman_data ; Use si as a pointer to the game data. This reduces byte count of the code: 1.141 + ; mov reg, [address] is a 4 byte instruction, while mov reg, [si] only has 2. 1.142 + 1.143 ;----------------------------------------------------------------------------------------------------- 1.144 ; game_loop: The main loop of the game. Tied to BIOS clock (which fires 18x per 1.145 ; second by default), to ensure that the game runs at the same speed on all machines 1.146 @@ -141,29 +181,28 @@ 1.147 ; (in the original, collisions are checked only once, and as a consequence, it is 1.148 ; possible in some circumstances to actually move Pac-Man through a ghost). 1.149 ;----------------------------------------------------------------------------------------------------- 1.150 + 1.151 game_loop: 1.152 hlt ; Wait for clock interrupt 1.153 - mov ah,0x00 1.154 + mov ah, 0x00 1.155 int 0x1a ; BIOS clock read 1.156 - xchg ax,dx 1.157 + xchg ax, dx 1.158 old_time equ $ + 1 1.159 - cmp ax,0x1234 ; Wait for time change 1.160 + cmp al, 0x12 ; Wait for time change 1.161 je game_loop 1.162 - mov [old_time],ax ; Save new time 1.163 + mov [old_time], al ; Save new time 1.164 1.165 - mov si, bootman_data ; Use si as a pointer to the game data. This reduces byte count of the code: 1.166 - ; mov reg, [address] is a 4 byte instruction, while mov reg, [si] only has 2. 1.167 - 1.168 - mov ah,0x01 ; BIOS Key available 1.169 + mov bx, 3 1.170 + mov ah, 0x01 ; BIOS Key available 1.171 int 0x16 1.172 cbw ; BIOS Read Key 1.173 je .no_key 1.174 int 0x16 1.175 - mov al,ah 1.176 + mov al, ah 1.177 dec ah ; Escape ? 1.178 jne .convert_scancode 1.179 -.exit: 1.180 - mov al,3 1.181 +;.exit: 1.182 + xchg ax, bx ; int 0x10 / ax = 3: Switch video mode. Switch mode to 80x25 characters 1.183 int 0x10 ; Reset screen 1.184 int 0x20 ; Exit to DOS... 1.185 int 0x19 ; ...or reboot 1.186 @@ -171,22 +210,23 @@ 1.187 ; This code converts al from scancode to movement direction. 1.188 ; Input: 0x48 (up), 0x4b (right), 0x50 (down), 0x4d (left) 1.189 ; Output: 0xce (up), 0xca (right), 0xc6 (down), 0xc2 (left) 1.190 + ; fe xx: dec dh dec dl inc dh inc dl 1.191 1.192 .convert_scancode: 1.193 sub al, 0x47 ; 0x01 0x04 0x09 0x06 1.194 - and al,0xfe ; 0x00 0x04 0x08 0x06 1.195 + and al, 0xfe ; 0x00 0x04 0x08 0x06 1.196 cmp al, 9 1.197 jnc .no_key ; if al >= 0x50, ignore scancode; 1.198 ; this includes key release events 1.199 neg al ; 0x00 0xfc 0xf8 0xfa 1.200 - add al,0xce ; 0xce 0xca 0xc6 0xc8 1.201 - test al,2 1.202 + add al, 0xce ; 0xce 0xca 0xc6 0xc8 1.203 + test al, 2 1.204 jne .translated 1.205 - mov al,0xc2 1.206 + mov al, 0xc2 1.207 .translated: 1.208 cmp al, [si + 2] ; If the new direction is the same as the current direction, ignore it 1.209 jz .no_key 1.210 - mov [si + 3], al ; Set new direction to the direction derived from the keyboard input 1.211 + mov [bx+si], al ; Set new direction to the direction derived from the keyboard input 1.212 .no_key: 1.213 dec byte [si + pace_offset] ; Decrease the pace counter. The pace counter determines the overall 1.214 ; speed of the game. We found that moving at a speed of 6 per second 1.215 @@ -196,30 +236,28 @@ 1.216 ; and after Boot-Man dies, by intitalizing the counter with higher values. 1.217 jnz game_loop ; If the pace counter is not 0, we immediately finish. 1.218 1.219 - mov byte [si + pace_offset], 0x3; Reset the pace counter. 1.220 + mov byte [si + pace_offset], bl ; Reset the pace counter to 3. 1.221 ;----------------------------------------------------------------------------------------------------- 1.222 ; Move Boot-Man 1.223 ;----------------------------------------------------------------------------------------------------- 1.224 - mov al, [si + 3] ; al = new movement direction, as determined by keyboard input 1.225 call newpos_bootman ; Update dx to move 1 square in the direction indicated by al 1.226 ; newpos also checks for collisions with walls (in which case ZF is set) 1.227 jz .nodirchange ; ZF indicates that new position collides with wall. We therefore try to keep 1.228 ; moving in the current direction instead. 1.229 - mov [si + 2], al ; If there's no collision, update the current movement direction 1.230 + mov [bx+si], al ; If there's no collision, update the current movement direction 1.231 .nodirchange: 1.232 - mov al, [si + 2] ; al = current movement direction 1.233 call newpos_bootman ; Update dx to move 1 square in direction al 1.234 jz .endbootman ; If there's a wall there, do nothing 1.235 -.move: 1.236 +;.move: 1.237 mov ax, 0x0f20 ; Prepare to remove Boot-Man from screen, before drawing it in the new position 1.238 ; 0x0f = black background, white foreground; 0x20 = space character 1.239 - cmp byte [es:di], 0x04 ; Detect power pill 1.240 - jnz .nopowerpill 1.241 + cmp byte [es:di], ah ; Detect power pill (0x04) 1.242 + ja .nopowerpill 1.243 mov byte [si + timer_offset], al; If Boot-Man just ate a power pill, set up the ghost timer to 0x20. We use al here 1.244 ; as it accidentally contains 0x20, and this is one byte shorter than having an 1.245 ; explicit constant. 1.246 .nopowerpill: 1.247 - xchg dx, [si] ; Retrieve current position and store new position 1.248 + xchg dx, [si + bm_offset_pos] ; Retrieve current position and store new position 1.249 call paint ; Actually remove Boot-Man from the screen 1.250 .endbootman: 1.251 ;----------------------------------------------------------------------------------------------------- 1.252 @@ -259,13 +297,12 @@ 1.253 ; target 4 squares ahead of Pac-Man, in the direction Pac-Man is currently moving. The other ghosts' 1.254 ; movement is a bit more complex than that. I had to simplify the AI because of the limited code size. 1.255 ;----------------------------------------------------------------------------------------------------- 1.256 - mov bx, 3 * gh_length + bm_length ; Set up offset to ghost data. With this, si + bx is a 1.257 + mov bl, 3 * gh_length + bm_length ; Set up offset to ghost data. With this, si + bx is a 1.258 ; pointer to the data from the last ghost. Also used as 1.259 ; loop counter to loop through all the ghosts 1.260 - mov byte [si + collision_offset], bh ; Reset collision detection. BH happens to be 0 at this point 1.261 .ghost_ai_outer: 1.262 - mov bp, 0xffff ; bp = minimum distance; start out at maxint 1.263 - mov ah, [bx + si] ; ah will become the forbidden movement direction. We start 1.264 + mov bp, si ; bp = minimum distance; start out at a big int 1.265 + mov ah, [bx + si + gh_offset_dir] ; ah will become the forbidden movement direction. We start 1.266 ; with the current direction, which is forbidden if Boot-Man 1.267 ; just ate a power pill 1.268 cmp byte [si + timer_offset], 0x20 ; If timer_offset == 0x20, Boot-Man just picked up a power pill 1.269 @@ -275,24 +312,20 @@ 1.270 mov al, 0xce ; al = current directions being tried. Doubles as loop counter 1.271 ; over all directions. 1.272 ; Values are the same as those used by the newpos routine 1.273 - mov dx, [bx + si + gh_offset_pos] ; dx = current ghost position 1.274 - cmp dx, [si] ; compare dx with Boot-Man position 1.275 - jne .ghost_ai_loop ; If they are equal, 1.276 - mov [si + collision_offset], al ; We store a non-zero value in the collision_detect flag 1.277 - ; We use al here as we know it to be non-zero, and this reduces 1.278 - ; code size compared to using a literal constant. 1.279 + call test_collision ; dx = current ghost position 1.280 .ghost_ai_loop: 1.281 push dx 1.282 cmp al, ah ; If the current direction is the forbidden direction 1.283 jz .next ; we continue with the next direction 1.284 call newpos ; Update ghost position and check if it collides with a wall 1.285 jz .next ; if so, we continue with the next direction 1.286 - mov cx, 0x0c10 ; Target position if ghosts are ethereal. Position 0x0c10 1.287 + push ax 1.288 + mov ax, 0x0c10 ; Target position if ghosts are ethereal. Position 0x0c10 1.289 ; (x = 0x10, y = 0x0c) is in the center of the maze. 1.290 cmp byte [si + timer_offset], bh ; See if ghost timer runs. We compare with bh, which is known to be 0. 1.291 jnz .skip_target ; If ghost timer runs, we use the aforementioned target position 1.292 - mov cx, [si] ; Otherwise we use Boot-Man's current position, 1.293 - add cx, [bx + si + gh_offset_focus] ; Updated with an offset that is different for each ghost 1.294 + mov ax, [si + bm_offset_pos] ; Otherwise we use Boot-Man's current position, 1.295 + add ax, [bx + si + gh_offset_focus] ; Updated with an offset that is different for each ghost 1.296 .skip_target: 1.297 ;----------------------------------------------------------------------------------------------------- 1.298 ; get_distance: Calculate distance between positions in cx (target position) and dx (ghost position) 1.299 @@ -300,28 +333,20 @@ 1.300 ; The square of the distance between the positions in cx and dx is calculated, 1.301 ; according to Pythagoras' theorem. 1.302 ;----------------------------------------------------------------------------------------------------- 1.303 - push ax 1.304 - sub cl, dl ; after this, cl contains the horizontal difference 1.305 - sub ch, dh ; and ch the vertical difference 1.306 + sub al, dl ; after this, al contains the horizontal difference 1.307 + sub ah, dh ; and ah the vertical difference 1.308 1.309 - mov al, ch 1.310 - cbw 1.311 - xchg ax,cx 1.312 - cbw ; expand cl to ax and ch to cx 1.313 - 1.314 - push dx 1.315 - mul ax 1.316 - xchg ax,cx ; cx = square of vertical difference 1.317 - mul ax ; ax = square of horizontal difference 1.318 - pop dx 1.319 - 1.320 + mov cl, ah 1.321 + imul al 1.322 + xchg ax, cx ; cx = square of horizontal difference 1.323 + imul al ; ax = square of vertical difference 1.324 add cx, ax ; cx = distance squared between positions in cx and dx 1.325 pop ax 1.326 1.327 cmp cx, bp ; Compare this distance to the current minimum 1.328 jnc .next ; and if it is, 1.329 mov bp, cx ; update the minimum distance 1.330 - mov [bx + si], al ; set the movement direction to the current direction 1.331 + mov [bx + si + gh_offset_dir], al ; set the movement direction to the current direction 1.332 mov [bx + si + gh_offset_pos], dx ; Store the new ghost position 1.333 .next: 1.334 pop dx ; Restore the current ghost position 1.335 @@ -342,29 +367,62 @@ 1.336 ; Note that this "terrain storing" approach can trigger a bug 1.337 ; if Boot-Man and a ghost share a position. In that case 1.338 ; an extra Boot-Man character may be drawn on screen. 1.339 - mov dx, [bx + si + gh_offset_pos + gh_length] ; dx = updated ghost position 1.340 - cmp dx, [si] ; compare dx with Boot-Man's position 1.341 - jne .skip_collision ; and if they coincide, 1.342 - mov [si + collision_offset], al ; set the collision detect flag to a non-zero value. 1.343 -.skip_collision: 1.344 + add bx, gh_length ; go to next ghost 1.345 + call test_collision ; dx = current ghost position 1.346 call get_screenpos ; find the address in video ram of the updated ghost position, 1.347 mov ax, [es:di] ; store its content in ax 1.348 - mov [bx + si + gh_offset_terrain + gh_length], ax ; and copy it to ghostterrain 1.349 - add bx, gh_length ; go to next ghost 1.350 + mov [bx + si + gh_offset_terrain], ax ; and copy it to ghostterrain 1.351 cmp bx, 3 * gh_length + bm_length ; and determine if it is the final ghost 1.352 jnz .ghostterrain_loop 1.353 1.354 - ; Test if ghosts are invisible 1.355 - mov ax, 0x2fec ; Assume ghost is visible: 0x2f = purple background, white text 1.356 - ; 0xec = infinity symbol = ghost eyes 1.357 - mov cl, 0x10 ; cl = difference in colour between successive ghosts 1.358 - ; ch is set to zero as that leads to smaller code 1.359 - cmp byte [si + timer_offset], bh ; See if ghost timer is running (note bh is still zero at this point) 1.360 - jnz .ghosts_invisible ; If it is, ghosts are ethereal 1.361 + mov ax, 0x0f00 ; Update ghost colour to black background, white eyes 1.362 + ; Update difference between colours of successive ghosts. Value of 0x0 1.363 + ; means all ghosts are the same colour when they are ethereal. 1.364 + dec byte [si + timer_offset] ; Update ghost_timer to limit the period of the ghosts being ethereal 1.365 + jns .ghostcolor 1.366 + mov byte [si + timer_offset], bh ; Ghost timer was not running 1.367 + mov ax, ghost_n_incr 1.368 +ghost_attr_patch equ $ - 2 1.369 + 1.370 +.ghostcolor: 1.371 + mov cl, al ; cl = difference in colour between successive ghosts 1.372 + mov al, ghost_color&255 ; 0xec = infinity symbol = ghost eyes 1.373 +.ghostdraw: ; Draw the ghosts on the screen 1.374 + mov dx, [bx + si + gh_offset_pos] ; dx = new ghost position 1.375 + call paint ; show ghost in video ram 1.376 + add ah, cl ; Update ghost colour. 1.377 + sub bl, gh_length ; Loop over all ghosts 1.378 + jns .ghostdraw ; until the final one. 1.379 1.380 - cmp byte [si + collision_offset], bh ; Ghosts are visible, so test for collisions 1.381 - jz .no_collision 1.382 - 1.383 + mov ax, game_loop ; ret instruction will jump to game_loop 1.384 + push ax 1.385 + mov ax, word 0x0e02 ; Draw boot-man on the screen. 0x0e = black background, yellow foreground 1.386 + ; 0x02 = smiley face 1.387 +;----------------------------------------------------------------------------------------------------- 1.388 +; paint: paints a character on screen at given x, y coordinates in dx 1.389 +; simple convenience function that gets called enough to be actually worth it, in terms 1.390 +; of code length. 1.391 +;----------------------------------------------------------------------------------------------------- 1.392 +paint_bootman: 1.393 + mov dx, [si + bm_offset_pos] ; dx = Boot-Man position 1.394 +paint: 1.395 + call get_screenpos ; Convert x, y coordinates in dx to video memory address 1.396 + stosw ; stosw = shorter code for mov [es:di], ax 1.397 + ; stosw also adds 2 to di, but that effect is ignored here 1.398 + ret 1.399 + 1.400 + 1.401 +;----------------------------------------------------------------------------------------------------- 1.402 +; test_collision: if end of game, restart from the beginning 1.403 +;----------------------------------------------------------------------------------------------------- 1.404 + 1.405 +test_collision: 1.406 + mov dx, [bx + si + gh_offset_pos] ; dx = current ghost position 1.407 + cmp dx, [si + bm_offset_pos] ; compare dx with Boot-Man position 1.408 + jnz no_collision 1.409 + cmp byte [si + timer_offset], bh ; Ghost timer was not running 1.410 + jnz no_collision 1.411 + pop ax ; Adjust stack 1.412 %ifdef WaitForAnyKey 1.413 ; Ghosts are visible and collide with boot-man, therefore boot-man is dead 1.414 mov ax, 0x0e0f ; Dead boot-man: 0x0e = black background, yellow foreground 1.415 @@ -373,51 +431,28 @@ 1.416 cbw 1.417 int 0x16 ; Wait for any key 1.418 %endif 1.419 -jump_start equ $ + 1 1.420 - mov si,0xFFFC ; restart boot sector 1.421 - push si 1.422 - ret 1.423 - 1.424 - ; Ghosts are invisible 1.425 -.ghosts_invisible: 1.426 - dec byte [si + timer_offset] ; Update ghost_timer to limit the period of the ghosts being ethereal 1.427 - mov ah, 0x0f ; Update ghost colour to black background, white eyes 1.428 - mov cl, 0x0 ; Update difference between colours of successive ghosts. Value of 0x0 1.429 - ; means all ghosts are the same colour when they are ethereal. 1.430 - 1.431 -.no_collision: 1.432 -.ghostdraw: ; Draw the ghosts on the screen 1.433 - mov dx, [bx + si + gh_offset_pos] ; dx = new ghost position 1.434 - call paint ; show ghost in video ram 1.435 - add ah, cl ; Update ghost colour. 1.436 - sub bx, gh_length ; Loop over all ghosts 1.437 - jns .ghostdraw ; until the final one. 1.438 - 1.439 - 1.440 - mov ax, word 0x0e02 ; Draw boot-man on the screen. 0x0e = black background, yellow foreground 1.441 - ; 0x02 = smiley face 1.442 - call paint_bootman ; show Boot-Man 1.443 - 1.444 -.end: 1.445 - jmp game_loop 1.446 + mov si, 0xfb5e ; restart boot sector 1.447 +source equ $ - 2 1.448 1.449 1.450 ;----------------------------------------------------------------------------------------------------- 1.451 ; copy: self copy to a fixed location 1.452 ;----------------------------------------------------------------------------------------------------- 1.453 -copy: 1.454 - pop si ; Get ip value (0x103 or 0x7C03, sometimes 0x0003) 1.455 + 1.456 +copy equ $ - 2 ; seek to 'pop si, sti' instruction (0x5e 0xfb) 1.457 +; pop si ; Get ip value (0x103 or 0x7C03, sometimes 0x0003) 1.458 +; sti ; Allow interrupts 1.459 push cs 1.460 pop ds 1.461 push cs ; Setup ds and es 1.462 pop es 1.463 - and [si+jump_start-moved], si ; Save value to the restart with unpatched code 1.464 mov di, moved ; Move self to a well known address 1.465 push di 1.466 - mov cx, 512-3 1.467 - sti ; Allow interrupts 1.468 + mov ch, (end-moved)/256+1 1.469 cld ; Clear the direction flag. We use string instructions a lot as they have one-byte codes 1.470 + mov [si+source-moved], si ; Save value to the restart with unpatched code 1.471 rep movsb 1.472 +no_collision: 1.473 ret 1.474 1.475 1.476 @@ -453,7 +488,9 @@ 1.477 ;----------------------------------------------------------------------------------------------------- 1.478 1.479 newpos_bootman: 1.480 - mov dx, [si] ; dx = current position of Boot-Man 1.481 + mov al, [bx+si] ; al = new or current movement direction 1.482 + dec bx 1.483 + mov dx, [si + bm_offset_pos] ; dx = current position of Boot-Man 1.484 newpos: 1.485 mov [.modified_instruction + 1], al ; Here the instruction to be executed is modified 1.486 .modified_instruction: 1.487 @@ -461,84 +498,78 @@ 1.488 and dl, 0x1f ; Deal with tunnels 1.489 get_screenpos: 1.490 xchg ax,di ; save ax 1.491 - mov al, 0x28 1.492 + mov al, Columns 1.493 +Columns_patch equ $ - 1 1.494 mul dh ; multiply ax by 0x28 = 40 decimal, the screen width 1.495 add al, dl 1.496 - adc ah, 0 1.497 + adc ah, bh 1.498 xchg ax, di ; di = y * 40 + x 1.499 +%ifdef MDAsupport 1.500 + add di, Margin 1.501 +Margin_patch equ $ - 1 1.502 +%else 1.503 scasw ; Skip the left border by adding 4 to di 1.504 scasw 1.505 - shl di, 1 ; Multiply di by 2 1.506 +%endif 1.507 + add di, di ; Multiply di by 2 1.508 cmp byte [es:di], 0xdb ; Check to see if the new position collides with a wall 1.509 ; 0xdb = full block character that makes up the wall 1.510 ret 1.511 1.512 -;----------------------------------------------------------------------------------------------------- 1.513 -; paint: paints a character on screen at given x, y coordinates in dx 1.514 -; simple convenience function that gets called enough to be actually worth it, in terms 1.515 -; of code length. 1.516 -;----------------------------------------------------------------------------------------------------- 1.517 -paint_bootman: 1.518 - mov dx, [si] ; dx = Boot-Man position 1.519 -paint: 1.520 - call get_screenpos ; Convert x, y coordinates in dx to video memory address 1.521 - stosw ; stosw = shorter code for mov [es:di], ax 1.522 - ; stosw also adds 2 to di, but that effect is ignored here 1.523 - ret 1.524 - 1.525 - 1.526 -bootman_data: 1.527 - db 0x0f, 0x0f ; Boot-Man's x and y position 1.528 - db 0xca ; Boot-Man's direction 1.529 - db 0xca ; Boot-Man's future direction 1.530 - 1.531 -pace_counter: db 0x10 1.532 -ghost_timer: db 0x0 ; if > 0 ghosts are invisible, and is counted backwards to 0 1.533 - 1.534 -ghostdata: 1.535 - db 0xc2 ; 1st ghost, direction 1.536 -ghostpos: 1.537 - db 0x01, 0x01 ; x and y position 1.538 -ghostterrain: 1.539 - dw 0x0ff9 ; terrain underneath 1.540 -ghostfocus: 1.541 - db 0x0, 0x0 ; focus point for movement 1.542 -secondghost: 1.543 - db 0xce ; 2nd ghost, direction 1.544 - db 0x01, 0x17 ; x and y position 1.545 - dw 0x0ff9 ; terrain underneath 1.546 - db 0x0, 0x4 ; focus point for movement 1.547 - db 0xca ; 3rd ghost, direction 1.548 - db 0x1e, 0x01 ; x and y position 1.549 - dw 0x0ff9 ; terrain underneath 1.550 - db 0xfc, 0x0 ; focus point for movement 1.551 - db 0xce ; 4th ghost, direction 1.552 - db 0x1e, 0x17 ; x and y position 1.553 - dw 0x0ff9 ; terrain underneath 1.554 - db 0x4, 0x0 ; focus point for movement 1.555 -lastghost: 1.556 - 1.557 -bm_length equ ghostdata - bootman_data 1.558 -gh_length equ secondghost - ghostdata 1.559 -gh_offset_pos equ ghostpos - ghostdata 1.560 -gh_offset_terrain equ ghostterrain - ghostdata 1.561 -gh_offset_focus equ ghostfocus - ghostdata 1.562 -pace_offset equ pace_counter - bootman_data 1.563 -timer_offset equ ghost_timer - bootman_data 1.564 - 1.565 ; The maze, as a bit array. Ones denote walls, zeroes denote food dots / corridors 1.566 ; The maze is stored upside down to save one cmp instruction in buildmaze 1.567 maze: dw 0xffff, 0x8000, 0xbffd, 0x8081, 0xfabf, 0x8200, 0xbefd, 0x8001 1.568 dw 0xfebf, 0x0080, 0xfebf, 0x803f, 0xaebf, 0xaebf, 0x80bf, 0xfebf 1.569 dw 0x0080, 0xfefd, 0x8081, 0xbebf, 0x8000, 0xbefd, 0xbefd, 0x8001 1.570 dw 0xffff 1.571 -maze_length: equ $ - maze 1.572 1.573 -; Collision detection flag. It is initialized by the code 1.574 -collision_detect: 1.575 +bootman_data: 1.576 +bootmanpos: 1.577 + db 0x0f, 0x0f ; Boot-Man's x and y position 1.578 +bootmandir: 1.579 + db 0xca ; Boot-Man's direction 1.580 + db 0xca ; Boot-Man's future direction 1.581 1.582 -collision_offset equ collision_detect - bootman_data 1.583 +ghost_timer equ maze + 2 ; if > 0 ghosts are invisible, and is counted backwards to 0 1.584 +;pace_counter equ maze + 22 ; 0x3f 1.585 +pace_counter: db 0x10 1.586 + 1.587 +ghostdata: 1.588 +ghostpos: 1.589 + db 0x01, 0x01 ; 1st ghost, x and y position 1.590 +ghostdir: 1.591 + db 0xc2 ; direction 1.592 +ghostterrain: 1.593 + dw 0x0ff9 ; terrain underneath 1.594 +ghostfocus: 1.595 + db 0x0, 0x0 ; focus point for movement 1.596 +secondghost: 1.597 + db 0x01, 0x17 ; 2nd ghost, x and y position 1.598 + db 0xce ; direction 1.599 + dw 0x0ff9 ; terrain underneath 1.600 + db 0x0, 0x4 ; focus point for movement 1.601 + db 0x1e, 0x01 ; 3rd ghost, x and y position 1.602 + db 0xca ; direction 1.603 + dw 0x0ff9 ; terrain underneath 1.604 + db 0xfc, 0x0 ; focus point for movement 1.605 + db 0x1e, 0x17 ; 4th ghost, x and y position 1.606 + db 0xce ; direction 1.607 + dw 0x0ff9 ; terrain underneath 1.608 + db 0x4, 0x0 ; focus point for movement 1.609 +lastghost: 1.610 + 1.611 +bm_length equ ghostdata - bootman_data 1.612 +gh_length equ secondghost - ghostdata 1.613 +gh_offset_dir equ ghostdir - ghostdata 1.614 +gh_offset_pos equ ghostpos - ghostdata 1.615 +gh_offset_terrain equ ghostterrain - ghostdata 1.616 +gh_offset_focus equ ghostfocus - ghostdata 1.617 +pace_offset equ pace_counter - bootman_data 1.618 +timer_offset equ ghost_timer - bootman_data 1.619 +bm_offset_pos equ bootmanpos - bootman_data 1.620 1.621 times 510 - ($ - $$) db 0 1.622 db 0x55 1.623 -db 0xaa 1.624 \ No newline at end of file 1.625 +db 0xaa 1.626 + 1.627 +end:
2.1 --- a/bootbricks/stuff/bricks.asm Wed Oct 04 13:13:17 2023 +0000 2.2 +++ b/bootbricks/stuff/bricks.asm Sun Feb 04 18:02:38 2024 +0000 2.3 @@ -14,32 +14,52 @@ 2.4 ; Press Left Alt to move the paddle to the right 2.5 ; 2.6 2.7 -old_time: equ 16 ; Old time 2.8 +old_time: equ 16 ; word: Old time 2.9 ball_x: equ 14 ; X-coordinate of ball (8.8 fraction) 2.10 ball_y: equ 12 ; Y-coordinate of ball (8.8 fraction) 2.11 ball_xs: equ 10 ; X-speed of ball (8.8 fraction) 2.12 ball_ys: equ 8 ; Y-speed of ball (8.8 fraction) 2.13 -beep: equ 6 ; Frame count to turn off sound 2.14 -bricks: equ 4 ; Remaining bricks 2.15 -score: equ 2 ; Current score 2.16 -balls: equ 0 ; Remaining balls 2.17 +beep: equ 6 ; byte: Frame count to turn off sound 2.18 +bricks: equ 4 ; word: Remaining bricks 2.19 +score: equ 2 ; word: Current score 2.20 +balls: equ 0 ; byte: Remaining balls 2.21 + 2.22 +%define MDA_SUPPORT 2.23 2.24 ; 2.25 ; Start of the game 2.26 ; 2.27 start: 2.28 + cld 2.29 sti ; Allow interrupts 2.30 - mov ax,0xb800 ; Address of video screen 2.31 - mov ds,ax ; Setup DS 2.32 - mov es,ax ; Setup ES 2.33 - sub sp,32 2.34 - cbw 2.35 + push cs 2.36 + pop ss 2.37 + mov sp,0xb800 ; Address of video screen 2.38 +.set_segments: 2.39 + mov ds,sp ; Setup DS 2.40 + mov es,sp ; Setup ES 2.41 +%ifdef MDA_SUPPORT 2.42 + xor di,di 2.43 + mov sp,0xb000 ; Address of video screen 2.44 + inc word [di] 2.45 + jz .set_segments 2.46 + xchg ax,di 2.47 +%else 2.48 + xor ax,ax 2.49 +%endif 2.50 push ax ; Reset score 2.51 mov al,4 2.52 push ax ; Balls remaining 2.53 mov bp,sp ; Setup stack frame for globals 2.54 mov al,0x02 ; Text mode 80x25x16 colors 2.55 int 0x10 ; Setup 2.56 +%if 0 2.57 + ; Disable VGA text mode cursor 2.58 + ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor 2.59 + mov ah, 0x01 2.60 + mov ch, 0x3f 2.61 + int 0x10 2.62 +%endif 2.63 ; 2.64 ; Start another level 2.65 ; 2.66 @@ -48,7 +68,6 @@ 2.67 xor di,di 2.68 mov ax,0x01b1 ; Draw top border 2.69 mov cx,80 2.70 - cld 2.71 rep stosw 2.72 mov cl,24 ; 24 rows 2.73 .1: 2.74 @@ -64,13 +83,17 @@ 2.75 .2: 2.76 mov cl,39 ; 39 bricks per row 2.77 .3: 2.78 + test ah,0x07 2.79 + jne .4 2.80 +%ifdef MDA_SUPPORT 2.81 + mov ah,0x02 2.82 +%else 2.83 + mov ah,0x01 2.84 +%endif 2.85 +.4: 2.86 stosw 2.87 stosw 2.88 inc ah ; Increase attribute color 2.89 - cmp ah,0x08 2.90 - jne .4 2.91 - mov ah,0x01 2.92 -.4: 2.93 loop .3 2.94 pop cx 2.95 2.96 @@ -85,9 +108,8 @@ 2.97 another_ball: 2.98 mov byte [bp+ball_x+1],0x28 ; Center X 2.99 mov byte [bp+ball_y+1],0x14 ; Center Y 2.100 - xor ax,ax 2.101 - mov [bp+ball_xs],ax ; Static on screen 2.102 - mov [bp+ball_ys],ax 2.103 + and word [bp+ball_xs],0 ; Static on screen 2.104 + and word [bp+ball_ys],0 2.105 mov byte [bp+beep],0x01 2.106 2.107 mov si,0x0ffe ; Don't erase ball yet 2.108 @@ -172,7 +194,7 @@ 2.109 mov al,[bx] 2.110 cmp al,0xb1 ; Touching borders 2.111 jne .3 2.112 - mov cx,5423 ; 1193180 / 220 2.113 + mov cl,5423/256 ; 1193180 / 220 2.114 call speaker ; Generate sound 2.115 pop bx 2.116 pop ax 2.117 @@ -197,30 +219,29 @@ 2.118 shl bx,cl 2.119 mov [bp+ball_xs],bx ; New X speed for ball 2.120 mov word [bp+ball_ys],0xff80 ; Update Y speed for ball 2.121 - mov cx,2711 ; 1193180 / 440 2.122 + mov cl,2711/256 ; 1193180 / 440 2.123 call speaker ; Generate sound 2.124 - pop bx 2.125 +.16: pop bx 2.126 pop ax 2.127 jmp .14 2.128 2.129 .4: 2.130 cmp al,0xdb ; Touching brick 2.131 jne .5 2.132 - mov cx,1355 ; 1193180 / 880 2.133 + mov cl,1355/256 ; 1193180 / 880 2.134 call speaker ; Generate sound 2.135 test bl,2 ; Aligned with brick? 2.136 jne .10 ; Yes, jump 2.137 dec bx ; Align 2.138 dec bx 2.139 -.10: xor ax,ax ; Erase brick 2.140 - mov [bx],ax 2.141 - mov [bx+2],ax 2.142 +.10: and word [bx],0 ; Erase brick 2.143 + and word [bx+2],0 2.144 inc word [bp+score] ; Increase score 2.145 neg word [bp+ball_ys] ; Negate Y speed (rebound) 2.146 + dec word [bp+bricks] ; One brick less on screen 2.147 + jne .16 ; Fully completed? No, jump. 2.148 pop bx 2.149 pop ax 2.150 - dec word [bp+bricks] ; One brick less on screen 2.151 - jne .14 ; Fully completed? No, jump. 2.152 jmp another_level ; Start another level 2.153 2.154 .5: 2.155 @@ -237,7 +258,7 @@ 2.156 ; Ball lost 2.157 ; 2.158 ball_lost: 2.159 - mov cx,10846 ; 1193180 / 110 2.160 + mov cl,10846/256 ; 1193180 / 110 2.161 call speaker ; Generate sound 2.162 2.163 and word [si],0 ; Erase ball 2.164 @@ -257,31 +278,27 @@ 2.165 je .0 2.166 mov [bp+old_time],dx 2.167 2.168 - dec byte [bp+beep] ; Decrease time to turn off beep 2.169 - jne .1 2.170 + dec byte [bp+beep] ; Decrease time to turn off beep 2.171 + jne speaker.2 2.172 .2: 2.173 in al,0x61 2.174 and al,0xfc ; Turn off 2.175 - out 0x61,al 2.176 -.1: 2.177 - 2.178 - ret 2.179 + jmp speaker.1 2.180 2.181 ; 2.182 ; Generate sound on PC speaker 2.183 ; 2.184 speaker: 2.185 + mov byte [bp+beep],3 ; Duration 2.186 mov al,0xb6 ; Setup timer 2 2.187 out 0x43,al 2.188 - mov al,cl ; Low byte of timer count 2.189 - out 0x42,al 2.190 - mov al,ch ; High byte of timer count 2.191 + out 0x42,al ; Low byte of timer count 2.192 + xchg ax,cx ; High byte of timer count 2.193 out 0x42,al 2.194 in al,0x61 2.195 or al,0x03 ; Connect PC speaker to timer 2 2.196 - out 0x61,al 2.197 - mov byte [bp+beep],3 ; Duration 2.198 - ret 2.199 +.1: out 0x61,al 2.200 +.2: ret 2.201 2.202 ; 2.203 ; Locate ball on screen 2.204 @@ -291,7 +308,7 @@ 2.205 mul ah ; AH = Y coordinate (row) 2.206 mov bl,bh ; BH = X coordinate (column) 2.207 mov bh,0 2.208 - shl bx,1 2.209 + add bx,bx 2.210 add bx,ax 2.211 ret 2.212
3.1 --- a/bootfbird/stuff/fbird.asm Wed Oct 04 13:13:17 2023 +0000 3.2 +++ b/bootfbird/stuff/fbird.asm Sun Feb 04 18:02:38 2024 +0000 3.3 @@ -11,45 +11,55 @@ 3.4 ; previous score. 3.5 ; 3.6 3.7 +%define MDA_SUPPORT 3.8 + 3.9 use16 3.10 cpu 8086 3.11 3.12 -restart: 3.13 - mov ax,0x0002 ; Set 80x25 text mode 3.14 - int 0x10 ; Call BIOS 3.15 +%ifdef FAT_BOOT 3.16 + jmp bootbr 3.17 + nop 3.18 + times 0x3B db 0 3.19 +bootbr: 3.20 +%endif 3.21 + cld ; Reset direction flag (so stosw increments registers) 3.22 + sti ; Allow interrupts 3.23 + push cs 3.24 + pop ss 3.25 + mov sp,0xb800 ; Point to video segment 3.26 +SetSegments: 3.27 + mov ds,sp ; Both the source (common access) 3.28 + mov es,sp ; and target segments 3.29 ; 3.30 ; Game restart 3.31 ; 3.32 -fb21: cld ; Reset direction flag (so stosw increments registers) 3.33 - sti ; Allow interrupts 3.34 - mov ax,0xb800 ; Point to video segment 3.35 - mov ds,ax ; Both the source (common access) 3.36 - mov es,ax ; and target segments 3.37 - mov di,pipe ; Init variables in video segment (saves big bytes) 3.38 - cbw 3.39 - stosw ; pipe 3.40 - stosw ; score 3.41 - stosw ; grav 3.42 - mov al,0xa0 3.43 - stosw ; next 3.44 - mov al,0x60 3.45 - stosw ; bird 3.46 +fb21: 3.47 + mov ax,0x0002 ; Set 80x25 text mode 3.48 + int 0x10 ; Call BIOS 3.49 + mov di,next ; Init variables in video segment (saves big bytes) 3.50 +%ifdef MDA_SUPPORT 3.51 + mov sp,0xb000 3.52 + inc byte [di] 3.53 + je SetSegments 3.54 +%endif 3.55 + mov ax,0x60a0 3.56 + stosw ; next, bird 3.57 + xor ax,ax 3.58 + stosw ; grav, pipe 3.59 + stosb 3.60 + 3.61 + ; Disable text mode cursor 3.62 + ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor 3.63 + mov ah, 0x01 3.64 + mov ch, 0x3f 3.65 + int 0x10 3.66 3.67 mov di,0x004a ; Game title 3.68 - mov ax,0x0f46 ; 'F' in white, good old ASCII 3.69 - stosw 3.70 - mov al,0x2d ; '-' 3.71 - stosw 3.72 - mov al,0x42 ; 'B' 3.73 - stosw 3.74 - mov al,0x49 ; 'I' 3.75 - stosw 3.76 - mov al,0x52 ; 'R' 3.77 - stosw 3.78 - mov al,0x44 ; 'D' 3.79 - stosw 3.80 + call print_string 3.81 + db 'F-BIRD' 3.82 mov bp,80 ; Introduce 80 columns of scenery 3.83 fb1: call scroll_scenery 3.84 +%define SI(n) [si+n-0x0fa2] 3.85 dec bp 3.86 jnz fb1 3.87 3.88 @@ -64,101 +74,43 @@ 3.89 ; 3.90 ; Main loop 3.91 ; 3.92 -fb12: mov si,bird 3.93 - lodsw ; Bird falls... 3.94 - add al,[grav] ; ...because of gravity... 3.95 +fb12: mov ax,[bird] ; Bird falls... 3.96 + add al,ah ; ...because of gravity... 3.97 mov [bird],al ; ...into new position. 3.98 and al,0xf8 ; Row is a 5.3 fraction, nullify fraction 3.99 mov ah,0x14 ; Given integer is x8, multiply by 20 to get 160 per line 3.100 mul ah ; Row into screen 3.101 add ax,$0020 ; Fixed column 3.102 xchg ax,di ; Pass to DI (AX cannot be used as pointer) 3.103 - lodsb ; mov al,[frame] 3.104 + mov dx,$0d1f ; Draw body 3.105 + mov ax,bp ; [frame] 3.106 + mov bx,-160 3.107 and al,4 ; Wing movement each 4 frames 3.108 jz fb15 3.109 - mov al,[di-160] ; Get character below 3.110 - mov word [di-160],$0d1e ; Draw upper wing 3.111 - add al,[di] ; Add another character below 3.112 - shr al,1 ; Normalize 3.113 - mov word [di],$0d14 ; Draw body 3.114 + mov ax,$0d1e ; Draw upper wing 3.115 + xchg ax,[bx+di] ; Get character below 3.116 jmp short fb16 3.117 - 3.118 -fb15: mov al,[di] ; Get character below 3.119 - mov word [di],$0d1f ; Draw body 3.120 -fb16: add al,[di+2] ; Get character below head 3.121 - mov word [di+2],$0d10 ; Draw head 3.122 - cmp al,0x40 ; Collision with scenery? 3.123 - jz fb19 3.124 ; 3.125 ; Stars and game over 3.126 ; 3.127 - mov byte [di],$2a ; '*' Asterisks to indicate crashing 3.128 - mov byte [di+2],$2a 3.129 +fb19: mov al,$2a ; '*' Asterisks to indicate crashing 3.130 + stosb 3.131 + inc di 3.132 + stosb 3.133 mov di,0x07CA 3.134 - mov ax,0x0f42 ; 'B' in white, good old ASCII 3.135 - stosw 3.136 - mov al,0x4F ; 'O' 3.137 - stosw 3.138 - mov al,0x4E ; 'N' 3.139 - stosw 3.140 - mov al,0x4B ; 'K' 3.141 - stosw 3.142 - mov al,0x21 ; '!' 3.143 - stosw 3.144 - mov bp,100 ; Wait 100 frames 3.145 + call print_string 3.146 + db 'BONK!' 3.147 + mov bp,-100 ; Wait 100 frames 3.148 fb20: call wait_frame 3.149 - dec bp 3.150 jnz fb20 3.151 jmp fb21 ; Restart 3.152 3.153 -fb19: call wait_frame ; Wait for frame 3.154 - mov al,[frame] 3.155 - and al,7 ; 8 frames have passed? 3.156 - jnz fb17 ; No, jump 3.157 - inc word [grav] ; Increase gravity 3.158 -fb17: 3.159 - mov al,$20 3.160 - mov [di-160],al ; Delete bird from screen 3.161 - mov [di+2],al 3.162 - stosb 3.163 - call scroll_scenery ; Scroll scenery 3.164 - call scroll_scenery ; Scroll scenery 3.165 - cmp byte [0x00a0],0xb0 ; Passed a column? 3.166 - jz fb27 3.167 - cmp byte [0x00a2],0xb0 ; Passed a column? 3.168 -fb27: jnz fb24 3.169 - inc word [score] ; Increase score 3.170 - mov ax,[score] 3.171 - mov di,0x008e ; Show current score 3.172 -fb25: xor dx,dx ; Extend AX to 32 bits 3.173 - mov bx,10 ; Divisor is 10 3.174 - div bx ; Divide 3.175 - add dx,0x0c30 ; Convert remaining 0-9 to ASCII, also put color 3.176 - xchg ax,dx 3.177 - std 3.178 - stosw 3.179 - mov byte [di],0x20 ; Clean at least one character of prev. score 3.180 - cld 3.181 - xchg ax,dx 3.182 - or ax,ax ; Score digits still remain? 3.183 - jnz fb25 ; Yes, jump 3.184 -fb24: mov ah,0x01 ; Any key pressed? 3.185 - int 0x16 3.186 - jz fb26 ; No, go to main loop 3.187 - mov ah,0x00 3.188 - int 0x16 ; Get key 3.189 - dec ah ; Escape key? 3.190 - jne fb4 ; No, jump 3.191 - mov al,3 3.192 - int 0x10 3.193 - int 0x20 ; Exit to DOS or to oblivion (boot sector) 3.194 - int 0x19 3.195 -fb4: mov ax,[bird] 3.196 - sub ax,0x10 ; Move bird two rows upward 3.197 - cmp ax,0x08 ; Make sure the bird doesn't fly free outside screen 3.198 +fb4: mov al,[bird] 3.199 + cmp al,0x18 ; Make sure the bird doesn't fly free outside screen 3.200 jb fb18 3.201 + sub al,0x10 ; Move bird two rows upward 3.202 +fb18: cbw ; Reset gravity 3.203 mov [bird],ax 3.204 -fb18: mov byte [grav],0 ; Reset gravity 3.205 mov al,0xb6 ; Flap sound 3.206 out (0x43),al 3.207 mov al,0x90 3.208 @@ -170,6 +122,56 @@ 3.209 out (0x61),al 3.210 fb26: jmp fb12 3.211 3.212 +fb16: mov dl,$14 ; Draw body 3.213 +fb15: or al,[di] ; Add another character below 3.214 + mov [di],dx ; Draw body 3.215 + mov dl,$10 ; Draw head 3.216 + xchg dx,[di+2] ; Get character below head 3.217 + add al,dl 3.218 + cmp al,0x40 ; Collision with scenery? 3.219 + jnz fb19 3.220 + call wait_frame ; Wait for frame 3.221 + mov ax,bp ; [frame] 3.222 + and al,7 ; 8 frames have passed? 3.223 + jnz fb17 ; No, jump 3.224 + inc byte SI(grav) ; Increase gravity 3.225 +fb17: 3.226 + mov al,$20 3.227 + mov [bx+di],al ; Delete bird from screen 3.228 + stosb 3.229 + inc di 3.230 + stosb 3.231 + call scroll_scenery ; Scroll scenery 3.232 + call scroll_scenery ; Scroll scenery 3.233 + mov di,bx 3.234 + mov al,0xb0 3.235 + scasb ; Passed a column? 3.236 + jz fb27 3.237 + inc di 3.238 + scasb ; Passed a column? 3.239 + jnz fb24 3.240 +fb27: mov di,0x008e ; Show current score 3.241 +fb25: dec di 3.242 + dec di 3.243 + mov ax,0x0c30 ; Convert remaining 0-9 to ASCII, also put color 3.244 + xchg [di], ax 3.245 + or al, 0x30 3.246 + cmp al, '9' 3.247 + je fb25 3.248 + inc ax 3.249 + stosb 3.250 +fb24: mov ah,0x01 ; Any key pressed? 3.251 + int 0x16 3.252 + jz fb26 ; No, go to main loop 3.253 + mov ah,0x00 3.254 + int 0x16 ; Get key 3.255 + dec ah ; Escape key? 3.256 + jne fb4 ; No, jump 3.257 +quit: mov al,3 3.258 + int 0x10 3.259 + int 0x20 ; Exit to DOS or to oblivion (boot sector) 3.260 + int 0x19 3.261 + 3.262 ; 3.263 ; Scroll scenery one column at a time 3.264 ; 3.265 @@ -179,6 +181,7 @@ 3.266 ; 3.267 mov si,0x00a2 ; Point to row 1, column 1 in SI 3.268 mov di,0x00a0 ; Point to row 1, column 0 in DI 3.269 + mov bx,di 3.270 fb2: mov cx,79 ; 79 columns 3.271 repz ; Scroll!!! 3.272 movsw 3.273 @@ -190,42 +193,49 @@ 3.274 ; 3.275 ; Insert houses 3.276 ; 3.277 - mov word [0x0f9e],0x02df ; Terrain 3.278 + mov word SI(0x0f9e),0x02df ; Terrain 3.279 in al,(0x40) ; Get "random" number 3.280 - and al,0x70 3.281 + test al,0x70 3.282 jz fb5 3.283 - mov bx,0x0408 ; House of one floor 3.284 - mov [0x0efe],bx 3.285 + mov dx,0x0408 ; House of one floor 3.286 mov di,0x0e5e 3.287 - and al,0x20 ; Check "random" number 3.288 + mov [bx+di],dx 3.289 + test al,0x20 ; Check "random" number 3.290 jz fb3 3.291 - mov [di],bx ; House of two floors 3.292 - sub di,0x00a0 3.293 + mov [di],dx ; House of two floors 3.294 + sub di,bx 3.295 fb3: mov word [di],0x091e ; Add roof 3.296 ; 3.297 ; Check if it's time to insert a column 3.298 ; 3.299 -fb5: dec word [next] ; Decrease time (column really) for next pipe 3.300 - mov bx,[next] 3.301 - cmp bx,0x03 ; bx = 3,2,1,0 for the four columns making the pipe 3.302 - ja fb6 3.303 - jne fb8 3.304 - in al,(0x40) ; Get "random" number 3.305 - and ax,0x0007 ; Between 0 and 7 3.306 +fb5: dec byte SI(next) ; Decrease time (column really) for next pipe 3.307 + mov dh,SI(next) 3.308 + mov dl,0xb1 ; Leftmost 3.309 + mov cl,3 3.310 + cmp dh,cl ; bl = 3,2,1,0 for the four columns making the pipe 3.311 + ja fb6 ; No yet, jump 3.312 + jne fb8 ; No leftmost, jump 3.313 + and al,0x07 ; Between 0 and 7 3.314 add al,0x04 ; Between 4 and 11 3.315 - mov [tall],ax ; This will tell how tall the pipe is 3.316 -fb8: mov cx,[tall] 3.317 - or bx,bx ; Rightmost? 3.318 + mov SI(tall),al ; This will tell how tall the pipe is 3.319 + jmp fb7 ; Leftmost, jump 3.320 + 3.321 +fb8: mov dl,0xdb 3.322 + or dh,dh ; Rightmost? 3.323 + jnz fb7 ; No, jump 3.324 mov dl,0xb0 3.325 - jz fb7 ; Yes, jump 3.326 - mov dl,0xdb 3.327 - cmp bx,0x03 ; Leftmost? 3.328 - jb fb7 ; No, jump 3.329 - mov dl,0xb1 3.330 + dec word SI(pipe) ; Increase total pipes shown 3.331 + or byte SI(pipe+1),0xfe 3.332 + mov ax,SI(pipe) 3.333 + sar ax,cl 3.334 + add al,0x50 ; Decrease distance between pipes 3.335 + mov SI(next),al ; Time for next pipe 3.336 fb7: mov di,0x013e ; Start from top of screen 3.337 + xchg ax,dx 3.338 mov ah,0x0a 3.339 - mov al,dl 3.340 + push ax 3.341 mov dx,0x009e 3.342 + mov cl,SI(tall) 3.343 fb9: stosw 3.344 add di,dx 3.345 loop fb9 3.346 @@ -234,56 +244,53 @@ 3.347 add di,0x009e*6+10 3.348 mov al,0xdf 3.349 stosw 3.350 - add di,dx 3.351 -fb10: mov al,dl 3.352 + pop ax 3.353 +fb10: add di,dx 3.354 stosw 3.355 - add di,dx 3.356 - cmp di,0x0f00 3.357 + cmp di,0x0ea0 3.358 jb fb10 3.359 - or bx,bx 3.360 - jnz fb6 3.361 - mov ax,[pipe] 3.362 - inc ax ; Increase total pipes shown 3.363 - mov [pipe],ax 3.364 - mov cl,3 3.365 - shr ax,cl 3.366 - mov ah,0x50 ; Decrease distance between pipes 3.367 - sub ah,al 3.368 - cmp ah,0x10 3.369 - ja fb11 3.370 - mov ah,0x10 3.371 -fb11: mov [next],ah ; Time for next pipe 3.372 fb6: ret 3.373 3.374 ; 3.375 ; Wait for a frame 3.376 ; 3.377 wait_frame: 3.378 - 3.379 - mov bx, tick 3.380 fb14: hlt ; Wait for clock interrupt 3.381 mov ah,0x00 ; Use base clock tick 3.382 int 0x1a 3.383 - cmp dx,[bx] 3.384 + cmp dx,[tick] 3.385 jz fb14 3.386 - mov [bx],dx 3.387 - inc word [bx+frame-tick] ; Increase frame count 3.388 + mov [tick],dx 3.389 in al,(0x61) 3.390 - and al,0xfc ; Turn off sound 3.391 + and al,0xfc ; Turn off sound 3.392 out (0x61),al 3.393 + inc bp ; Increase frame count 3.394 ret 3.395 3.396 - times 507 - ($-$$) db 0 ; pad remaining 507 bytes with zeroes 3.397 + ; 3.398 + ; Print a string 3.399 + ; 3.400 +print_string: 3.401 + pop si 3.402 + mov ah, 0xf ; white 3.403 + db 0x3C ; cmp al,0xab 3.404 +pr1: stosw 3.405 + cs lodsb 3.406 + cmp al,0xbd 3.407 + jne pr1 3.408 + dec si 3.409 + push si 3.410 + ret 3.411 3.412 - db "OTG" ; 3 unused bytes 3.413 + times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes 3.414 3.415 db 0x55,0xaa ; Bootable signature 3.416 3.417 -pipe: equ 0x0fa0 3.418 -score: equ 0x0fa2 3.419 -grav: equ 0x0fa4 3.420 -next: equ 0x0fa6 3.421 -bird: equ 0x0fa8 3.422 -frame: equ 0x0faa 3.423 -tall: equ 0x0fac 3.424 -tick: equ 0x0fae 3.425 +tick: equ 0x0fa0 ; word 3.426 + 3.427 +next: equ tick+2 ; byte 3.428 +bird: equ tick+3 ; byte 3.429 +grav: equ bird+1 ; byte 3.430 +pipe: equ tick+5 ; word 3.431 + 3.432 +tall: equ tick+7 ; byte
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/bootlife/receipt Sun Feb 04 18:02:38 2024 +0000 4.3 @@ -0,0 +1,33 @@ 4.4 +# SliTaz package receipt. 4.5 + 4.6 +PACKAGE="bootlife" 4.7 +VERSION="1.0" 4.8 +CATEGORY="games" 4.9 +SHORT_DESC="Bootable Conway's life game in a 512-byte boot sector." 4.10 +MAINTAINER="pascal.bellard@slitaz.org" 4.11 +LICENSE="MIT" 4.12 +WEB_SITE="http://www.slitaz.org" 4.13 +TARGET="i486" 4.14 + 4.15 +BUILD_DEPENDS="nasm" 4.16 + 4.17 +# Rules to configure and make the package. 4.18 +compile_rules() 4.19 +{ 4.20 + mkdir -p $src 4.21 + nasm -o $src/life.com -l $src/life.lst $stuff/life.asm 4.22 +} 4.23 + 4.24 +# Rules to gen a SliTaz package suitable for Tazpkg. 4.25 +genpkg_rules() 4.26 +{ 4.27 + mkdir -p $fs/boot 4.28 + cp $src/life.com $fs/boot/life 4.29 +} 4.30 + 4.31 +# Post install/remove commands for Tazpkg. 4.32 +post_install() 4.33 +{ 4.34 + grep -qs ^bootlife $1/boot/bootmenu || 4.35 + echo "life Life,life Conway's life game (may run under DOS if renamed to life.com)" >> $1/boot/bootmenu 4.36 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/bootlife/stuff/life.asm Sun Feb 04 18:02:38 2024 +0000 5.3 @@ -0,0 +1,413 @@ 5.4 +bits 16 5.5 + 5.6 +%define ResetScreen 5.7 +%define StepCounter 5.8 + 5.9 +cpu 8086 5.10 +org 0x7c00 5.11 + 5.12 +;; Constants 5.13 + 5.14 +;; Boot sector size in bytes 5.15 +%assign BootSector.Size 512 5.16 + 5.17 +;; Words in 16 bit x86 are 2 bytes 5.18 +%assign WordSize 2 5.19 + 5.20 +%assign DigitsNumber 6 5.21 + 5.22 +;; This is the value to store in segment register to access the MDA and the VGA text buffer. 5.23 +;; In 16 bit x86, segmented memory accesses are of the form: 5.24 +;; 5.25 +;; (segment register) * 0x10 + (offset register) 5.26 +;; 5.27 +;; The MDA text buffer is at 0xb00000, so if 0xb000 is stored in a segment 5.28 +;; register, then memory access instructions will be relative to the MDA text 5.29 +;; buffer, allowing easier access. For example, trying to access the nth byte of 5.30 +;; memory will *actually* access the nth byte of the text buffer. 5.31 +%assign TextBuf.Seg 0xb000 5.32 +%assign Vga.TextBuf.Offset 0x8000 5.33 + 5.34 +%assign BitMap 0x8000 5.35 + 5.36 +;; Dimensions of text buffer 5.37 +%assign TextBuf.Width 80 5.38 +%assign TextBuf.Height 25 5.39 +%assign TextBuf.Size (TextBuf.Width * TextBuf.Height) 5.40 + 5.41 +;; Macro to get the index of a text buffer cell from coordinates 5.42 +%define TextBuf.Index(y, x) ((y) * TextBuf.Width * 2 + (x) * 2) 5.43 + 5.44 +%assign Pixel.UpDown 0xdb 5.45 +%assign Pixel.Up 0xdf 5.46 +%assign Pixel.Down 0xdc 5.47 +%assign Pixel.Free 0x20 5.48 +%assign Cursor.Up 0x1e 5.49 +%assign Cursor.Down 0x1f 5.50 + 5.51 +;; Keyboard scan codes 5.52 +;; http://www.delorie.com/djgpp/doc/rbinter/it/06/0.html 5.53 +%assign Key.ScanCode.Tab 0x0f 5.54 +%assign Key.ScanCode.Space 0x39 5.55 +%assign Key.ScanCode.Up 0x48 5.56 +%assign Key.ScanCode.Down 0x50 5.57 +%assign Key.ScanCode.Left 0x4b 5.58 +%assign Key.ScanCode.Right 0x4d 5.59 +%assign Key.ScanCode.Enter 0x1c 5.60 +%assign Key.ScanCode.QuitGame 0x01 5.61 + 5.62 +;; BootLife is supported as both a DOS game and a boot sector game :) 5.63 + 5.64 +;; Entry point: set up graphics and run game 5.65 +BootLife: 5.66 + cld 5.67 + sti ;allow interrupts 5.68 + 5.69 +%ifdef ResetScreen 5.70 + ; MDA/VGA text mode 0x03 5.71 + ; 80x25 text resolution 5.72 + mov ax, 3 5.73 + int 0x10 5.74 + 5.75 + ; Disable VGA text mode cursor 5.76 + ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor 5.77 + mov ah, 0x01 5.78 + mov ch, 0x3f 5.79 + int 0x10 5.80 +%endif 5.81 + 5.82 +;; Run game (the game is restarted by jumping here) 5.83 +RunGame: 5.84 + push cs 5.85 + pop ss 5.86 + mov sp, TextBuf.Seg 5.87 + 5.88 + ; Load VGA text buffer segment into segment registers 5.89 + mov es, sp 5.90 + push cs 5.91 + pop ds 5.92 + 5.93 +;; 5.94 +tick: 5.95 + call get_data 5.96 +data: 5.97 +%define INITDATA_COLS 36 5.98 +%define INITDATA_POS 24 5.99 +%define INITDATA_OFFSET TextBuf.Width*5+10 5.100 +datainit: 5.101 + db 0x01 5.102 +cursor: db 0x00 5.103 +xpos: db 0x00,0x00,0x14 5.104 +ypos: db 0x00 5.105 + db 0x00,0x30,0x30 5.106 +cmd: db 0x00,0x0c , 0x80,0x08,0x03,0xc0 5.107 + db 0x03,0x04,0x31,0x00,0x30 , 0x40,0x34,0x14 5.108 +cursor_pos: 5.109 + db 0x00 5.110 + db 0x00,0x04,0x01,0x01,0x00 , 0x80,0x08,0x00,0x00 5.111 + db 0x00,0x30 5.112 +datainit_end: 5.113 +;012345670123456701234567012345670123 5.114 +; X ; 5.115 +; X X ; 5.116 +; XX XX XX; 5.117 +; X X XX XX; 5.118 +;XX X X XX ; 5.119 +;XX X X XX X X ; 5.120 +; X X X ; 5.121 +; X X ; 5.122 +; XX ; 5.123 +;awk 'BEGIN { n = split("0x00,0x00...0x00", t,",") 5.124 +; i=1; x=strtonum(t[i]); b=8; c=36 5.125 +; while (i <= n) { 5.126 +; if (and(x,1) == 0) printf " "; else printf "X" 5.127 +; x=rshift(x,1) 5.128 +; if (--c == 0) { printf "\n" ; c=36 ; } 5.129 +; if (--b == 0) { x=strtonum(t[++i]) ; b=8 ; } 5.130 +; } 5.131 +; printf "\n" 5.132 +;}' < /dev/null 5.133 + 5.134 + 5.135 +get_data: 5.136 + pop bp 5.137 +%define BP(x) [bp + x - data] 5.138 +ZeroBuf: 5.139 + mov dx, INITDATA_COLS-INITDATA_POS 5.140 + mov cx, TextBuf.Size*2+DigitsNumber+1 5.141 + mov bx, BitMap 5.142 +.1: 5.143 + mov byte [bx], dh 5.144 + inc bx 5.145 + loop .1 5.146 + 5.147 + mov bx, BitMap+INITDATA_POS+INITDATA_OFFSET 5.148 + mov si, bp 5.149 + mov cl, datainit_end-datainit 5.150 +.next_byte: 5.151 + lodsb 5.152 + mov ah,8 5.153 +.next_bit: 5.154 + shr al,1 5.155 + adc byte [bx], dh 5.156 + inc bx 5.157 + dec dx 5.158 + jnz .noln 5.159 + mov dl, INITDATA_COLS 5.160 + add bx, TextBuf.Width-INITDATA_COLS 5.161 +.noln: 5.162 + dec ah 5.163 + jnz .next_bit 5.164 + loop .next_byte 5.165 + 5.166 +;; Main loop to process key presses and update state 5.167 +GameLoop: 5.168 +;; Display board 5.169 + xor di, di 5.170 + mov si, TextBuf.Width 5.171 + mov bx, BitMap 5.172 + mov dx, TextBuf.Height + 256 5.173 + push bx 5.174 +.line: 5.175 + mov cx, si 5.176 +.next: 5.177 + mov ax, Pixel.Free + 256*Pixel.Up 5.178 + test byte [bx + si], dh 5.179 + jz .NotDown 5.180 + mov ax, Pixel.Down + 256*Pixel.UpDown 5.181 +.NotDown: 5.182 + test byte [bx], dh 5.183 + jz .NotUp 5.184 + mov al, ah 5.185 +.NotUp: 5.186 + inc bx 5.187 + call PutChar 5.188 + loop .next 5.189 + add bx, si 5.190 + dec dl 5.191 + jnz .line 5.192 + pop bx 5.193 + 5.194 + call PutCursor 5.195 + 5.196 +%ifdef StepCounter 5.197 + mov si, BitMap+TextBuf.Size*2+DigitsNumber 5.198 +.next_digit: 5.199 + mov byte [si], ch 5.200 + dec si 5.201 + inc byte [si] 5.202 + cmp byte [si], 10 5.203 + je .next_digit 5.204 + mov cl, DigitsNumber 5.205 + mov si, BitMap+TextBuf.Size*2 5.206 + mov di, 6 5.207 +.digit: 5.208 + lodsb 5.209 + or al, '0' 5.210 + call PutChar 5.211 + loop .digit 5.212 +%endif 5.213 + 5.214 + cmp byte BP(cmd), Key.ScanCode.Enter 5.215 + jnz wait_key 5.216 + 5.217 + mov ah,0x01 ; Any key pressed? 5.218 + int 0x16 5.219 + jz nokey ; No, go to main loop 5.220 +wait_key: 5.221 + mov ah,0x00 5.222 + int 0x16 ; Get key 5.223 + mov al, ah 5.224 + mov byte BP(cmd), al 5.225 + cmp al, Key.ScanCode.QuitGame 5.226 + jne notesc ; No, jump 5.227 +%ifdef ResetScreen 5.228 + mov al,3 5.229 + int 0x10 5.230 +%endif 5.231 + int 0x20 ; Exit to DOS or to oblivion (boot sector) 5.232 + int 0x19 5.233 + 5.234 +notesc: 5.235 + mov cx, BP(ypos) 5.236 + mov di, BP(xpos) 5.237 + 5.238 + cmp al, Key.ScanCode.Tab 5.239 + jne not_tab 5.240 + call GetTextBufIndex 5.241 + xor byte [bx + si], 1 5.242 +GoGameLoop: 5.243 + jmp GameLoop 5.244 + 5.245 +PutCursor: 5.246 + xchg ax, dx 5.247 + xchg al, BP(cursor) 5.248 + mov di, BP(cursor_pos) 5.249 + cmp al, 0 5.250 + je Ret 5.251 +PutChar: 5.252 + mov [es:di + Vga.TextBuf.Offset], al 5.253 + stosb 5.254 + inc di 5.255 +Ret: ret 5.256 + 5.257 +not_tab: 5.258 +CmpUp: 5.259 + ; Move cursor up 5.260 + dec cx 5.261 + cmp al, Key.ScanCode.Up 5.262 + je WrapCursor 5.263 + inc cx 5.264 +CmpDown: 5.265 + ; Move cursor down 5.266 + inc cx 5.267 + cmp al, Key.ScanCode.Down 5.268 + je WrapCursor 5.269 + dec cx 5.270 +CmpLeft: 5.271 + ; Move cursor left 5.272 + dec di 5.273 + cmp al, Key.ScanCode.Left 5.274 + je WrapCursor 5.275 + inc di 5.276 +CmpRight: 5.277 + ; Move cursor right 5.278 + inc di 5.279 + cmp al, Key.ScanCode.Right 5.280 + jne nokey 5.281 +WrapCursor: 5.282 + call GetTextBufIndex 5.283 + mov BP(ypos), cx 5.284 + mov BP(xpos), di 5.285 + mov al, Cursor.Up 5.286 + shr cx, 1 5.287 + adc al, ch ; Cursor.Down = Cursor.Up + 1 5.288 + add cx, cx 5.289 + call GetTextBufIndex 5.290 + add si, di 5.291 + mov BP(cursor_pos), si 5.292 + mov BP(cursor), al 5.293 +GoGameLoop2: 5.294 + jmp GoGameLoop 5.295 + 5.296 +wait_tick: 5.297 + hlt ; Wait for clock interrupt 5.298 +nokey: 5.299 + mov ah, 0x00 ; Use base clock tick 5.300 + int 0x1a 5.301 + cmp dx, BP(tick) 5.302 + jz wait_tick 5.303 + mov BP(tick), dx 5.304 + 5.305 +;; Compute next board 5.306 + mov cx, TextBuf.Height*2-1 5.307 +.loop_y: 5.308 + mov di, TextBuf.Width-1 5.309 +.loop_x: 5.310 + call CountCells ; si = &TextBuf[cx = y][di = x] 5.311 + sub al, 3 ; empty cell with 3 neighbors or full cell with 2 neighbors 5.312 + je .alive 5.313 + sub al, [bx + si] ; full cell with 3 neighbors 5.314 + jne .dead 5.315 +.alive: 5.316 + or byte [bx + si], 2 5.317 +.dead: 5.318 + dec di 5.319 + jns .loop_x 5.320 + dec cx 5.321 + jns .loop_y 5.322 + 5.323 + mov cx, TextBuf.Size*2 5.324 +.loop_shr: 5.325 + shr byte [bx], 1 5.326 + inc bx 5.327 + loop .loop_shr 5.328 + 5.329 + jmp GoGameLoop2 5.330 + 5.331 + ; Count adjacent cells 5.332 +CountCells: 5.333 + xor ax, ax 5.334 + 5.335 + ; Count left-row 5.336 + dec di 5.337 + call UpAndCountRow 5.338 + 5.339 + ; Count center-row 5.340 + call CountNextRow 5.341 + 5.342 + ; Count right-row 5.343 + call CountNextRow 5.344 + 5.345 + ; re-center 5.346 + dec cx 5.347 + dec di 5.348 + 5.349 +;; Compute the text buffer index from y and x coordinates 5.350 +;; 5.351 +;; si = &TextBuf[cx = y][di = x] 5.352 +;; 5.353 +;; This computes the equivalent of the TextBuf.Index(y, x) macro, but at runtime 5.354 +;; 5.355 +;; Parameters: 5.356 +;; * cx - y coordinate 5.357 +;; * di - x coordinate 5.358 +;; Returns: 5.359 +;; * si - text buffer index 5.360 +GetTextBufIndex: 5.361 + ; Base case: bounds check y 5.362 + mov si, TextBuf.Width 5.363 + cmp cx, TextBuf.Height*2 5.364 + jb .check_x 5.365 + sub cx, TextBuf.Height*2 ; overflow ? 5.366 + jns .check_x 5.367 + add cx, TextBuf.Height*4 ; underflow ! 5.368 +.check_x: 5.369 + ; Base case: bounds check x 5.370 + cmp di, si 5.371 + jb .check_ok 5.372 + sub di, si 5.373 + jns .check_ok 5.374 + add di, TextBuf.Width*2 5.375 +.check_ok: 5.376 + xchg ax, si 5.377 + imul cl 5.378 + xchg ax, si 5.379 + add si, di 5.380 + ret 5.381 + 5.382 +CountNextRow: 5.383 + inc di 5.384 + dec cx 5.385 +UpAndCountRow: 5.386 + dec cx 5.387 +CountRow: 5.388 + call Count 5.389 + call CountDown 5.390 +CountDown: 5.391 + inc cx 5.392 +Count: 5.393 + ; si = &TextBuf[cx = y][di = x] 5.394 + call GetTextBufIndex 5.395 +%if 0 5.396 + cmp [bx + si], ah 5.397 +%else 5.398 + test byte [bx + si], 1 5.399 +%endif 5.400 + je .Ret 5.401 + inc ax 5.402 +.Ret: ret 5.403 + 5.404 +;; Print program size at build time 5.405 +%assign CodeSize $ - $$ 5.406 +%assign Size $ - BootLife 5.407 +%warning Code is Size bytes 5.408 + 5.409 +CodeEnd: 5.410 + ; Pad to size of boot sector, minus the size of a word for the boot sector 5.411 + ; magic value. If the code is too big to fit in a boot sector, the `times` 5.412 + ; directive uses a negative value, causing a build error. 5.413 + times (BootSector.Size - WordSize) - CodeSize db 0 5.414 + 5.415 + ; Boot sector magic 5.416 + dw 0xaa55
6.1 --- a/bootmine/stuff/mine.asm Wed Oct 04 13:13:17 2023 +0000 6.2 +++ b/bootmine/stuff/mine.asm Sun Feb 04 18:02:38 2024 +0000 6.3 @@ -1,12 +1,20 @@ 6.4 bits 16 6.5 6.6 -%define UNVEIL_ON_GAME_OVER 6.7 -%define CGA_DISPLAY 6.8 -%define DOS_QUIT 6.9 -%define FAT_BOOT 6.10 -%define RESTART_ON_ANY_KEY 6.11 +%if 1 6.12 +%define UNVEIL_ON_GAME_OVER +16 6.13 +%define CGA_DISPLAY +6 6.14 +%define MDA_SUPPORT +74 6.15 +%define DOS_QUIT +12 6.16 +%define UNDO_FLAG +10 6.17 +%define EXPLOSION +4 6.18 +;%define LAZY_CHECK -4 cylinder board 6.19 +;%define USE_RDTSC -15 need a 586+ 6.20 +%else 6.21 +%define USE_RDTSC -15 need a 586+ 6.22 +%endif 6.23 6.24 cpu 8086 6.25 +org 0x7c00 6.26 6.27 ;; Constants 6.28 6.29 @@ -25,7 +33,11 @@ 6.30 ;; register, then memory access instructions will be relative to the VGA text 6.31 ;; buffer, allowing easier access. For example, trying to access the nth byte of 6.32 ;; memory will *actually* access the nth byte of the text buffer. 6.33 +%ifdef MDA_SUPPORT 6.34 +%assign TextBuf.Seg 0xb000 6.35 +%else 6.36 %assign TextBuf.Seg 0xb800 6.37 +%endif 6.38 6.39 ;; Dimensions of text buffer 6.40 %assign TextBuf.Width 40 6.41 @@ -35,21 +47,15 @@ 6.42 ;; Macro to get the index of a text buffer cell from coordinates 6.43 %define TextBuf.Index(y, x) ((y) * TextBuf.Width * 2 + (x) * 2) 6.44 6.45 -;; Length of Dirs array defined below 6.46 -%assign Dirs.Len 8 6.47 - 6.48 ;; Keyboard scan codes 6.49 ;; http://www.delorie.com/djgpp/doc/rbinter/it/06/0.html 6.50 -%assign Key.ScanCode.Space 0x39 6.51 -%assign Key.ScanCode.Up 0x48 6.52 -%assign Key.ScanCode.Down 0x50 6.53 -%assign Key.ScanCode.Left 0x4b 6.54 -%assign Key.ScanCode.Right 0x4d 6.55 -%assign Key.ScanCode.Enter 0x1c 6.56 - 6.57 -;; Keyboard ASCII codes 6.58 -%assign Key.Ascii.RestartGame 'r' 6.59 -%assign Key.Ascii.QuitGame 27 6.60 +%assign Key.ScanCode.Space 0x39 6.61 +%assign Key.ScanCode.Up 0x48 6.62 +%assign Key.ScanCode.Down 0x50 6.63 +%assign Key.ScanCode.Left 0x4b 6.64 +%assign Key.ScanCode.Right 0x4d 6.65 +%assign Key.ScanCode.Enter 0x1c 6.66 +%assign Key.ScanCode.QuitGame 0x01 6.67 6.68 ;; This is a convenience macro for creating VGA characters. VGA characters are 6.69 ;; 16 bit words, with the lower byte as the ASCII value and the upper byte 6.70 @@ -58,32 +64,40 @@ 6.71 6.72 ;; VGA colors to use for game items 6.73 ;; https://wiki.osdev.org/Text_UI#Colours 6.74 -%assign Color.Veiled 0x77 6.75 -%assign Color.Unveiled 0xf0 6.76 -%assign Color.Cursor 0x00 6.77 -%assign Color.Flag 0xcc 6.78 -%assign Color.GameWinText 0x20 6.79 -%assign Color.GameOverText 0xc0 6.80 +%assign Color.Veiled 0x77 6.81 +%assign Color.Unveiled 0xf0 6.82 +%assign Color.Cursor 0x00 6.83 +%assign Color.Flag 0xcc 6.84 +%assign Color.GameWinText 0x20 6.85 +%assign Color.GameOverText 0xc0 6.86 + 6.87 +%ifdef MDA_SUPPORT 6.88 +%assign Mda.InverseAttr 0x70 ; 0x[7F][08] Bit 3: High intensity. 6.89 +%assign Mda.CursorAttr 0x09 ; 0x?[19] Bits 0-2: 1 => underline, other values => no underline. 6.90 +%assign Mda.HiddenAttr 0x00 ; 0x[08][08] Bit 7: Blink. 6.91 +%assign Mda.Veiled 0xdb ; 0xb1 6.92 +%assign Mda.Flag 0x02 6.93 +%assign Mda.Cursor 0xb1 ; 0x16 6.94 +%assign Mda.Screen.Offset 0x8000 6.95 +%assign Mda.Buffer 0x8000 6.96 +%define Mda.Attr(cga, mda) (((cga) << 8) | (mda)) 6.97 +%endif 6.98 6.99 ;; This value is used to calculate bomb frequency. The probability that any 6.100 ;; given cell is a bomb is (1/2)^n, where n = "number of ones in the binary 6.101 ;; representation of BombFreq". 6.102 ;; 6.103 -;; In other words, when BombFreq=0, every cell is a bomb, and appending a one 6.104 +;; In other words, when BombFreq=-1, every cell is a bomb, and appending a one 6.105 ;; halves the amount of bombs. 6.106 -%assign BombFreq 0b111 6.107 - 6.108 -%ifdef FAT_BOOT 6.109 - jmp BootMine 6.110 - nop 6.111 - times 0x3B db 0 6.112 -%endif 6.113 +%assign BombFreq 0b111 6.114 +%assign Ascii.Bomb '*' 6.115 6.116 ;; BootMine is supported as both a DOS game and a boot sector game :) 6.117 6.118 ;; Entry point: set up graphics and run game 6.119 BootMine: 6.120 cld 6.121 + sti ;allow interrupts 6.122 6.123 ; VGA text mode 0x00 6.124 ; 320x200 pixel resolution 6.125 @@ -127,11 +141,6 @@ 6.126 out dx, al 6.127 %endif 6.128 6.129 - ; Load VGA text buffer segment into segment registers 6.130 - mov dx, TextBuf.Seg 6.131 - mov es, dx 6.132 - mov ds, dx 6.133 - 6.134 ; Disable VGA text mode cursor 6.135 ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor 6.136 mov ah, 0x01 6.137 @@ -140,25 +149,47 @@ 6.138 6.139 ;; Run game (the game is restarted by jumping here) 6.140 RunGame: 6.141 + push cs 6.142 + pop ss 6.143 + mov sp, TextBuf.Seg 6.144 6.145 -;; Set all cells of game map to veiled '0' cells 6.146 -ZeroTextBuf: 6.147 - xor di, di 6.148 - mov cx, TextBuf.Size 6.149 - mov ax, VgaChar(Color.Veiled, '0') 6.150 - rep stosw 6.151 + ; Load VGA text buffer segment into segment registers 6.152 + mov es, sp 6.153 + mov ds, sp 6.154 6.155 %ifndef USE_RDTSC 6.156 ; Initialyze the simple pseudo-random number generator 6.157 ; seed = set_system_time() 6.158 - %if 1 6.159 - cbw 6.160 + mov ah, 0 6.161 int 0x1a 6.162 - push dx 6.163 +%endif 6.164 + 6.165 +;; Set all cells of game map to veiled '0' cells 6.166 +ZeroTextBuf: 6.167 +%ifdef MDA_SUPPORT 6.168 + %ifdef UNVEIL_ON_GAME_OVER 6.169 + call FillScreen 6.170 %else 6.171 - in al,(0x40) ; Read timer 6.172 - push ax 6.173 + mov ax, VgaChar(Color.Veiled, '0') 6.174 + mov di, Mda.Screen.Offset 6.175 + mov bx, TextBuf.Width - Mda.Screen.Offset 6.176 + mov bp, TextBuf.Height 6.177 +.1: 6.178 + mov cx, TextBuf.Width 6.179 +.2: 6.180 + mov [cs:di + Mda.Buffer - Mda.Screen.Offset], ax 6.181 + mov byte [bx + di], Mda.Veiled 6.182 + stosw 6.183 + loop .2 6.184 + add bx, TextBuf.Width * 2 6.185 + dec bp 6.186 + jnz .1 6.187 %endif 6.188 +%else 6.189 + mov ax, VgaChar(Color.Veiled, '0') 6.190 + mov cx, TextBuf.Size 6.191 + xor di, di 6.192 + rep stosw 6.193 %endif 6.194 6.195 ;; Populate text buffer with mines and digits 6.196 @@ -172,150 +203,98 @@ 6.197 ;; Note that the coordinates on the outside border are skipped to avoid bounds 6.198 ;; checking logic. 6.199 PopulateTextBuf: 6.200 - ; Iterate over y coordinates 6.201 - mov bx, TextBuf.Height - 2 6.202 + ; Iterate over y coordinates. ch = 0 form ZeroTextBuf 6.203 + mov cl, TextBuf.Height - 2 6.204 6.205 .LoopY: 6.206 - ; Iterate over x coordinates. ch = 0 form ZeroTextBuf 6.207 -%ifndef USE_RDTSC 6.208 - mov cx, TextBuf.Width - 2 6.209 -%else 6.210 - mov cl, TextBuf.Width - 2 6.211 -%endif 6.212 + mov di, TextBuf.Width - 2 6.213 6.214 .LoopX: 6.215 - ; di = &TextBuf[y][x] 6.216 - call GetTextBufIndex 6.217 -.si_value: 6.218 6.219 - ; The register dl holds a boolean that is 1 if the current cell is a bomb, 0 6.220 + ; The register al holds a boolean that is 1 if the current cell is a bomb, 0 6.221 ; otherwise. 6.222 %ifndef USE_RDTSC 6.223 ; It is calculated by bitwise and-ing the result of the simple pseudo-random number 6.224 ; generator seed = ((seed + LARGE_PRIME1) * LARGE_PRIME2) % LARGE_PRIME3 6.225 ; 6.226 - ; dl = ! (prng() & BombFreq) 6.227 + ; al = ! (prng() & BombFreq) 6.228 %assign Prime1 32749 6.229 %assign Prime2 65519 6.230 %assign Prime3 65521 6.231 - pop ax 6.232 + xchg ax, dx 6.233 add ax, Prime1 6.234 - mov bp, Prime2 6.235 - mul bp 6.236 - inc bp 6.237 - inc bp 6.238 - div bp 6.239 - xchg ax, dx 6.240 - push ax 6.241 + mov si, Prime2 6.242 + mul si 6.243 + inc si 6.244 + inc si 6.245 + div si 6.246 + test dl, cl 6.247 %else 6.248 ; It is calculated by bitwise and-ing the result of rdtsc. (rdtsc returns the 6.249 ; amount of CPU cycles since boot, which works okay as a cheap random number 6.250 ; generator, and it's apparently supported on all x86 CPUs since the Pentium line) 6.251 ; 6.252 - ; dl = ! (rdtsc() & BombFreq) 6.253 + ; al = ! (rdtsc() & BombFreq) 6.254 cpu 686 6.255 rdtsc 6.256 cpu 8086 6.257 + test al, BombFreq 6.258 %endif 6.259 - and al, BombFreq 6.260 - mov dx, '*' * 256 + 0 6.261 6.262 - ; Initialize loop counter for .LoopDir 6.263 - mov bp, Dirs.Len 6.264 + mov ax, VgaChar(Color.Veiled, Ascii.Bomb) 6.265 + 6.266 + ; If this cell isn't a bomb, then skip marking it as a bomb 6.267 + jnz .Iterated 6.268 6.269 - ; If this cell isn't a bomb, then skip marking it as a bomb 6.270 - jnz .LoopDir 6.271 + ; si = &TextBuf[y][x] 6.272 + call GetTextBufIndex 6.273 6.274 ; Mark the current cell as a bomb 6.275 - mov byte [di], dh 6.276 - inc dx 6.277 - 6.278 ; Iterate over adjacent cells (directions) 6.279 -.LoopDir: 6.280 - ; Load adjacent cell offset from Dirs array into ax. 6.281 - mov al, byte [cs:bp + si + Dirs - .si_value - 1] 6.282 - cbw 6.283 - ; Set di = pointer to adjacent cell 6.284 - add di, ax 6.285 - 6.286 - ; If adjacent cell is a bomb, skip digit incrementing 6.287 - cmp byte [di], dh 6.288 - je .LoopDirIsMine 6.289 - ; The adjacent cell is a 0-7 digit and not a bomb. Add dl to the cell, which 6.290 - ; is 1 if the original cell is a bomb. This gradually accumulates to the 6.291 - ; amount of neighboring bombs and represents the number cells in the 6.292 - ; minesweeper game. 6.293 - add [di], dl 6.294 -.LoopDirIsMine: 6.295 - ; Restore di to original cell pointer 6.296 - sub di, ax 6.297 - 6.298 - ; Decrement adjacent direction loop counter and continue if nonzero 6.299 - dec bp 6.300 - jnz .LoopDir 6.301 + call AdjacentCells 6.302 +.Iterated: 6.303 6.304 ; Decrement x coordinate loop counter and continue if nonzero 6.305 - loop .LoopX 6.306 + dec di 6.307 + jnz .LoopX 6.308 6.309 ; Decrement y coordinate loop counter and continue if nonzero 6.310 - dec bx 6.311 - jnz .LoopY 6.312 -%ifndef USE_RDTSC 6.313 - pop ax 6.314 -%endif 6.315 + loop .LoopY 6.316 + ; cl and di are zeroed from the PopulateTextBuf loops above 6.317 6.318 ;; Done populating the text buffer 6.319 6.320 - ; Set the initial cursor color for game loop. The dl register is now used to 6.321 + ; Set the initial cursor color for game loop. The dh register is now used to 6.322 ; store the saved cell color that the cursor is on, since the cursor 6.323 ; overwrites the cell color with the cursor color. 6.324 - mov dl, Color.Veiled 6.325 + xchg ax, dx 6.326 6.327 ;; Main loop to process key presses and update state 6.328 GameLoop: 6.329 - ; Get keystroke 6.330 - ; ah = BIOS scan code 6.331 - ; al = ASCII character 6.332 - ; http://www.delorie.com/djgpp/doc/rbinter/id/63/17.html 6.333 - xor ax, ax 6.334 - int 0x16 6.335 -%ifdef DOS_QUIT 6.336 - cmp al, Key.Ascii.QuitGame 6.337 - je Quit 6.338 -%endif 6.339 - 6.340 - ; bx and cx are zeroed from the PopulateTextBuf loops above 6.341 - ; bx = y coord 6.342 - ; cx = x coord 6.343 - 6.344 - ; di = cell pointer 6.345 - call GetTextBufIndex 6.346 - ; Apply saved cell color 6.347 - mov [di + 1], dl 6.348 6.349 ;; Detect win (a win occurs when every veiled cell is a mine) 6.350 DetectWin: 6.351 - ; Use si register as cell pointer for win detection 6.352 - xor si, si 6.353 - ; Use bp as loop counter 6.354 - mov bp, TextBuf.Size 6.355 -.Loop: 6.356 - ; if (char != '*' && (color == Color.Veiled || color == Color.Flag)) { 6.357 + mov bx, TextBuf.Size 6.358 + ; if (char != Ascii.Bomb && (color == Color.Veiled || color == Color.Flag)) { 6.359 ; break; // Didn't win yet :( 6.360 ; } 6.361 - ; Load VGA char into al 6.362 - lodsb 6.363 - cmp al, '*' 6.364 - ; Load VGA color into al 6.365 - lodsb 6.366 + ; Use si register as cell pointer for win detection 6.367 +%ifdef MDA_SUPPORT 6.368 + mov si, Mda.Buffer 6.369 +.Loop: 6.370 + cs lodsw 6.371 +%else 6.372 + xor si, si 6.373 +.Loop: 6.374 + lodsw 6.375 +%endif 6.376 + cmp al, Ascii.Bomb 6.377 je .Continue 6.378 - cmp al, Color.Veiled 6.379 - je Break 6.380 - cmp al, Color.Flag 6.381 - je Break 6.382 + cmp ah, Color.Unveiled 6.383 + jb .Break 6.384 .Continue: 6.385 - dec bp 6.386 - jnz .Loop 6.387 + dec bx 6.388 + jne .Loop 6.389 ; If loop completes without breaking, then we win! :) 6.390 6.391 ;; Show game win screen 6.392 @@ -324,188 +303,247 @@ 6.393 call GameEndHelper 6.394 db 'GAME WIN' 6.395 6.396 -;; Wait for restart key to be pressed, then restart game 6.397 +.Break: 6.398 WaitRestart: 6.399 - xor ax, ax 6.400 +;; if bx == 0: Wait for restart key to be pressed, then restart game 6.401 + ; Get keystroke 6.402 + ; ah = BIOS scan code 6.403 + ; al = ASCII character 6.404 + ; http://www.delorie.com/djgpp/doc/rbinter/id/63/17.html 6.405 + mov ah, 0 6.406 int 0x16 6.407 %ifdef DOS_QUIT 6.408 - cmp al, Key.Ascii.QuitGame 6.409 - jnz Quit.notQuit 6.410 -Quit: 6.411 - mov ax,0x0003 ; Restore text mode 6.412 - int 0x10 6.413 - int 0x20 6.414 - int 0x19 6.415 -.notQuit: 6.416 + dec ah 6.417 + je Quit 6.418 %endif 6.419 -%ifndef RESTART_ON_ANY_KEY 6.420 - cmp al, Key.Ascii.RestartGame 6.421 - jne WaitRestart 6.422 + dec bx 6.423 + js RunGame 6.424 + 6.425 + ; cl = y coord 6.426 + ; di = x coord 6.427 + 6.428 + ; si = cell pointer 6.429 + call GetTextBufIndex 6.430 + ; Apply saved cell color 6.431 +%ifdef MDA_SUPPORT 6.432 + call SetColor 6.433 +%else 6.434 + mov byte [si + 1], dh 6.435 %endif 6.436 - jmp RunGame 6.437 6.438 -;; Array of adjacent cell offsets. A byte in this array can be added to a text 6.439 -;; buffer cell pointer to get the pointer to an adjacent cell. This is used for 6.440 -;; spawning digit cells. 6.441 -Dirs: 6.442 - db TextBuf.Index(-1, -1) 6.443 - db TextBuf.Index(-1, 0) 6.444 - db TextBuf.Index(-1, +1) 6.445 - db TextBuf.Index( 0, +1) 6.446 - db TextBuf.Index(+1, +1) 6.447 - db TextBuf.Index(+1, 0) 6.448 - db TextBuf.Index(+1, -1) 6.449 - db TextBuf.Index( 0, -1) 6.450 - 6.451 -Break: 6.452 ; Didn't win yet 6.453 - mov al, ah 6.454 + xchg al, ah 6.455 6.456 ;; Process key press. This is an if-else chain that runs code depending on the 6.457 ;; key pressed. 6.458 CmpUp: 6.459 ; Move cursor up 6.460 - dec bx 6.461 - cmp al, Key.ScanCode.Up 6.462 + dec cx 6.463 +%ifdef DOS_QUIT 6.464 + sub al, Key.ScanCode.Up-1 6.465 +%else 6.466 + sub al, Key.ScanCode.Up 6.467 +%endif 6.468 je WrapCursor 6.469 - inc bx 6.470 + inc cx 6.471 CmpDown: 6.472 ; Move cursor down 6.473 - inc bx 6.474 - cmp al, Key.ScanCode.Down 6.475 + inc cx 6.476 + sub al, Key.ScanCode.Down - Key.ScanCode.Up 6.477 je WrapCursor 6.478 - dec bx 6.479 + dec cx 6.480 CmpLeft: 6.481 ; Move cursor left 6.482 - dec cx 6.483 - cmp al, Key.ScanCode.Left 6.484 + dec di 6.485 + sub al, Key.ScanCode.Left - Key.ScanCode.Down 6.486 je WrapCursor 6.487 - inc cx 6.488 + inc di 6.489 CmpRight: 6.490 ; Move cursor right 6.491 - inc cx 6.492 - cmp al, Key.ScanCode.Right 6.493 + inc di 6.494 + sub al, Key.ScanCode.Right - Key.ScanCode.Left 6.495 je WrapCursor 6.496 - dec cx 6.497 + dec di 6.498 CmpEnter: 6.499 - cmp al, Key.ScanCode.Enter 6.500 + sub al, Key.ScanCode.Enter - Key.ScanCode.Right 6.501 jne CmpSpace 6.502 ; Place flag by coloring current cell 6.503 - mov dl, Color.Flag 6.504 - mov [di + 1], dl 6.505 +%ifdef MDA_SUPPORT 6.506 + %ifdef UNDO_FLAG 6.507 + cmp byte [bp + si + Mda.Buffer - Mda.Screen.Offset + 1], Color.Unveiled 6.508 + jae GameLoop 6.509 + xor byte [bp + si + Mda.Buffer - Mda.Screen.Offset + 1], Color.Flag ^ Color.Veiled 6.510 + mov dx, [bp + si + Mda.Buffer - Mda.Screen.Offset] 6.511 + %else 6.512 + mov dh, Color.Flag 6.513 + %endif 6.514 + call SetColor 6.515 +%else 6.516 + %ifdef UNDO_FLAG 6.517 + inc si 6.518 + cmp byte [si], Color.Unveiled 6.519 + jae GameLoop 6.520 + xor byte [si], Color.Flag ^ Color.Veiled 6.521 + mov dx, [si] 6.522 + %else 6.523 + mov dh, Color.Flag 6.524 + mov [si + 1], dh 6.525 + %endif 6.526 +%endif 6.527 ; jmp GameLoop 6.528 CmpSpace: 6.529 - cmp al, Key.ScanCode.Space 6.530 + sub al, Key.ScanCode.Space - Key.ScanCode.Enter 6.531 jne GameLoop 6.532 6.533 ;; If the player pressed space, clear the current cell 6.534 ClearCell: 6.535 - ; Set ax = cell value 6.536 - mov ax, [di] 6.537 - call UnveilCell 6.538 -;; If-else chain checking the cell value 6.539 -.CmpEmpty: 6.540 - cmp al, '0' 6.541 - jne .CmpMine 6.542 +%ifdef EXPLOSION 6.543 + %ifdef MDA_SUPPORT 6.544 + mov al, [bp + si + Mda.Buffer - Mda.Screen.Offset] 6.545 + %else 6.546 + mov al, [si] 6.547 + %endif 6.548 + cmp al, Ascii.Bomb 6.549 +%else 6.550 + %ifdef MDA_SUPPORT 6.551 + cmp byte [bp + si + Mda.Buffer - Mda.Screen.Offset], Ascii.Bomb 6.552 + %else 6.553 + cmp byte [si], Ascii.Bomb 6.554 + %endif 6.555 +%endif 6.556 + je GameOver 6.557 + 6.558 ; If cell is empty, run flood fill algorithm 6.559 call Flood 6.560 -.jmpGameLoop: 6.561 +%ifdef MDA_SUPPORT 6.562 +GetCharAndColor: 6.563 + mov dx, [bp + si + Mda.Buffer - Mda.Screen.Offset] 6.564 +%endif 6.565 +jmpGameLoop: 6.566 jmp GameLoop 6.567 -.CmpMine: 6.568 - cmp al, '*' 6.569 - ; No handling needed if cell is digit 6.570 - jne .jmpGameLoop 6.571 + 6.572 +;; Helper code for GameWin and GameOver; print a string in the center of the 6.573 +;; text buffer, then wait for game to be restarted. 6.574 +GameEndHelper: 6.575 + pop si 6.576 +%ifdef MDA_SUPPORT 6.577 + mov di, TextBuf.Index(TextBuf.Height / 2, TextBuf.Width / 2 - (GameOverStr.End - GameOverStr) / 2) + Mda.Screen.Offset 6.578 +%else 6.579 + mov di, TextBuf.Index(TextBuf.Height / 2, TextBuf.Width / 2 - (GameOverStr.End - GameOverStr) / 2) 6.580 +%endif 6.581 + xor bx, bx 6.582 +.String: 6.583 + cs lodsb 6.584 + cmp al, 'Z' 6.585 + ja WaitRestart 6.586 +%ifdef MDA_SUPPORT 6.587 + mov [di + TextBuf.Index(TextBuf.Height / 2, TextBuf.Width / 2) - Mda.Screen.Offset], al 6.588 +%endif 6.589 + stosw 6.590 + jmp .String 6.591 + 6.592 +%ifdef DOS_QUIT 6.593 +Quit: 6.594 + mov al,0x03 ; Restore text mode 6.595 + int 0x10 6.596 + int 0x20 6.597 + int 0x19 6.598 +%endif 6.599 + 6.600 +GameOver: 6.601 ; If cell is bomb, game over :( 6.602 +%ifdef EXPLOSION 6.603 + call UnveilCell 6.604 +%endif 6.605 6.606 ;; Show game over screen 6.607 6.608 %ifdef UNVEIL_ON_GAME_OVER 6.609 + %ifdef MDA_SUPPORT 6.610 + call UnveilBomb 6.611 + %else 6.612 mov cx, TextBuf.Size 6.613 xor si, si 6.614 .Loop: 6.615 ; Load VGA character into ax 6.616 lodsw 6.617 - cmp al, '*' 6.618 + cmp al, Ascii.Bomb 6.619 jne .Next 6.620 and byte [si-1], Color.Unveiled 6.621 .Next: 6.622 loop .Loop 6.623 + %endif 6.624 %endif 6.625 -;;GameOver: 6.626 mov ah, Color.GameOverText 6.627 call GameEndHelper 6.628 GameOverStr: 6.629 db 'GAME OVER' 6.630 -%assign GameOverStr.Len $ - GameOverStr 6.631 - 6.632 -;; Helper code for GameWin and GameOver; print a string in the center of the 6.633 -;; text buffer, then wait for game to be restarted. 6.634 -GameEndHelper: 6.635 - pop si 6.636 - mov di, TextBuf.Index(TextBuf.Height / 2, TextBuf.Width / 2 - GameOverStr.Len / 2) 6.637 -.Loop: 6.638 - cs lodsb 6.639 - cmp al, 'Z' 6.640 - ja WaitRestart 6.641 - stosw 6.642 - jmp .Loop 6.643 +GameOverStr.End: 6.644 6.645 ;; Set y and x coordinates of cursor to zero if they are out of bounds 6.646 +ResetCursor: 6.647 + xchg ax, cx 6.648 + xor di, di 6.649 + 6.650 WrapCursor: 6.651 -.Y: 6.652 - ; Wrap y cursor 6.653 - cmp bx, TextBuf.Height 6.654 - jb .X 6.655 - xor bx, bx 6.656 - 6.657 -.X: 6.658 - ; Wrap x cursor 6.659 - cmp cx, TextBuf.Width 6.660 - jb SetCursorPos 6.661 - xor cx, cx 6.662 - 6.663 -;; Redraw cursor in new position 6.664 -SetCursorPos: 6.665 ; Get text buffer index (it changed) 6.666 call GetTextBufIndex 6.667 + jae ResetCursor 6.668 + 6.669 ; Draw cursor by changing cell to the cursor color, but save current color for 6.670 ; restoring in the next iteration of the game loop. 6.671 - mov dl, Color.Cursor 6.672 - xchg dl, [di + 1] 6.673 +%ifdef MDA_SUPPORT 6.674 + mov dx, Mda.Attr(Color.Cursor, Mda.Cursor) 6.675 + call SetScreenColor 6.676 + jmp GetCharAndColor 6.677 +%else 6.678 + mov dh, Color.Cursor 6.679 + xchg byte [si + 1], dh 6.680 + jmp jmpGameLoop 6.681 +%endif 6.682 6.683 - jmp ClearCell.jmpGameLoop 6.684 - 6.685 -;; Compute the text buffer index from y and x coordinates 6.686 -;; 6.687 -;; di = &TextBuf[bx = y][cx = x] 6.688 -;; 6.689 -;; This computes the equivalent of the TextBuf.Index(y, x) macro, but at runtime 6.690 -;; 6.691 -;; Parameters: 6.692 -;; * bx - y coordinate 6.693 -;; * cx - x coordinate 6.694 -;; Returns: 6.695 -;; * di - text buffer index 6.696 -;; * si - caller return address 6.697 -GetTextBufIndex: 6.698 - xchg ax, di 6.699 - mov al, TextBuf.Width * 2 6.700 - imul bl 6.701 - xchg ax, di 6.702 - add di, cx 6.703 - add di, cx 6.704 - ; load caller return address in si 6.705 - pop si 6.706 - push si 6.707 +%ifdef UNVEIL_ON_GAME_OVER 6.708 + %ifdef MDA_SUPPORT 6.709 +FillScreen: 6.710 + mov cx, Mda.Veiled 6.711 +UnveilBomb: 6.712 + mov si, Mda.Buffer 6.713 + mov bp, TextBuf.Height 6.714 + mov bx, 0 - TextBuf.Width + Mda.Buffer 6.715 +.LoopLine: 6.716 + mov di, TextBuf.Width 6.717 + add bx, 2*TextBuf.Width 6.718 +.Loop: 6.719 + mov ax, VgaChar(Color.Veiled, '0') 6.720 + or cl, cl 6.721 + js .Fill 6.722 + ; Load VGA character into ax 6.723 + mov ax,[cs:si] 6.724 + cmp al, Ascii.Bomb 6.725 + jne .Next 6.726 + and ah, Color.Unveiled 6.727 + mov cl, al 6.728 +.Fill: 6.729 + mov [si], ax 6.730 + mov [cs:si], ax 6.731 + mov [bx + si + Mda.Screen.Offset - Mda.Buffer], cl 6.732 +.Next: 6.733 + lodsw 6.734 + dec di 6.735 + jnz .Loop 6.736 + dec bp 6.737 + jnz .LoopLine 6.738 ret 6.739 + %endif 6.740 +%endif 6.741 6.742 ;; Unveil a cell so it is visible on the screen 6.743 ;; 6.744 ;; Parameters: 6.745 -;; * di - cell pointer in text buffer 6.746 +;; * si - cell pointer in text buffer 6.747 ;; * al - cell ASCII value 6.748 ;; Returns: 6.749 -;; * dl - written VGA color code 6.750 +;; * dh - written VGA color code 6.751 UnveilCell: 6.752 ; TLDR: Use xor magic to make the cells colored. 6.753 ; 6.754 @@ -529,113 +567,159 @@ 6.755 ; Case 2: the cell is a bomb 6.756 ; 6.757 ; We don't really care about this case as long as the bomb is visible against 6.758 - ; the background. The bomb turns out to be green, oh well. 6.759 - ; 6.760 - ; Case 3: the cell is an empty space 6.761 - ; 6.762 - ; This ends up coloring the cell bright yellow, which isn't a big problem. 6.763 + ; the background. The bomb turns out to be bright yellow, oh well. 6.764 +%ifdef MDA_SUPPORT 6.765 mov dl, al 6.766 - xor dl, '0' ^ Color.Unveiled 6.767 - mov [di + 1], dl 6.768 + mov [si], al 6.769 +%endif 6.770 + xor al, '0' ^ Color.Unveiled 6.771 + mov dh, al 6.772 +%ifdef MDA_SUPPORT 6.773 +SetColor: 6.774 + mov [bp + si + Mda.Buffer - Mda.Screen.Offset + 1], dh 6.775 + cmp dh, Color.Flag 6.776 + ja .NotVeiled 6.777 + mov dl, Mda.Veiled 6.778 + jne .NotVeiled 6.779 + mov dl, Mda.Flag 6.780 +.NotVeiled: 6.781 +SetScreenColor: 6.782 + mov [bx + si], dl 6.783 +%endif 6.784 + mov [si + 1], dh 6.785 ret 6.786 6.787 ;; Flood fill empty cells 6.788 ;; 6.789 ;; Parameters: 6.790 -;; * bx - cell y coordinate 6.791 -;; * cx - cell x coordinate 6.792 -;; Clobbered registers: 6.793 -;; * ax - cell value 6.794 -;; * di - cell pointer in text buffer 6.795 -Flood: 6.796 - ; Init: get cell pointer and value 6.797 - call GetTextBufIndex 6.798 - mov ax, [di] 6.799 - 6.800 - ; Base case: bounds check y 6.801 - cmp bx, TextBuf.Height 6.802 - jae .Ret 6.803 - 6.804 - ; Base case: bounds check x 6.805 - cmp cx, TextBuf.Width 6.806 - jae .Ret 6.807 - 6.808 +;; * cl - cell y coordinate 6.809 +;; * di - cell x coordinate 6.810 +;; * si - cell pointer in text buffer 6.811 +FloodStep: 6.812 +%ifdef MDA_SUPPORT 6.813 + mov al, [bp + si + Mda.Buffer - Mda.Screen.Offset] 6.814 +%else 6.815 + mov al, [si] 6.816 +%endif 6.817 cmp al, '0' 6.818 6.819 + ; Base case: nonempty cell unveiled and stop recursion 6.820 + ja UnveilCell 6.821 + 6.822 + mov ax, VgaChar(Color.Unveiled, ' ') 6.823 + 6.824 ; Base case: we visited this cell already or bomb 6.825 - jb .Ret 6.826 - 6.827 - ; Base case: nonempty cell unveiled and stop recursion 6.828 - jne UnveilCell 6.829 - 6.830 - ; Body: unveil empty cell 6.831 - call UnveilCell 6.832 + jb Ret 6.833 6.834 ; Body: mark cell as visited and empty 6.835 - mov byte [di], ' ' 6.836 +%ifdef MDA_SUPPORT 6.837 + mov [si], ax 6.838 + mov [bx + si], al 6.839 +AdjacentCells: 6.840 + mov [bp + si + Mda.Buffer - Mda.Screen.Offset], ax 6.841 +%else 6.842 +AdjacentCells: 6.843 + mov [si], ax 6.844 +%endif 6.845 6.846 ; Recursive case: flood adjacent cells 6.847 6.848 - ; Flood down 6.849 - inc bx 6.850 - call Flood 6.851 - dec bx 6.852 + ; Flood left-row 6.853 + dec di 6.854 + call UpAndFloodRow 6.855 6.856 - ; Flood left 6.857 + ; Flood center-row 6.858 + call FloodNextRow 6.859 + 6.860 + ; Flood right-row 6.861 + call FloodNextRow 6.862 + 6.863 + ; re-center 6.864 dec cx 6.865 - call Flood 6.866 - inc cx 6.867 + dec di 6.868 6.869 - ; Flood right 6.870 - inc cx 6.871 - call Flood 6.872 - dec cx 6.873 - 6.874 - ; Flood up-left 6.875 - dec cx 6.876 - call .Flood_up 6.877 - inc cx 6.878 - 6.879 - ; Flood up-right 6.880 - inc cx 6.881 - call .Flood_up 6.882 - dec cx 6.883 - 6.884 - ; Flood down-left 6.885 - inc bx 6.886 - dec cx 6.887 - call Flood 6.888 - inc cx 6.889 - dec bx 6.890 - 6.891 - ; Flood down-right 6.892 - inc bx 6.893 - inc cx 6.894 - call Flood 6.895 - dec cx 6.896 - dec bx 6.897 - 6.898 -.Flood_up: 6.899 - ; Flood up 6.900 - dec bx 6.901 - call Flood 6.902 - inc bx 6.903 - 6.904 -.Ret: 6.905 +;; Compute the text buffer index from y and x coordinates 6.906 +;; 6.907 +;; si = &TextBuf[cl = y][di = x] 6.908 +;; 6.909 +;; This computes the equivalent of the TextBuf.Index(y, x) macro, but at runtime 6.910 +;; 6.911 +;; Parameters: 6.912 +;; * cl - y coordinate 6.913 +;; * di - x coordinate 6.914 +;; Returns: 6.915 +;; * si - text buffer index 6.916 +;; * carry - x and y are valid coordinates 6.917 +GetTextBufIndex: 6.918 + xchg ax, si 6.919 + mov al, TextBuf.Width * 2 6.920 + imul cl 6.921 + xchg ax, si 6.922 +%ifdef MDA_SUPPORT 6.923 + lea bx, [si + TextBuf.Width - Mda.Screen.Offset] 6.924 + lea si, [bx + di - TextBuf.Width] 6.925 + add si, di 6.926 + %ifdef LAZY_CHECK 6.927 + cmp si, TextBuf.Size * 2 + Mda.Screen.Offset 6.928 + %endif 6.929 +%else 6.930 + add si, di 6.931 + add si, di 6.932 + %ifdef LAZY_CHECK 6.933 + cmp si, TextBuf.Size * 2 6.934 + %endif 6.935 +%endif 6.936 +%ifndef LAZY_CHECK 6.937 + ; Base case: bounds check y 6.938 + cmp cl, TextBuf.Height 6.939 + jae Ret 6.940 + ; Base case: bounds check x 6.941 + cmp di, TextBuf.Width 6.942 +%endif 6.943 +Ret: 6.944 ret 6.945 6.946 +FloodNextRow: 6.947 + inc di 6.948 + dec cx 6.949 +UpAndFloodRow: 6.950 + dec cx 6.951 +FloodRow: 6.952 + call Flood 6.953 + call FloodDown 6.954 +FloodDown: 6.955 + inc cx 6.956 +Flood: 6.957 + ; si = &TextBuf[y][x] 6.958 + call GetTextBufIndex 6.959 + jae Ret 6.960 + 6.961 + cmp al, Ascii.Bomb 6.962 + jne FloodStep 6.963 +Counter: 6.964 + ; If adjacent cell is a bomb, skip digit incrementing 6.965 +%ifdef MDA_SUPPORT 6.966 + cmp [bp + si + Mda.Buffer - Mda.Screen.Offset], al 6.967 +%else 6.968 + cmp [si], al 6.969 +%endif 6.970 + je .IsMine 6.971 + ; The adjacent cell is a 0-7 digit and not a bomb. Add 1 to the cell. 6.972 + ; This gradually accumulates to the 6.973 + ; amount of neighboring bombs and represents the number cells in the 6.974 + ; minesweeper game. 6.975 +%ifdef MDA_SUPPORT 6.976 + inc byte [bp + si + Mda.Buffer - Mda.Screen.Offset] 6.977 +%else 6.978 + inc byte [si] 6.979 +%endif 6.980 +.IsMine: 6.981 + ret 6.982 6.983 ;; Print program size at build time 6.984 %assign CodeSize $ - $$ 6.985 -%warning Code is CodeSize bytes 6.986 - 6.987 -%ifdef MBR_BOOT 6.988 -%assign PartitionTable 0x1BE 6.989 -%if CodeSize > PartitionTable 6.990 -%assign OverFlow CodeSize - PartitionTable 6.991 -%error Code is OverFlow bytes too large 6.992 -%endif 6.993 -%endif 6.994 +%assign Size $ - BootMine 6.995 +%warning Code is Size bytes 6.996 6.997 CodeEnd: 6.998 ; Pad to size of boot sector, minus the size of a word for the boot sector
7.1 --- a/bootris/stuff/tetranglix.asm Wed Oct 04 13:13:17 2023 +0000 7.2 +++ b/bootris/stuff/tetranglix.asm Sun Feb 04 18:02:38 2024 +0000 7.3 @@ -1,6 +1,8 @@ 7.4 ; Modified by nanochess for compatibility with VirtualBox. 7.5 ; to require only 8086 and also now it's in color. 7.6 7.7 +%define MDA_SUPPORT 7.8 + 7.9 BITS 16 7.10 7.11 BSS EQU 0x7E00 7.12 @@ -57,11 +59,20 @@ 7.13 mov ax, 0x103 ; Some BIOS crash without the 03. 7.14 int 0x10 7.15 7.16 - mov es, sp 7.17 - 7.18 ; White spaces on black background. 7.19 xor di, di 7.20 +%ifdef MDA_SUPPORT 7.21 + mov ax, sp 7.22 +mda: 7.23 + mov es, ax 7.24 + mov ah, 0xb0 7.25 + inc word [es:di] 7.26 + jz mda 7.27 + mov ah, 0x0F 7.28 +%else 7.29 + mov es, sp 7.30 mov ax, 0x0F00 7.31 +%endif 7.32 mov ch, 8 ; At least 80x25x2. 7.33 rep stosw 7.34 call pop_check 7.35 @@ -72,7 +83,8 @@ 7.36 ; Carry set if colliding. 7.37 tetramino_collision_check: 7.38 7.39 - lea bx, [bp + check_collision - tetramino_collision_check] 7.40 +%define BP(x) [bp + x - tetramino_collision_check] 7.41 + lea bx, BP(check_collision) 7.42 7.43 ; Processes the current tetramino, calling bx per "tetramino pixel". 7.44 ; bx -> where to call to; al contains tetramino pixel, di the address into stack. 7.45 @@ -243,7 +255,7 @@ 7.46 mov ah,al 7.47 7.48 ; Load tetramino bitmap in dl. 7.49 - mov dl, [bp + di + (tetraminos - tetramino_collision_check) - 1] 7.50 + mov dl, BP(di + tetraminos - 1) 7.51 mov cl, 4 7.52 shl dx, cl 7.53 7.54 @@ -282,32 +294,31 @@ 7.55 xor ah, ah 7.56 int 0x16 7.57 7.58 - .exit: 7.59 + mov al, ah 7.60 dec ah 7.61 je exit_dos 7.62 - mov al, ah 7.63 7.64 ; Go left. 7.65 .left: 7.66 - cmp al, LEFT_SCANCODE-1 7.67 + cmp al, LEFT_SCANCODE 7.68 je .call_bp 7.69 7.70 ; Go right. 7.71 .right: 7.72 - cmp al, RIGHT_SCANCODE-1 7.73 + cmp al, RIGHT_SCANCODE 7.74 jne .rotate 7.75 7.76 add byte [si],2 7.77 7.78 .call_bp: 7.79 dec byte [si] 7.80 - xor al, (LEFT_SCANCODE-1) ^ (RIGHT_SCANCODE-1) 7.81 + xor al, LEFT_SCANCODE ^ RIGHT_SCANCODE 7.82 call bp 7.83 jc .left 7.84 7.85 ; Rotate it. 7.86 .rotate: 7.87 - cmp al, UP_SCANCODE-1 7.88 + cmp al, UP_SCANCODE 7.89 jne .vertical_increment 7.90 7.91 inc cx 7.92 @@ -376,12 +387,7 @@ 7.93 7.94 ; Joins the current tetramino to the stack, and any complete lines together. 7.95 ; si -> OFFSET. 7.96 - push es 7.97 - 7.98 - push ds 7.99 - pop es 7.100 - 7.101 - lea bx, [bp + merge - tetramino_collision_check] 7.102 + lea bx, BP(merge) 7.103 call tetramino_process 7.104 7.105 mov si, STACK + 15 7.106 @@ -399,8 +405,19 @@ 7.107 jz .next_line 7.108 7.109 lea cx, [si - (STACK - 1)] 7.110 +%if 1 7.111 lea di, [si + 16] 7.112 + push es 7.113 + push ds 7.114 + pop es 7.115 rep movsb 7.116 + pop es 7.117 +%else 7.118 + .loop_movsb: 7.119 + lodsb 7.120 + mov [si + 16 - 1], al 7.121 + loop .loop_movsb 7.122 +%endif 7.123 mov cl, 64 7.124 call upd_score 7.125 7.126 @@ -411,7 +428,6 @@ 7.127 jb .loop_lines 7.128 7.129 cld 7.130 - pop es 7.131 7.132 jmp .borders 7.133 7.134 @@ -482,14 +498,13 @@ 7.135 add di, (80 - 8) * 2 7.136 7.137 .load_tetramino: 7.138 - lodsb 7.139 - or al,al 7.140 - mov ah,al 7.141 - mov al,0xdb 7.142 + mov ax, 0xdb 7.143 + or ah, [si] 7.144 ; Output two characters for "squarish" output. 7.145 jne .load_tetramino2 7.146 mov ax, [es:di] 7.147 .load_tetramino2: 7.148 + inc si 7.149 stosw 7.150 stosw 7.151 7.152 @@ -498,23 +513,19 @@ 7.153 jmp .event_loop 7.154 7.155 upd_score: 7.156 - push ds 7.157 - mov bx, SCREEN_SEGMENT 7.158 - mov ds, bx 7.159 - mov bx, SCORE_DIGITS * 2 7.160 + mov di, SCORE_DIGITS * 2 7.161 7.162 .chk_score: 7.163 - dec bx 7.164 - dec bx 7.165 + dec di 7.166 + dec di 7.167 js pop_check.game_over 7.168 mov al, '0' 7.169 - xchg [bx], al 7.170 + xchg [es:di], al 7.171 or al, 0x30 7.172 cmp al, '9' 7.173 je .chk_score 7.174 inc ax 7.175 - mov [bx], al 7.176 - pop ds 7.177 + stosb 7.178 7.179 loop upd_score 7.180 ret
8.1 --- a/bootsokoban/stuff/sokoban.asm Wed Oct 04 13:13:17 2023 +0000 8.2 +++ b/bootsokoban/stuff/sokoban.asm Sun Feb 04 18:02:38 2024 +0000 8.3 @@ -2,17 +2,340 @@ 8.4 cpu 8086 8.5 8.6 %define CURRENT_LEVEL 0x7E00 8.7 -%define CURRENT_LEVEL_4 0x7E04 8.8 +%define CURRENT_LEVEL_2 0x7E02 8.9 %define SCREEN_DS 0xb800 8.10 +%define HISTORY SCREEN_DS 8.11 8.12 -%define MOVE_COUNT 8.13 +%define MDA_SUPPORT +7 bytes 8.14 + %define MDA_CGA40 +25 bytes 8.15 +%define MOVE_COUNT +20 bytes 8.16 +%define UNDO +35 bytes 8.17 + %define FAST_UNDO +5 bytes 8.18 + 8.19 +%define COL80 80 8.20 +%define COL40 40 8.21 +%ifdef MDA_SUPPORT 8.22 + %define COLS COL80 8.23 +%else 8.24 + %define COLS COL40 8.25 +%endif 8.26 +%define LINES 25 8.27 8.28 boot: 8.29 +tail: 8.30 + call get_data 8.31 + 8.32 +; data section: 8.33 + 8.34 +; 0000 0000 EMPTY 8.35 +; 0000 0001 SPOT 8.36 +; 0000 0010 BRICK 8.37 +%define BRICK 2 8.38 +; 0000 0011 BRICK ON SPOT 8.39 +; 0000 0100 WALL 8.40 +; 0000 0101 OUTSIDE 8.41 +; 0000 0110 PLAYER 8.42 +%define PLAYER 6 8.43 +; 0000 0111 PLAYER ON SPOT 8.44 +display_chars: db 0, 0x07 ; blank 8.45 + db 0xF9, 0x07 ; spot 8.46 + db 0xFE, 0x0A ; brick 8.47 +%ifdef MDA_SUPPORT 8.48 + db 4, 0x0C ; brick on spot 8.49 +%else 8.50 + db 0xFE, 0x0C ; brick on spot 8.51 +%endif 8.52 + db 0xB0, 0x71 ; wall 8.53 + db 0, 0x07 ; out blank 8.54 + db 1, 0x0F ; player 8.55 + db 1, 0x0F ; player on spot 8.56 + 8.57 +str_you_win: db 'YOU WIN! ',1,1,1 8.58 + 8.59 +test_level: 8.60 + 8.61 +%define p(a,b,c) ((c&7)+((b&7)*6)+((a&7)*6*6)) 8.62 +%define LEVEL 2 8.63 + 8.64 +;# convert a sokojs level 8.65 +;echo ";$1" 8.66 +;sed '/^" /!d;s|^" ||;s|".*||;y|_.$*# @+|012345@Z|' $1 | awk 'BEGIN { q=""; l=0 } 8.67 +;function out() { 8.68 +; line[l] = "" 8.69 +; while (length(q) > 2) { 8.70 +; line[l] = line[l] sprintf(" p(%c,%c,%c),", substr(q,1,1), substr(q,2,1), substr(q,3,1)) 8.71 +; q = substr(q,4) 8.72 +; } 8.73 +; l++ 8.74 +;} 8.75 +;{ if (index($0,"@") > 0) { px=l; py=index($0,"@")-1 } 8.76 +; if (index($0,"Z") > 0) { px=l; py=index($0,"Z")-1 } 8.77 +; r=length($0); q = q $0; out() 8.78 +;} 8.79 +;END { q = q "55"; if (length(q) > 2) out() 8.80 +; print "%define WIDTH " r; print "%define HEIGHT " l 8.81 +; print "%define PLAYERXY WIDTH*" px "+" py " ; " r*px+py 8.82 +; if ((r*px+py) % 256 >= 90 ) print "%define EOS PLAYERXY" 8.83 +; print " dw PLAYERXY" 8.84 +; for (i = 0; i < l; i++) print line[i] 8.85 +;}' | sed 's|,$||;s|^ p| db p|;s|@|0|;s|Z|1|' 8.86 + 8.87 +%if LEVEL == 1 8.88 +;sokojs sokojs/level57.htm 8.89 +%define WIDTH 14 8.90 +%define HEIGHT 10 8.91 +%define PLAYERXY WIDTH*4+7 8.92 +%define EOS PLAYERXY 8.93 + dw PLAYERXY 8.94 + db p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(5,5, 4) 8.95 + db p(1,1, 0), p(0,4,0), p(0,0,0), p(0,4,4), p(4, 4,1) 8.96 + db p(1, 0,0), p(4,0,2), p(0,0,2), p(0,0,4) 8.97 + db p(4,1,1), p(0,0,4), p(2,4,4), p(4,4,0), p(0,4, 4) 8.98 + db p(1,1, 0), p(0,0,0), p(8,0,4), p(4,0,0), p(4, 4,1) 8.99 + db p(1, 0,0), p(4,0,4), p(0,0,2), p(0,4,4) 8.100 + db p(4,4,4), p(4,4,4), p(0,4,4), p(2,0,2), p(0,4, 5) 8.101 + db p(5,4, 0), p(2,0,0), p(2,0,2), p(0,2,0), p(4, 5,5) 8.102 + db p(4, 0,0), p(0,0,4), p(0,0,0), p(0,0,4) 8.103 + db p(5,5,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4, 5) 8.104 +%elif LEVEL == 2 8.105 +;sokojs sokojs/level83.htm modified 8.106 +%define WIDTH 15 8.107 +%define HEIGHT 14 8.108 +%define PLAYERXY WIDTH*7+13 8.109 +%define EOS PLAYERXY 8.110 + dw PLAYERXY 8.111 + db p(4,4,4), p(4,5,5), p(5,5,5), p(4,4,4), p(4,5,5) 8.112 + db p(4,0,0), p(4,4,4), p(4,4,4), p(4,1,1), p(4,4,4) 8.113 + db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(1,1,4) 8.114 + db p(4,0,2), p(0,0,0), p(2,2,0), p(4,3,3), p(3,1,4) 8.115 + db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(3,1,4) 8.116 + db p(4,0,0), p(2,0,2), p(0,2,0), p(4,3,1), p(3,1,4) 8.117 + db p(4,4,0), p(2,0,2), p(0,2,0), p(1,3,1), p(3,1,4) 8.118 + db p(4,0,0), p(2,0,2), p(0,2,0), p(1,3,1), p(3,1,4) 8.119 + db p(4,0,0), p(2,0,2), p(0,2,0), p(4,3,1), p(3,1,4) 8.120 + db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(3,1,4) 8.121 + db p(4,0,2), p(0,0,0), p(2,2,0), p(4,3,3), p(3,1,4) 8.122 + db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(1,1,4) 8.123 + db p(4,0,0), p(4,4,4), p(4,4,4), p(4,1,1), p(4,4,4) 8.124 + db p(4,4,4), p(4,5,5), p(5,5,5), p(4,4,4), p(4,5,5) 8.125 +%elif LEVEL == 3 8.126 +;sokojs ALDMGR/level0.htm 8.127 +%define WIDTH 19 8.128 +%define HEIGHT 18 8.129 +%define PLAYERXY WIDTH+8 8.130 +%define EOS PLAYERXY 8.131 + dw PLAYERXY 8.132 + db p(5,5,5), p(5,5,5), p(5,4,4), p(4,5,5), p(5,5,5), p(5,5,5), p(5, 5,5) 8.133 + db p(4, 4,4), p(4,4,0), p(8,0,4), p(4,4,4), p(4,4,4), p(5,5, 5) 8.134 + db p(5,4, 0), p(0,0,4), p(0,2,0), p(4,0,0), p(0,0,0), p(4,4,5) 8.135 + db p(5,4,4), p(0,2,0), p(4,4,0), p(0,4,2), p(0,0,0), p(0,0,4), p(4, 5,4) 8.136 + db p(0, 2,0), p(2,0,4), p(0,0,4), p(0,2,4), p(4,0,2), p(0,4, 4) 8.137 + db p(4,0, 3), p(1,1,0), p(4,4,0), p(4,2,0), p(4,4,2), p(0,0,4) 8.138 + db p(4,0,2), p(1,1,3), p(0,0,4), p(0,4,0), p(2,0,0), p(0,0,4), p(4, 4,0) 8.139 + db p(3, 2,4), p(2,3,0), p(4,2,0), p(0,0,2), p(0,0,4), p(4,5, 4) 8.140 + db p(1,1, 0), p(4,0,1), p(1,4,0), p(0,4,4), p(4,2,4), p(4,5,5) 8.141 + db p(4,0,4), p(4,4,4), p(4,0,4), p(0,2,0), p(0,2,0), p(0,4,4), p(5, 4,0) 8.142 + db p(2, 0,4), p(0,2,0), p(0,2,0), p(2,0,2), p(0,2,0), p(4,4, 4) 8.143 + db p(0,1, 1), p(4,1,1), p(0,2,0), p(4,0,0), p(0,2,0), p(2,0,4) 8.144 + db p(4,0,1), p(1,2,1), p(1,0,4), p(0,4,0), p(2,4,4), p(0,0,0), p(4, 4,0) 8.145 + db p(1, 1,1), p(1,1,2), p(4,0,4), p(0,0,0), p(0,0,2), p(0,4, 4) 8.146 + db p(0,1, 1), p(1,1,1), p(0,4,0), p(4,2,2), p(2,0,2), p(0,4,4) 8.147 + db p(4,0,1), p(1,1,1), p(1,2,4), p(0,4,0), p(0,0,0), p(0,4,4), p(5, 4,0) 8.148 + db p(1, 1,1), p(1,1,0), p(4,0,4), p(4,4,4), p(4,4,4), p(5,5, 5) 8.149 + db p(4,4, 4), p(4,4,4), p(4,5,4), p(5,5,5), p(5,5,5), p(5,5,5) 8.150 +%else 8.151 +;sokojs David_W_Skinner_Arranged/level174.htm 8.152 +%define WIDTH 30 8.153 +%define HEIGHT 20 8.154 +%define PLAYERXY WIDTH*7+12 ; 222 8.155 +%define EOS PLAYERXY 8.156 + dw PLAYERXY 8.157 + db p(5,5,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,5,5) 8.158 + db p(5,5,4), p(0,0,0), p(0,4,0), p(0,0,0), p(4,0,0), p(0,0,4), p(0,0,0), p(0,4,0), p(0,0,0), p(4,5,5) 8.159 + db p(4,4,4), p(1,2,2), p(1,4,1), p(2,2,1), p(4,1,2), p(2,1,4), p(1,2,2), p(1,4,1), p(2,2,1), p(4,4,4) 8.160 + db p(4,0,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,3,0), p(0,3,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,0,4) 8.161 + db p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4), p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4) 8.162 + db p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4), p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4) 8.163 + db p(4,0,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,3,0), p(0,3,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,0,4) 8.164 + db p(4,4,4), p(1,2,2), p(1,0,1), p(2,2,1), p(0,1,2), p(2,1,0), p(1,2,2), p(1,0,1), p(2,2,1), p(4,4,4) 8.165 + db p(4,0,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,3,0), p(0,3,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,0,4) 8.166 + db p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4), p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4) 8.167 + db p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4), p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4) 8.168 + db p(4,0,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,3,0), p(0,3,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,0,4) 8.169 + db p(4,4,4), p(1,2,2), p(1,0,1), p(2,2,1), p(0,1,2), p(2,1,0), p(1,2,2), p(1,0,1), p(2,2,1), p(4,4,4) 8.170 + db p(4,0,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,3,0), p(0,3,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,0,4) 8.171 + db p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4), p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4) 8.172 + db p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4), p(4,0,2), p(0,4,4), p(0,2,0), p(4,4,0), p(2,0,4) 8.173 + db p(4,0,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,3,0), p(0,3,1), p(3,0,0), p(3,1,3), p(0,0,3), p(1,0,4) 8.174 + db p(4,4,4), p(1,2,2), p(1,4,1), p(2,2,1), p(4,1,2), p(2,1,4), p(1,2,2), p(1,4,1), p(2,2,1), p(4,4,4) 8.175 + db p(5,5,4), p(0,0,0), p(0,4,0), p(0,0,0), p(4,0,0), p(0,0,4), p(0,0,0), p(0,4,0), p(0,0,0), p(4,5,5) 8.176 + db p(5,5,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,4,4), p(4,5,5) 8.177 +%endif 8.178 + 8.179 +get_data: 8.180 + pop bp 8.181 +%define BP(x) [bp+x-display_chars] 8.182 + ; set up stack 8.183 + push cs 8.184 + pop ss 8.185 + xor sp, sp 8.186 + 8.187 +%ifdef MDA_SUPPORT 8.188 + %ifdef MDA_CGA40 8.189 + ; screen memory in text mode 8.190 + mov cx, SCREEN_DS 8.191 +.init_es: 8.192 +%define patch_ofs 0x170 8.193 +%define BPDI(x) [bp+di+x-display_chars-patch_ofs] 8.194 + mov di, patch_ofs 8.195 + xor word BPDI(patch_win_pos), ((COL40 * 12 + COL40/2 - 6) * 2) ^ ((COL80 * 12 + COL80/2 - 6) * 2) 8.196 + xor word BPDI(patch_middle), ((25 * COL40) - (WIDTH & 0xFE) - (COL40 * (HEIGHT & 0xFE))) ^ ((25 * COL80) - (WIDTH & 0xFE) - (COL80 * (HEIGHT & 0xFE))) 8.197 + xor byte BPDI(patch_next_row),((COL40 - WIDTH) * 2) ^ ((COL80 - WIDTH) * 2) 8.198 + xor word BPDI(patch_count_pos),(COL40 + 2 + (LINES-1)*COL40*2) ^ (COL80 + 2 + (LINES-1)*COL80*2) 8.199 + mov es, cx 8.200 + mov ch, 0xb0 8.201 + inc byte [es:di] 8.202 + je .init_es 8.203 + %endif 8.204 +%endif 8.205 +restart: 8.206 + cld 8.207 sti ; Allow interrupts 8.208 + 8.209 + push cs 8.210 + pop ds 8.211 + 8.212 +%ifdef UNDO 8.213 + mov word [HISTORY], sp 8.214 +%endif 8.215 + 8.216 +back: 8.217 + call init_data 8.218 +%ifdef UNDO 8.219 + mov word BP(tail), es ; HISTORY = es 8.220 +%endif 8.221 + 8.222 +mainloop: 8.223 + ; read key 8.224 + xor ax, ax 8.225 + cwd 8.226 +%ifdef UNDO 8.227 + mov si, BP(tail) 8.228 + or ah, [si] 8.229 + jnz .arrows 8.230 +%endif 8.231 +.wait_for_esc: 8.232 + int 0x16 8.233 + 8.234 + dec ah ; esc 8.235 + je quit 8.236 + 8.237 +.chk_restart: 8.238 + or dx, dx 8.239 + jne restart 8.240 + 8.241 +.arrows: 8.242 + xchg al, ah 8.243 + 8.244 + inc dx 8.245 + cmp al, 0x4d-1 ; right arrow 8.246 + je .try_move_right 8.247 + cmp al, 0x4b-1 ; left arrow 8.248 + je .try_move_left 8.249 + 8.250 + mov dl, WIDTH ; (width of current level) to the right = 1 down 8.251 + cmp al, 0x50-1 ; down arrow 8.252 + je .try_move_down 8.253 + cmp al, 0x48-1 ; up arrow 8.254 +%ifdef UNDO 8.255 + je .try_move_up 8.256 + 8.257 + sub al, 0xE-1 ; backspace 8.258 + jne .redraw 8.259 + mov [si-1], al 8.260 + jmp back 8.261 +%else 8.262 + jne .redraw 8.263 +%endif 8.264 + 8.265 +.try_move_left: 8.266 +.try_move_up: 8.267 + neg dx 8.268 +.try_move_right: 8.269 +.try_move_down: 8.270 + 8.271 +%ifdef UNDO 8.272 + xchg [si], al 8.273 + inc si 8.274 + mov BP(tail), si 8.275 + or al, al 8.276 + jnz .not_end 8.277 + mov [si], ax 8.278 +.not_end: 8.279 +%endif 8.280 + 8.281 + call try_move 8.282 + 8.283 +%ifdef UNDO 8.284 + %ifdef FAST_UNDO 8.285 + lodsw 8.286 + or ah, ah 8.287 + jnz mainloop 8.288 + %endif 8.289 +%endif 8.290 + 8.291 +.redraw: 8.292 + call draw_current_level 8.293 + 8.294 + dec bx 8.295 + jne mainloop ; found a spotless brick 8.296 + 8.297 +win: 8.298 + ; print a nice win message to the middle of the screen 8.299 + lea si, BP(str_you_win) 8.300 + 8.301 + ; destination position on screen 8.302 + mov di, (COLS * 12 + COLS/2 - 6) * 2 8.303 +patch_win_pos equ $ - 2 8.304 + 8.305 +.loop: 8.306 + lodsb 8.307 + 8.308 +%ifdef EOS 8.309 + %if EOS & 0x80 == 0 8.310 + cbw 8.311 + %else 8.312 + mov ah, 0 8.313 + %endif 8.314 + cmp al, EOS & 255 8.315 + je mainloop.wait_for_esc 8.316 +%else 8.317 + cbw 8.318 + cmp al, 'Z' 8.319 + ja mainloop.wait_for_esc 8.320 +%endif 8.321 + 8.322 + mov ah, 0x0F 8.323 + stosw 8.324 + jmp .loop 8.325 + 8.326 + 8.327 +;; functions: 8.328 + 8.329 +quit: 8.330 + mov ax, 0x0003 ; text mode 80x25 16 colours 8.331 + int 0x10 8.332 + int 0x20 8.333 + int 0x19 8.334 + 8.335 +init_data: 8.336 +%ifdef MDA_SUPPORT 8.337 + %ifdef MDA_CGA40 8.338 + push es 8.339 + %endif 8.340 +%endif 8.341 ; clear screen (re-set text mode) 8.342 mov ax, 0x0001 ; text mode 40x25 16 colours 8.343 -%define COLS 40 8.344 -%define LINES 25 8.345 int 0x10 8.346 8.347 ; disable cursor 8.348 @@ -20,405 +343,162 @@ 8.349 mov ch, 0x3f 8.350 int 0x10 8.351 8.352 - ; set up stack 8.353 - push cs 8.354 - pop ss 8.355 - xor sp, sp 8.356 + ; set current level to test level by copying 8.357 + lea si, BP(test_level) 8.358 8.359 push cs 8.360 - pop ds 8.361 - push cs 8.362 pop es 8.363 + mov di, CURRENT_LEVEL ; next address to copy to 8.364 + 8.365 + ; copy player position ("uncompressed") 8.366 + movsw 8.367 + 8.368 + mov cx, (( WIDTH * HEIGHT ) +2 ) /3 8.369 +uncompress: 8.370 + ; load one "compressed" byte and store 3 "uncompressed" bytes 8.371 + lodsb 8.372 + ; aam n: ah = al / n al = al % n 8.373 + aam 36 8.374 + mov [di], ah ; p/6/6 8.375 + inc di 8.376 + aam 6 8.377 + xchg al, ah 8.378 + stosw ; p/6%6 p%6 8.379 + 8.380 + loop uncompress 8.381 + or byte [di - ((( WIDTH * HEIGHT ) +2 ) /3)*3 + PLAYERXY], PLAYER 8.382 + 8.383 +%ifdef MDA_SUPPORT 8.384 + %ifdef MDA_CGA40 8.385 + pop es 8.386 + %else 8.387 + ; screen memory in text mode 8.388 + mov ch, SCREEN_DS/256 8.389 +.init_es: 8.390 + mov es, cx 8.391 + mov ch, 0xb0 8.392 + mov al, 0xff 8.393 + scasb 8.394 + je .init_es 8.395 + %endif 8.396 +%else 8.397 + ; screen memory in text mode 8.398 + mov ch, SCREEN_DS/256 8.399 + mov es, cx 8.400 + %if 1 8.401 + mov al, 0xff 8.402 + scasb 8.403 + je quit 8.404 + %endif 8.405 +%endif 8.406 8.407 - call get_data 8.408 +draw_current_level: 8.409 + 8.410 + ; print in the middle and not in the corner 8.411 + mov di, (25 * COLS) - (WIDTH & 0xFE) - (COLS * (HEIGHT & 0xFE)) 8.412 +patch_middle equ $ - 2 8.413 + 8.414 + mov si, CURRENT_LEVEL_2 ; source byte 8.415 + mov bx, HEIGHT * 256 + 1 8.416 +.looph: 8.417 + mov cx, WIDTH 8.418 +.loop: 8.419 + lodsb 8.420 + cbw 8.421 + cmp al, BRICK ; spotless brick 8.422 + jne .not_brick 8.423 +%ifdef HUGE_SPOTLESS_COUNT 8.424 + mov bl, al 8.425 +%else 8.426 + inc bx ; 254 max 8.427 +%endif 8.428 +.not_brick: 8.429 + xchg ax, si 8.430 + add si, si 8.431 + mov si, BP(si+display_chars) 8.432 + xchg ax, si 8.433 8.434 -; data section: 8.435 + stosw 8.436 8.437 -; 0000 0000 EMPTY 8.438 -; 0000 0001 SPOT 8.439 -; 0000 0010 BRICK 8.440 -; 0000 0011 BRICK ON SPOT 8.441 -; 0000 0100 WALL 8.442 -; 0000 1000 PLAYER 8.443 -; 0000 1001 PLAYER ON SPOT 8.444 -test_level: 8.445 + ; subtract 1 from X axis counter 8.446 + loop .loop 8.447 8.448 -%define p(a,b,c) ((c&7)+((b&7)*6)+((a&7)*6*6)) 8.449 -%if 1 8.450 - db 15, 14 ;width, height 8.451 - dw 15*7+13 ;playerxy 8.452 -%define PLAYER 9 8.453 - db p(4,4,4), p(4,5,5), p(5,5,5), p(4,4,4), p(4,5,5) 8.454 - db p(4,0,0), p(4,4,4), p(4,4,4), p(4,1,1), p(4,4,4) 8.455 - db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(1,1,4) 8.456 - db p(4,0,2), p(0,0,0), p(2,2,0), p(4,3,3), p(3,1,4) 8.457 - db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(3,1,4) 8.458 - db p(4,0,0), p(2,0,2), p(0,2,0), p(4,3,1), p(3,1,4) 8.459 - db p(4,4,0), p(2,0,2), p(0,2,0), p(1,3,1), p(3,1,4) 8.460 - db p(4,0,0), p(2,0,2), p(0,2,0), p(1,3,1), p(3,PLAYER,4) 8.461 - db p(4,0,0), p(2,0,2), p(0,2,0), p(4,3,1), p(3,1,4) 8.462 - db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(3,1,4) 8.463 - db p(4,0,2), p(0,0,0), p(2,2,0), p(4,3,3), p(3,1,4) 8.464 - db p(4,0,2), p(0,2,0), p(2,0,0), p(4,1,1), p(1,1,4) 8.465 - db p(4,0,0), p(4,4,4), p(4,4,4), p(4,1,1), p(4,4,4) 8.466 - db p(4,4,4), p(4,5,5), p(5,5,5), p(4,4,4), p(4,5,5) 8.467 -%else 8.468 - db 21, 18 ;width, height 8.469 - dw 21+8 ;playerxy 8.470 -%define PLAYER 8 8.471 - db p(5,5,5), p(5,5,5), p(5,4,4), p(4,5,5), p(5,5,5), p(5,5,5), p(5,5,5) 8.472 - db p(5,5,4), p(4,4,4), p(4,0,8), p(0,4,4), p(4,4,4), p(4,4,5), p(5,5,5) 8.473 - db p(5,5,4), p(0,0,0), p(4,0,2), p(0,4,0), p(0,0,0), p(0,4,4), p(5,5,5) 8.474 - db p(5,4,4), p(0,2,0), p(4,4,0), p(0,4,2), p(0,0,0), p(0,0,4), p(4,5,5) 8.475 - db p(5,4,0), p(2,0,2), p(0,4,0), p(0,4,0), p(2,4,4), p(0,2,0), p(4,5,5) 8.476 - db p(4,4,0), p(3,1,1), p(0,4,4), p(0,4,2), p(0,4,4), p(2,0,0), p(4,5,5) 8.477 - db p(4,0,2), p(1,1,3), p(0,0,4), p(0,4,0), p(2,0,0), p(0,0,4), p(4,5,5) 8.478 - db p(4,0,3), p(2,4,2), p(3,0,4), p(2,0,0), p(0,2,0), p(0,4,4), p(5,5,5) 8.479 - db p(4,1,1), p(0,4,0), p(1,1,4), p(0,0,4), p(4,4,2), p(4,4,5), p(5,5,5) 8.480 - db p(4,0,4), p(4,4,4), p(4,0,4), p(0,2,0), p(0,2,0), p(0,4,4), p(5,5,5) 8.481 - db p(4,0,2), p(0,4,0), p(2,0,0), p(2,0,2), p(0,2,0), p(2,0,4), p(4,5,5) 8.482 - db p(4,0,1), p(1,4,1), p(1,0,2), p(0,4,0), p(0,0,2), p(0,2,0), p(4,5,5) 8.483 - db p(4,0,1), p(1,2,1), p(1,0,4), p(0,4,0), p(2,4,4), p(0,0,0), p(4,5,5) 8.484 - db p(4,0,1), p(1,1,1), p(1,2,4), p(0,4,0), p(0,0,0), p(0,2,0), p(4,5,5) 8.485 - db p(4,0,1), p(1,1,1), p(1,0,4), p(0,4,2), p(2,2,0), p(2,0,4), p(4,5,5) 8.486 - db p(4,0,1), p(1,1,1), p(1,2,4), p(0,4,0), p(0,0,0), p(0,4,4), p(5,5,5) 8.487 - db p(4,0,1), p(1,1,1), p(1,0,4), p(0,4,4), p(4,4,4), p(4,4,5), p(5,5,5) 8.488 - db p(5,4,4), p(4,4,4), p(4,4,5), p(4,5,5), p(5,5,5), p(5,5,5), p(5,5,5) 8.489 + ; jump to next row down 8.490 +patch_next_row equ $ + 2 8.491 + add di, (COLS - WIDTH) * 2 8.492 + 8.493 + dec bh ; subtract 1 from Y axis counter 8.494 + jnz .looph 8.495 + ret 8.496 + 8.497 +try_move: 8.498 + ; try to move the player 8.499 + ; dx = offset of how much to move by 8.500 + 8.501 + mov di,CURRENT_LEVEL_2 8.502 + 8.503 + ; calculate requested destination position 8.504 + mov bx, dx 8.505 + add bx, [di - 2] 8.506 + 8.507 + ; get value at destination position 8.508 + cmp byte [bx + di], 4 8.509 + je .cant_push ; it's a wall 8.510 + test byte [bx + di], 0x02 8.511 + jz .dont_push ; it's not a brick (on spot, or not), so don't try pushing 8.512 + 8.513 + ; try pushing brick 8.514 + mov ax, bx ; store player's destination position (brick's current position) 8.515 + add bx, dx ; bx = next brick position 8.516 + 8.517 + ; get value at destination position 8.518 + test byte [bx + di], 0x0E ; test if the destination is occupied at all by ANDing with 0000 1110 8.519 + jnz .cant_push 8.520 + 8.521 + ; all checks passed! push the brick 8.522 + mov dl, BRICK 8.523 + call .move_object 8.524 +%ifdef OPEN_LEVEL 8.525 + jc .cant_push 8.526 %endif 8.527 8.528 - 8.529 -display_chars: db 0, 0x07 ; blank 8.530 - db 249, 0x07 ; spot 8.531 - db 0xFE, 0x0A ; brick 8.532 - db 0xFE, 0x0C ; brick on spot 8.533 - db 176, 0x71 ; wall 8.534 - db 0, 0x07 ; out blank 8.535 - db "6", 0x07 ; (no 6) 8.536 - db "7", 0x07 ; (no 7) 8.537 - db 1, 0x0F ; player 8.538 - db 1, 0x0F ; player on spot 8.539 - 8.540 -str_you_win: db 'YOU WIN! ',1,1,1,0 8.541 - 8.542 -get_data: 8.543 - pop bp 8.544 - 8.545 - ; set current level to test level by copying 8.546 - mov si, bp 8.547 - 8.548 - ; get width and height and multiply by each other 8.549 - lodsw 8.550 - 8.551 - mov di, CURRENT_LEVEL ; next address to copy to 8.552 - 8.553 - ; copy map size and player position ("uncompressed") 8.554 - stosw 8.555 - mul ah 8.556 - 8.557 - ; set multiplied width and height + 4 as counter 8.558 - mov cx, ax 8.559 - ;add cx, 4 8.560 - 8.561 - lodsw 8.562 - stosw 8.563 - 8.564 - xchg ax,bx 8.565 - add bx,di ;save player location 8.566 - mov dx,36*256+6 8.567 - 8.568 -.copy_level_loop: 8.569 - ; load one "compressed" byte and store 3 "uncompressed" bytes 8.570 - lodsb 8.571 - mov ah,0 8.572 - div dh 8.573 - stosb ; p/6/6 8.574 - mov al,ah 8.575 - cbw 8.576 - div dl 8.577 - stosw ; p/6%6 p%6 8.578 - 8.579 - loop .copy_level_loop 8.580 - mov byte [bx],PLAYER 8.581 - 8.582 - call draw_current_level 8.583 - 8.584 -.mainloop: 8.585 - ; read key 8.586 - xor ax, ax 8.587 - int 0x16 8.588 - 8.589 - mov al, byte [CURRENT_LEVEL] ; (width of current level) to the right = 1 down 8.590 - dec ah ; esc 8.591 - jne .not_boot 8.592 -.exit: 8.593 - mov al, 0x03 ; text mode 80x25 16 colours 8.594 - int 0x10 8.595 - int 0x20 8.596 - int 0x19 8.597 - 8.598 -.not_boot: 8.599 - cmp ah, 0x50-1 ; down arrow 8.600 - je .try_move_down 8.601 - 8.602 - cmp ah, 0x48-1 ; up arrow 8.603 - je .try_move_up 8.604 - 8.605 - mov al, 1 8.606 - cmp ah, 0x4d-1 ; right arrow 8.607 - je .try_move_right 8.608 - 8.609 - cmp ah, 0x4b-1 ; left arrow 8.610 - jne .redraw 8.611 - 8.612 -.try_move_left: 8.613 -.try_move_up: 8.614 - neg al 8.615 -.try_move_right: 8.616 -.try_move_down: 8.617 %ifdef MOVE_COUNT 8.618 - push ax 8.619 - push ds 8.620 - mov bx, SCREEN_DS 8.621 - mov ds, bx 8.622 - mov bx, COLS + 2 + (LINES-1)*COLS*2 8.623 - 8.624 + push di 8.625 + mov di, COLS + 2 + (LINES-1)*COLS*2 8.626 +patch_count_pos equ $ - 2 8.627 .chk_score: 8.628 - dec bx 8.629 - dec bx 8.630 + dec di 8.631 + dec di 8.632 mov al, '0' 8.633 - xchg [bx], al 8.634 + xchg [es:di], al 8.635 or al, 0x30 8.636 cmp al, '9' 8.637 je .chk_score 8.638 inc ax 8.639 - mov [bx], al 8.640 - pop ds 8.641 - pop ax 8.642 + stosb 8.643 + pop di 8.644 %endif 8.645 - call try_move 8.646 8.647 -.redraw: 8.648 - call draw_current_level 8.649 +.dont_push: 8.650 + mov ax, bx ; bx = next player position 8.651 + xchg ax, [di - 2] ; update player position in memory 8.652 + ; ax = current player position 8.653 8.654 -.check_win: 8.655 - 8.656 - ; get width and height 8.657 - mov ax, [CURRENT_LEVEL] ; al = width; ah = height 8.658 - mul ah 8.659 - mov cx, ax ; cx = size of map 8.660 - 8.661 - xor bx, bx ; bx = number of bricks-NOT-on-a-spot 8.662 - 8.663 - mov si, CURRENT_LEVEL_4 8.664 -.check_win_loop: 8.665 - lodsb 8.666 - cmp al, 2 8.667 - jne .not_a_brick 8.668 - inc bx 8.669 -.not_a_brick: 8.670 - loop .check_win_loop 8.671 - 8.672 - ; so, did we win? is the number of spotless bricks == 0?? 8.673 - cmp bx, 0 8.674 - je win 8.675 - jmp .mainloop 8.676 - 8.677 - 8.678 -win: 8.679 - ; print a nice win message to the middle of the screen 8.680 - lea si, [bp+str_you_win-test_level] 8.681 - 8.682 - ; destination position on screen 8.683 - mov ax, SCREEN_DS 8.684 - mov es, ax 8.685 - mov di, (COLS * 12 + COLS/2 - 6) * 2 8.686 - 8.687 - mov ah, 0x0F 8.688 -.loop: 8.689 - cs lodsb 8.690 - 8.691 - cmp al, 0 8.692 - je wait_for_esc 8.693 - 8.694 - stosw 8.695 - jmp .loop 8.696 - 8.697 -wait_for_esc: 8.698 - ; read key 8.699 - xor ax, ax 8.700 - int 0x16 8.701 - 8.702 - dec ah ; esc 8.703 - je get_data.exit 8.704 - jmp boot 8.705 -; halt: 8.706 -; cli ; clear interrupt flag 8.707 -; hlt ; halt execution 8.708 - 8.709 - 8.710 -;; functions: 8.711 - 8.712 -draw_current_level: 8.713 - ; get width and height 8.714 - mov cx, [CURRENT_LEVEL] ; cl = width; ch = height 8.715 - push cx ; put it in the stack for later reuse 8.716 - 8.717 - ; print in the middle and not in the corner 8.718 - mov di, 25*COLS; middle of screen 8.719 - 8.720 - ; offset by half of width 8.721 - mov bx, 0x00FE 8.722 - and bl, cl 8.723 - sub di, bx 8.724 - 8.725 - ; offset by half of height 8.726 - mov cl, ch 8.727 - and cx, 0x00FE 8.728 - mov ax, COLS 8.729 - mul cx 8.730 - sub di, ax 8.731 - 8.732 - 8.733 - mov si, CURRENT_LEVEL_4 ; source byte 8.734 - 8.735 - ; screen memory in text mode 8.736 - mov ax, SCREEN_DS 8.737 - mov es, ax 8.738 - 8.739 -.loop: 8.740 - push si 8.741 - lodsb 8.742 - cbw 8.743 - xchg ax, si 8.744 - add si, si 8.745 - mov ax, [bp+si+display_chars-test_level] 8.746 - pop si 8.747 - 8.748 - stosw 8.749 - 8.750 - inc si 8.751 - pop cx ; get counters 8.752 - dec cl ; subtract 1 from X axis counter 8.753 - jz .nextrow 8.754 - push cx 8.755 - jmp .loop 8.756 - 8.757 -.nextrow: 8.758 - dec ch ; subtract 1 from Y axis counter 8.759 - jz .finished 8.760 - mov cl, [CURRENT_LEVEL] 8.761 - push cx 8.762 - 8.763 - ; jump to next row down 8.764 - xor ch, ch 8.765 - neg cx 8.766 - add cx, COLS 8.767 - add cx, cx 8.768 - add di, cx 8.769 - 8.770 - jmp .loop 8.771 - 8.772 -.finished: 8.773 - ret 8.774 - 8.775 -try_move: 8.776 - ; try to move the player 8.777 - ; al = offset of how much to move by 8.778 - 8.779 - ; extend al into ax (signed) 8.780 - test al, al ; check if negative 8.781 - js .negative_al 8.782 - xor ah, ah 8.783 - jmp .after_al 8.784 -.negative_al: 8.785 - mov ah, 0xFF 8.786 -.after_al: 8.787 - push ax 8.788 - 8.789 - mov di,CURRENT_LEVEL_4 8.790 - 8.791 - ; calculate total level size 8.792 - mov ax, [CURRENT_LEVEL] 8.793 - mul ah 8.794 - 8.795 - ; calculate requested destination position 8.796 - pop bx 8.797 - push bx 8.798 - mov dx, [di - 2] 8.799 - add bx, dx 8.800 - 8.801 - ; check if in bounds 8.802 - cmp bx, 0 8.803 - jl .finished 8.804 - cmp bx, ax 8.805 - jg .finished 8.806 - 8.807 - ; get value at destination position 8.808 - mov cl, [bx + di] 8.809 - cmp cl, 4 8.810 - je .cant_push ; it's a wall 8.811 - test cl, 0x02 8.812 - jz .dont_push ; it's not a brick (on spot, or not), so don't try pushing 8.813 - 8.814 - ; try pushing brick 8.815 - pop cx ; get move offset 8.816 - push bx ; store player's destination position (brick's current position) 8.817 - 8.818 - mov dx, bx ; dx = current brick position 8.819 - add bx, cx ; bx = next brick position 8.820 - 8.821 + mov dl, PLAYER 8.822 +.move_object: 8.823 +%ifdef OPEN_LEVEL 8.824 ; check bounds 8.825 - cmp bx, 0 8.826 - jl .cant_push 8.827 - cmp bx, ax 8.828 - jg .cant_push 8.829 - 8.830 - ; get value at destination position 8.831 - mov ch, [bx + di] 8.832 - test ch, 0x0E ; test if the destination is occupied at all by ANDing with 0000 1110 8.833 - jnz .cant_push 8.834 - 8.835 - ; all checks passed! push the brick 8.836 - 8.837 - ; add new brick to screen 8.838 - or ch, 0x02 ; add brick bit, by ORing with 0000 0010 8.839 - mov [bx + di], ch 8.840 - 8.841 - ; remove old brick from screen 8.842 - add di, dx 8.843 - mov cl, [di] 8.844 - and cl, 0xFD ; remove brick bit, by ANDing with 1111 1101 8.845 - mov [di], cl 8.846 - sub di, dx 8.847 - 8.848 - mov dx, [di - 2] ; dx = current player position 8.849 - pop bx ; bx = next player position 8.850 - jmp .redraw_player 8.851 + cmp bx, WIDTH * HEIGHT 8.852 + cmc 8.853 + jc .cant_push 8.854 +%endif 8.855 + ; add new object to screen 8.856 + or byte [bx + di], dl ; add object bits 8.857 + ; remove old object from screen 8.858 + xchg ax, bx 8.859 + xor byte [bx + di], dl ; remove object bits 8.860 8.861 .cant_push: 8.862 - pop bx 8.863 - jmp .finished 8.864 - 8.865 -.dont_push: 8.866 - pop cx ; don't need to have this offset in the stack anymore 8.867 - 8.868 -.redraw_player: 8.869 - ; remove old player from screen 8.870 - add di, dx 8.871 - mov cl, [di] 8.872 - and cl, 0xF7 ; remove player bit, by ANDing with 1111 0111 8.873 - mov [di], cl 8.874 - sub di, dx 8.875 - 8.876 - ; add new player to screen 8.877 - mov ch, [bx + di] 8.878 - or ch, 0x08 ; add player bit, by ORing with 0000 1000 8.879 - mov [bx + di], ch 8.880 - 8.881 - ; update player position in memory 8.882 - mov [di - 2], bx 8.883 - 8.884 -.finished: 8.885 ret 8.886 8.887
9.1 --- a/x86test/receipt Wed Oct 04 13:13:17 2023 +0000 9.2 +++ b/x86test/receipt Sun Feb 04 18:02:38 2024 +0000 9.3 @@ -33,10 +33,10 @@ 9.4 # Rules to configure and make the package. 9.5 compile_rules() 9.6 { 9.7 - mkdir -p $src && cd $src 9.8 - tune_lzma 36,mf=bt2 PB 0 9.9 - test8086_88=$((0x$(sed '/test8086_88$/!d;s|.*text:0*||;s| .*||' patch.lst))) 9.10 - dd if=patch.bin bs=1 skip=$test8086_88 2> /dev/null | dd conv=notrunc of=x86test bs=1 seek=$((0xA00+$test8086_88)) 2> /dev/null 9.11 + mkdir -p $src && cd $src && cp $stuff/x86test . 9.12 + tune_lzma 35,mf=bt2,lc=3,lp=0,pb=0 LC 3 LP 0 PB 0 9.13 + patch=$((0x$(sed '/patch$/!d;s|.*text:0*||;s| .*||' patch.lst))) 9.14 + dd if=patch.bin bs=1 skip=$patch 2> /dev/null | dd conv=notrunc of=x86test bs=1 seek=$((0xA00+$patch)) 2> /dev/null 9.15 ./pack x86test x86test.packed 9.16 dd if=bootloader.bin of=x86test conv=notrunc 2> /dev/null 9.17 }
10.1 --- a/x86test/stuff/patch.S Wed Oct 04 13:13:17 2023 +0000 10.2 +++ b/x86test/stuff/patch.S Sun Feb 04 18:02:38 2024 +0000 10.3 @@ -1,6 +1,8 @@ 10.4 .code16 10.5 10.6 - .org 0xc3 10.7 + .org 0xb1 10.8 +str_NEC_V20: .string "NEC V-20" 10.9 +str_NEC_V30: .string "NEC V-30" 10.10 str_8086: .string "8086 (16-bit NMOS)" 10.11 str_8088: .string "8088 (8-bit NMOS)" 10.12 str_80C86: .string "80C86 (16-bit CMOS)" 10.13 @@ -11,29 +13,49 @@ 10.14 10.15 test_width8_16: 10.16 10.17 - .org 0x18a 10.18 + .org 0x165 10.19 + 10.20 +patch: 10.21 + movw $str_80188-str_80186, %dx 10.22 + movw $str_80186, %si 10.23 + jnz width8_16 10.24 + 10.25 +pre_186: /* 10.26 + * NEC V20/30 support 80186 instructions e.g. pusha (executed 10.27 + * as 2-byte nop on 8086/88). 10.28 + */ 10.29 + movb $str_NEC_V30-str_NEC_V20, %dl 10.30 + movw $str_NEC_V20, %si 10.31 + 10.32 + movw %sp, %ax 10.33 + pushaw 10.34 + xchgw %ax, %sp 10.35 + subw %sp, %ax 10.36 + jnz width8_16 10.37 + 10.38 test8086_88: 10.39 pushf 10.40 - xorw %cx, %cx 10.41 + xchgw %ax, %cx 10.42 + movw $0x46c, %di 10.43 movw %cx, %es 10.44 - es movw 0x46c, %bx # BIOS tick count l.o. word 10.45 + es movw (%di), %bx # BIOS tick count l.o. word 10.46 1: pushw %cx 10.47 sti 10.48 rep cs lodsb 10.49 cli 10.50 orw %cx, %cx 10.51 popw %cx 10.52 - movw $str_8088, %ax 10.53 + movb $str_8088-str_8086, %dl 10.54 movw $str_8086, %si 10.55 jnz nmos_8086_88 10.56 - movb $str_80C88, %al 10.57 + incw %dx 10.58 movw $str_80C86, %si 10.59 - es cmpw 0x046c, %bx 10.60 + es cmpw (%di), %bx 10.61 loope 1b 10.62 nmos_8086_88: popf 10.63 - call test_width8_16 10.64 +width8_16: call test_width8_16 10.65 jz 1f 10.66 - xchgw %ax, %si 10.67 + addw %dx, %si 10.68 1: ret 10.69 10.70 .org 0x1bb