wok-tiny rev 180
Add boot-man, bootbricks, bootfbird, bootinvaders, bootmine, bootpillman, bootris, bootsokoban
author | Pascal Bellard <pascal.bellard@slitaz.org> |
---|---|
date | Wed Sep 20 13:08:44 2023 +0000 (7 months ago) |
parents | d5c772484b59 |
children | 2a1ec9d88ac0 |
files | boot-man/receipt boot-man/stuff/boot-man.asm bootbricks/receipt bootbricks/stuff/bricks.asm bootfbird/receipt bootfbird/stuff/fbird.asm bootinvaders/receipt bootinvaders/stuff/invaders.asm bootmine/receipt bootmine/stuff/mine.asm bootpillman/receipt bootpillman/stuff/pillman.asm bootris/receipt bootris/stuff/tetranglix.asm bootsokoban/receipt bootsokoban/stuff/sokoban.asm |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/boot-man/receipt Wed Sep 20 13:08:44 2023 +0000 1.3 @@ -0,0 +1,36 @@ 1.4 +# SliTaz package receipt. 1.5 + 1.6 +PACKAGE="boot-man" 1.7 +VERSION="slitaz" 1.8 +CATEGORY="games" 1.9 +SHORT_DESC="Bootable text pacman game in a 512-byte boot sector." 1.10 +MAINTAINER="pascal.bellard@slitaz.org" 1.11 +LICENSE="unknown" 1.12 +#TARBALL="boot-man.asm" 1.13 +WEB_SITE="https://github.com/guyhill/Boot-Man" 1.14 +#WGET_URL="https://github.com/guyhill/Boot-Man/raw/b51fccd9e8974db434c8a40ced133ec0fc5f1b80/boot-man.asm" 1.15 + 1.16 +TARGET="i486" 1.17 + 1.18 +BUILD_DEPENDS="nasm" 1.19 + 1.20 +# Rules to configure and make the package. 1.21 +compile_rules() 1.22 +{ 1.23 + mkdir -p $src 1.24 + nasm -f bin $stuff/boot-man.asm -o $src/boot-man.img -l $src/boot-man.lst 1.25 +} 1.26 + 1.27 +# Rules to gen a SliTaz package suitable for Tazpkg. 1.28 +genpkg_rules() 1.29 +{ 1.30 + mkdir -p $fs/boot 1.31 + cp $src/boot-man.img $fs/boot/$PACKAGE 1.32 +} 1.33 + 1.34 +# Post install/remove commands for Tazpkg. 1.35 +post_install() 1.36 +{ 1.37 + grep -qs ^boot-man $1/boot/bootmenu || 1.38 + echo "boot-man pacman Bootable pacman game (may run under DOS if renamed to pacman.com)" >> $1/boot/bootmenu 1.39 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/boot-man/stuff/boot-man.asm Wed Sep 20 13:08:44 2023 +0000 2.3 @@ -0,0 +1,539 @@ 2.4 +; Boot-Man 2.5 +; 2.6 +; (c) 2019 Guido van den Heuvel 2.7 +; 2.8 +; Boot-Man is a Pac-Man clone that fits (snugly) inside the Master Boot Record of a USB stick. 2.9 +; A USB stick with Boot-Man on it boots into the game (hence the name). Unfortunately, however, 2.10 +; Boot-Man leaves no room in the MBR for a partition table, which means that a USB stick with Boot-Man 2.11 +; in its MBR cannot be used to store data. In fact, Windows does not recognize a USB stick with 2.12 +; Boot-Man in its MBR as a valid storage medium. 2.13 +; 2.14 +; Controls of the game: you control Boot-Man using the arrow keys. No other user input is necessary. Some other 2.15 +; keys can also be used to control Boot-Man, this is a side effect of coding my own keyboard handler in 2.16 +; just a few bytes. There simply wasn't room for checking the validity of every key press. 2.17 +; 2.18 +; The game starts automatically, and when Boot-Man dies, a new game starts automatically after any key hit. 2.19 +; 2.20 +; 2.21 +; I've had to take a couple of liberties with the original Pac-Man game to fit Boot-Man inside the 510 2.22 +; bytes available in the MBR: 2.23 +; 2.24 +; * The ghosts start in the four corners of the maze, they do not emerge from a central cage like in the original 2.25 +; 2.26 +; * There's just a single level. If you finish the level, the game keeps running with an empty maze. While 2.27 +; it is rather difficult to finish the game (which is intentional, because of the single level), it is possible. 2.28 +; 2.29 +; * Boot-Man only has 1 life. If Boot-Man dies, another game is started automatically. 2.30 +; 2.31 +; * Power pills function differently from the original. When Boot-Man eats a power pill, all ghosts become 2.32 +; ethereal (represented in game by just their eyes being visible) and cease to chase Boot-Man. While ethereal, 2.33 +; Boot-Man can walk through ghosts with no ill effects. While I would really like to include the "ghost hunting" 2.34 +; from the original, which I consider to be an iconic part of the game, this simply isn't possible in the little 2.35 +; space available. 2.36 +; 2.37 +; * There's no score, and no fruit to get bonus points from. 2.38 +; 2.39 +; * All ghosts, as well as Boot-Man itself, have the same, constant movement speed. In the original, the ghosts 2.40 +; run at higher speeds than Pac-Man, while Pac-Man gets delayed slightly when eating and ghosts get delayed when moving 2.41 +; through the tunnel connecting both sides of the maze. This leads to very interesting dynamics and strategies 2.42 +; in the original that Boot-Man, by necessity, lacks. 2.43 +; 2.44 +; 2.45 +; Boot-Man runs in text mode. It uses some of the graphical characters found in IBM codepage 437 for its objects: 2.46 +; - Boot-Man itself is represented by the smiley face (☻), which is character 0x02 in the IBM charset 2.47 +; - The Ghosts are represented by the infinity symbol (∞), which is character 0xec. These represent 2.48 +; a ghost's eyes, with the ghost's body being represented simply by putting the character on a 2.49 +; coloured background 2.50 +; - The dots that represent Boot-Man's food are represented by the bullet character (•), 2.51 +; which is character 0xf9 2.52 +; - The power pills with which Boot-Man gains extra powers are represented by the diamond (♦), 2.53 +; which is character 0x04 2.54 +; - The walls of the maze are represented by the full block character (█), which is character 0xdb 2.55 +; 2.56 +; Boot-Man runs off BIOS clock. It should therefore run at the same speed on all PCs. The code is quite heavily 2.57 +; optimized for size, so code quality is questionable at best, and downright atrocious at worst. 2.58 + 2.59 +;%define WaitForAnyKey 2.60 + 2.61 +org 0x0600 ; The code will move to a well known position to allow some patches. 2.62 + 2.63 +cpu 8086 ; Boot-Man runs on the original IBM PC with a CGA card. 2.64 +bits 16 ; Boot-Man runs in Real Mode. I am assuming that the BIOS leaves the CPU is Real Mode. 2.65 + ; This is true for the vast majority of PC systems. If your system's BIOS 2.66 + ; switches to Protected Mode or Long Mode during the boot process, Boot-Man 2.67 + ; won't run on your machine. 2.68 + 2.69 +start: 2.70 + call copy ; Can run as bootsector or DOS COM file 2.71 + 2.72 +moved: 2.73 + push cs 2.74 + pop ss 2.75 + mov sp, cx ; Set up the stack. 2.76 + 2.77 + xchg ax, cx 2.78 + inc ax ; int 0x10 / ah = 0: Switch video mode. Switch mode to 40x25 characters (al = 1). 2.79 + int 0x10 ; In this mode, characters are approximately square, which means that horizontal 2.80 + ; and vertical movement speeds are almost the same. 2.81 + 2.82 + mov cx, 0xb800 2.83 + mov es, cx ; Set up the es segment to point to video RAM 2.84 + 2.85 + mov ax, 0x0101 ; int 0x10 / ah = 1: Determine shape of hardware cursor. al = 1 avoid AMI BIOS bug. 2.86 + mov ch, 0x20 ; With cx = 0x2000, this removes the hardware cursor from the screen altogether 2.87 + int 0x10 2.88 + 2.89 + 2.90 + 2.91 +;----------------------------------------------------------------------------------------------------- 2.92 +; buildmaze: builds the maze. The maze is stored in memory as a bit array, with 1 representing a wall 2.93 +; and 0 representing a food dot. Since the maze is left-right symmetrical, only half of the 2.94 +; maze is stored in memory. The positions of the power pills is hard-coded in the code. 2.95 +; Adding the power pills to the bit array would have necessitated 2 bits for every 2.96 +; character, increasing its size drastically. 2.97 +; 2.98 +; Both sides of the maze are drawn simultaneously. The left part is drawn left to right, 2.99 +; while the right part is drawn right to left. For efficiency reasons, the entire maze 2.100 +; is built from the bottom up. Therefore, the maze is stored upside down in memory 2.101 +;----------------------------------------------------------------------------------------------------- 2.102 +buildmaze: 2.103 + mov di, 0x0788 ; Lower left corner of maze in video ram 2.104 + mov si, maze ; The first byte of the bit array containing the maze 2.105 + mov dx, 0x05fa ; Address in video ram of the lower left powerpill 2.106 +.maze_outerloop: 2.107 + mov cx, 0x003c ; The distance between a character in the maze and its 2.108 + ; symmetric counterpart. Also functions as loop counter 2.109 + lodsw ; Read 16 bits from the bit array, which represents one 2.110 + ; 32 character-wide row of the maze 2.111 +.maze_innerloop: 2.112 + shl ax, 1 ; shift out a single bit to determine whether a wall or dot must be shown 2.113 + push ax 2.114 + mov ax, 0x01db ; Assume it is a wall character (0x01: blue; 0xdb: full solid block) 2.115 + jc .draw ; Draw the character if a 1 was shifted out 2.116 + mov ax, 0x0ff9 ; otherwise, assume a food character (0x0f: white; x0f9: bullet) 2.117 + cmp di, dx ; See if instead of food we need to draw a power pill 2.118 + jnz .draw 2.119 + mov dh, 0x00 ; Update powerpill address to draw remaining powerpills 2.120 + mov al, 0x04 ; powerpill character (0x04: diamond - no need to set up colour again) 2.121 +.draw: 2.122 + stosw ; Store character + colour in video ram 2.123 + push di 2.124 + add di, cx ; Go to its symmetric counterpart 2.125 + stosw ; and store it as well 2.126 + pop di 2.127 + pop ax 2.128 + sub cx, 4 ; Update the distance between the two sides of the maze 2.129 + jns .maze_innerloop ; As long as the distance between the two halves is positive, we continue 2.130 + 2.131 + sub di, 0x70 ; Go to the previous line on the screen in video RAM. 2.132 + jns .maze_outerloop ; Keep going as long as this line is on screen. 2.133 + 2.134 +;----------------------------------------------------------------------------------------------------- 2.135 +; game_loop: The main loop of the game. Tied to BIOS clock (which fires 18x per 2.136 +; second by default), to ensure that the game runs at the same speed on all machines 2.137 +; 2.138 +; The code first updates Boot-Man's position according to its movement direction 2.139 +; and keyboard input. Then the ghost AI is run, to determine the ghosts' movement 2.140 +; direction and update their position. Finally, Boot-Man and the ghosts are drawn 2.141 +; in their new positions. Collisions between Boot-Man and the ghosts are checked 2.142 +; before and after ghost movement. We need to detect for collisions twice, because 2.143 +; if you only check once, Boot-Man can change position with a ghost without colliding 2.144 +; (in the original, collisions are checked only once, and as a consequence, it is 2.145 +; possible in some circumstances to actually move Pac-Man through a ghost). 2.146 +;----------------------------------------------------------------------------------------------------- 2.147 +game_loop: 2.148 + hlt ; Wait for clock interrupt 2.149 + mov ah,0x00 2.150 + int 0x1a ; BIOS clock read 2.151 + xchg ax,dx 2.152 +old_time equ $ + 1 2.153 + cmp ax,0x1234 ; Wait for time change 2.154 + je game_loop 2.155 + mov [old_time],ax ; Save new time 2.156 + 2.157 + mov si, bootman_data ; Use si as a pointer to the game data. This reduces byte count of the code: 2.158 + ; mov reg, [address] is a 4 byte instruction, while mov reg, [si] only has 2. 2.159 + 2.160 + mov ah,0x01 ; BIOS Key available 2.161 + int 0x16 2.162 + cbw ; BIOS Read Key 2.163 + je .no_key 2.164 + int 0x16 2.165 + mov al,ah 2.166 + 2.167 + ; This code converts al from scancode to movement direction. 2.168 + ; Input: 0x48 (up), 0x4b (right), 0x50 (down), 0x4d (left) 2.169 + ; Output: 0xce (up), 0xca (right), 0xc6 (down), 0xc2 (left) 2.170 + 2.171 + sub al, 0x47 ; 0x01 0x04 0x09 0x06 2.172 + and al,0xfe ; 0x00 0x04 0x08 0x06 2.173 + cmp al, 9 2.174 + jnc .no_key ; if al >= 0x50, ignore scancode; 2.175 + ; this includes key release events 2.176 + neg al ; 0x00 0xfc 0xf8 0xfa 2.177 + add al,0xce ; 0xce 0xca 0xc6 0xc8 2.178 + test al,2 2.179 + jne .translated 2.180 + mov al,0xc2 2.181 +.translated: 2.182 + cmp al, [si + 2] ; If the new direction is the same as the current direction, ignore it 2.183 + jz .no_key 2.184 + mov [si + 3], al ; Set new direction to the direction derived from the keyboard input 2.185 +.no_key: 2.186 + dec byte [si + pace_offset] ; Decrease the pace counter. The pace counter determines the overall 2.187 + ; speed of the game. We found that moving at a speed of 6 per second 2.188 + ; gives good speed and control, so we use a counter to only move 2.189 + ; once for every three times that the interrupt fires. 2.190 + ; We also use the pace counter to include longer delays at game start 2.191 + ; and after Boot-Man dies, by intitalizing the counter with higher values. 2.192 + jnz game_loop ; If the pace counter is not 0, we immediately finish. 2.193 + 2.194 + mov byte [si + pace_offset], 0x3; Reset the pace counter. 2.195 +;----------------------------------------------------------------------------------------------------- 2.196 +; Move Boot-Man 2.197 +;----------------------------------------------------------------------------------------------------- 2.198 + mov al, [si + 3] ; al = new movement direction, as determined by keyboard input 2.199 + call newpos_bootman ; Update dx to move 1 square in the direction indicated by al 2.200 + ; newpos also checks for collisions with walls (in which case ZF is set) 2.201 + jz .nodirchange ; ZF indicates that new position collides with wall. We therefore try to keep 2.202 + ; moving in the current direction instead. 2.203 + mov [si + 2], al ; If there's no collision, update the current movement direction 2.204 +.nodirchange: 2.205 + mov al, [si + 2] ; al = current movement direction 2.206 + call newpos_bootman ; Update dx to move 1 square in direction al 2.207 + jz .endbootman ; If there's a wall there, do nothing 2.208 +.move: 2.209 + mov ax, 0x0f20 ; Prepare to remove Boot-Man from screen, before drawing it in the new position 2.210 + ; 0x0f = black background, white foreground; 0x20 = space character 2.211 + cmp byte [es:di], 0x04 ; Detect power pill 2.212 + jnz .nopowerpill 2.213 + 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 2.214 + ; as it accidentally contains 0x20, and this is one byte shorter than having an 2.215 + ; explicit constant. 2.216 +.nopowerpill: 2.217 + xchg dx, [si] ; Retrieve current position and store new position 2.218 + call paint ; Actually remove Boot-Man from the screen 2.219 +.endbootman: 2.220 +;----------------------------------------------------------------------------------------------------- 2.221 +; ghost AI and movement 2.222 +; 2.223 +; Determine the new movement direction for each ghost. Ghost movement direction is determined by 2.224 +; the following rule: 2.225 +; (1) Every ghost must keep moving 2.226 +; (2) It is forbidden for ghosts to suddenly start moving backwards. Unless Boot-Man just consumed 2.227 +; a powerpill, in which case ghosts are forbidden from continuing in the direction they were going 2.228 +; (3) Whenever a ghost has multiple movement options (i.e., it is at a crossroads), try moving 1 space 2.229 +; in each direction that is allowed, and calculate the distance to the target location after 2.230 +; that move. Choose the direction for which this distance is lowest as the new movement direction 2.231 +; 2.232 +; During normal movement, ghosts target a position that is related to the position of Boot-Man, as follows: 2.233 +; 2.234 +; number | ghost colour | target 2.235 +; -------+--------------+------------------- 2.236 +; 1 | purple | bootman's position 2.237 +; 2 | red | 4 squares below Boot-Man 2.238 +; 3 | cyan | 4 squares to the left of Boot-Man 2.239 +; 4 | green | 4 squares to the right of Boot-Man 2.240 +; 2.241 +; There's two different reasons for having slightly different AI for each ghost: 2.242 +; (1) If all ghosts have the same AI they tend to bunch together and stay there. With the current AI, 2.243 +; ghosts will sometimes bunch together, but they will split apart eventually 2.244 +; (2) With this setup, the ghosts tend to surround Boot-Man, making it harder for the player 2.245 +; 2.246 +; When Boot-Man picks up a power pill, a timer starts running, and ghosts become ethereal. 2.247 +; As long as the ghosts are ethereal, the 2.248 +; ghosts will not chase Boot-Man. Instead they will use the center of the big rectangular block 2.249 +; in the middle of the maze as their target. They cannot reach it, obviously, so the result is 2.250 +; that they will keep circling this block for as long as the timer runs. 2.251 +; 2.252 +; This AI is related to, but not the same as, the AI actually used in Pac-Man. The red Pac-Man ghost 2.253 +; uses Pac-Man itself as target, same as my purple ghost, while the pink Pac-Man ghost will 2.254 +; target 4 squares ahead of Pac-Man, in the direction Pac-Man is currently moving. The other ghosts' 2.255 +; movement is a bit more complex than that. I had to simplify the AI because of the limited code size. 2.256 +;----------------------------------------------------------------------------------------------------- 2.257 + mov bx, 3 * gh_length + bm_length ; Set up offset to ghost data. With this, si + bx is a 2.258 + ; pointer to the data from the last ghost. Also used as 2.259 + ; loop counter to loop through all the ghosts 2.260 + mov byte [si + collision_offset], bh ; Reset collision detection. BH happens to be 0 at this point 2.261 +.ghost_ai_outer: 2.262 + mov bp, 0xffff ; bp = minimum distance; start out at maxint 2.263 + mov ah, [bx + si] ; ah will become the forbidden movement direction. We start 2.264 + ; with the current direction, which is forbidden if Boot-Man 2.265 + ; just ate a power pill 2.266 + cmp byte [si + timer_offset], 0x20 ; If timer_offset == 0x20, Boot-Man just picked up a power pill 2.267 + jz .reverse ; so in that case we do not flip the direction. 2.268 + xor ah, 8 ; Flip the current direction to obtain the forbidden direction in ah 2.269 +.reverse: 2.270 + mov al, 0xce ; al = current directions being tried. Doubles as loop counter 2.271 + ; over all directions. 2.272 + ; Values are the same as those used by the newpos routine 2.273 + mov dx, [bx + si + gh_offset_pos] ; dx = current ghost position 2.274 + cmp dx, [si] ; compare dx with Boot-Man position 2.275 + jne .ghost_ai_loop ; If they are equal, 2.276 + mov [si + collision_offset], al ; We store a non-zero value in the collision_detect flag 2.277 + ; We use al here as we know it to be non-zero, and this reduces 2.278 + ; code size compared to using a literal constant. 2.279 +.ghost_ai_loop: 2.280 + push dx 2.281 + cmp al, ah ; If the current direction is the forbidden direction 2.282 + jz .next ; we continue with the next direction 2.283 + call newpos ; Update ghost position and check if it collides with a wall 2.284 + jz .next ; if so, we continue with the next direction 2.285 + mov cx, 0x0c10 ; Target position if ghosts are ethereal. Position 0x0c10 2.286 + ; (x = 0x10, y = 0x0c) is in the center of the maze. 2.287 + cmp byte [si + timer_offset], bh ; See if ghost timer runs. We compare with bh, which is known to be 0. 2.288 + jnz .skip_target ; If ghost timer runs, we use the aforementioned target position 2.289 + mov cx, [si] ; Otherwise we use Boot-Man's current position, 2.290 + add cx, [bx + si + gh_offset_focus] ; Updated with an offset that is different for each ghost 2.291 +.skip_target: 2.292 +;----------------------------------------------------------------------------------------------------- 2.293 +; get_distance: Calculate distance between positions in cx (target position) and dx (ghost position) 2.294 +; This used to be a function, but I inlined it to save some space. 2.295 +; The square of the distance between the positions in cx and dx is calculated, 2.296 +; according to Pythagoras' theorem. 2.297 +;----------------------------------------------------------------------------------------------------- 2.298 + push ax 2.299 + sub cl, dl ; after this, cl contains the horizontal difference 2.300 + sub ch, dh ; and ch the vertical difference 2.301 + 2.302 + mov al, ch 2.303 + cbw 2.304 + xchg ax,cx 2.305 + cbw ; expand cl to ax and ch to cx 2.306 + 2.307 + push dx 2.308 + mul ax 2.309 + xchg ax,cx ; cx = square of vertical difference 2.310 + mul ax ; ax = square of horizontal difference 2.311 + pop dx 2.312 + 2.313 + add cx, ax ; cx = distance squared between positions in cx and dx 2.314 + pop ax 2.315 + 2.316 + cmp cx, bp ; Compare this distance to the current minimum 2.317 + jnc .next ; and if it is, 2.318 + mov bp, cx ; update the minimum distance 2.319 + mov [bx + si], al ; set the movement direction to the current direction 2.320 + mov [bx + si + gh_offset_pos], dx ; Store the new ghost position 2.321 +.next: 2.322 + pop dx ; Restore the current ghost position 2.323 + sub al, 4 ; Update the current direction / loop counter 2.324 + cmp al, 0xc2 2.325 + jnc .ghost_ai_loop 2.326 + 2.327 + mov ax, [bx + si + gh_offset_terrain] ; Remove the ghost in the old position from the screen 2.328 + call paint ; by painting the terrain underneath that ghost that was 2.329 + ; determined in the previous movement phase. 2.330 + sub bx, gh_length ; Go to the next ghost, 2.331 + jns .ghost_ai_outer ; and stop after the final ghost 2.332 + 2.333 + 2.334 +.ghostterrain_loop: ; Second loop through all the ghosts, to determine terrain 2.335 + ; underneath each one. This is used in the next movement phase 2.336 + ; to restore the terrain underneath the ghosts. 2.337 + ; Note that this "terrain storing" approach can trigger a bug 2.338 + ; if Boot-Man and a ghost share a position. In that case 2.339 + ; an extra Boot-Man character may be drawn on screen. 2.340 + mov dx, [bx + si + gh_offset_pos + gh_length] ; dx = updated ghost position 2.341 + cmp dx, [si] ; compare dx with Boot-Man's position 2.342 + jne .skip_collision ; and if they coincide, 2.343 + mov [si + collision_offset], al ; set the collision detect flag to a non-zero value. 2.344 +.skip_collision: 2.345 + call get_screenpos ; find the address in video ram of the updated ghost position, 2.346 + mov ax, [es:di] ; store its content in ax 2.347 + mov [bx + si + gh_offset_terrain + gh_length], ax ; and copy it to ghostterrain 2.348 + add bx, gh_length ; go to next ghost 2.349 + cmp bx, 3 * gh_length + bm_length ; and determine if it is the final ghost 2.350 + jnz .ghostterrain_loop 2.351 + 2.352 + ; Test if ghosts are invisible 2.353 + mov ax, 0x2fec ; Assume ghost is visible: 0x2f = purple background, white text 2.354 + ; 0xec = infinity symbol = ghost eyes 2.355 + mov cl, 0x10 ; cl = difference in colour between successive ghosts 2.356 + ; ch is set to zero as that leads to smaller code 2.357 + cmp byte [si + timer_offset], bh ; See if ghost timer is running (note bh is still zero at this point) 2.358 + jnz .ghosts_invisible ; If it is, ghosts are ethereal 2.359 + 2.360 + cmp byte [si + collision_offset], bh ; Ghosts are visible, so test for collisions 2.361 + jz .no_collision 2.362 + 2.363 +%ifdef WaitForAnyKey 2.364 + ; Ghosts are visible and collide with boot-man, therefore boot-man is dead 2.365 + mov ax, 0x0e0f ; Dead boot-man: 0x0e = black background, yellow foreground 2.366 + ; 0x0f = 8 pointed star 2.367 + call paint_bootman 2.368 + cbw 2.369 + int 0x16 ; Wait for any key 2.370 +%endif 2.371 +.exit: 2.372 + mov ax,3 2.373 + int 0x10 ; Reset screen 2.374 + int 0x20 ; Exit to DOS... 2.375 +jump_start equ $ + 1 2.376 + mov si,0xFFFC ; ...or restart boot sector 2.377 + push si 2.378 + ret 2.379 + 2.380 + ; Ghosts are invisible 2.381 +.ghosts_invisible: 2.382 + dec byte [si + timer_offset] ; Update ghost_timer to limit the period of the ghosts being ethereal 2.383 + mov ah, 0x0f ; Update ghost colour to black background, white eyes 2.384 + mov cl, 0x0 ; Update difference between colours of successive ghosts. Value of 0x0 2.385 + ; means all ghosts are the same colour when they are ethereal. 2.386 + 2.387 +.no_collision: 2.388 +.ghostdraw: ; Draw the ghosts on the screen 2.389 + mov dx, [bx + si + gh_offset_pos] ; dx = new ghost position 2.390 + call paint ; show ghost in video ram 2.391 + add ah, cl ; Update ghost colour. 2.392 + sub bx, gh_length ; Loop over all ghosts 2.393 + jns .ghostdraw ; until the final one. 2.394 + 2.395 + 2.396 + mov ax, word 0x0e02 ; Draw boot-man on the screen. 0x0e = black background, yellow foreground 2.397 + ; 0x02 = smiley face 2.398 + call paint_bootman ; show Boot-Man 2.399 + 2.400 +.end: 2.401 + jmp game_loop 2.402 + 2.403 + 2.404 +;----------------------------------------------------------------------------------------------------- 2.405 +; copy: self copy to a fixed location 2.406 +;----------------------------------------------------------------------------------------------------- 2.407 +copy: 2.408 + pop si ; Get ip value (0x103 or 0x7C03, sometimes 0x0003) 2.409 + push cs 2.410 + pop ds 2.411 + push cs ; Setup ds and es 2.412 + pop es 2.413 + and [si+jump_start-moved], si ; Save value to the restart with unpatched code 2.414 + mov di, moved ; Move self to a well known address 2.415 + push di 2.416 + mov cx, 512-3 2.417 + cld ; Clear the direction flag. We use string instructions a lot as they have one-byte codes 2.418 + rep movsb 2.419 + ret 2.420 + 2.421 + 2.422 +;----------------------------------------------------------------------------------------------------- 2.423 +; newpos: calculates a new position, starting from a position in dx and movement direction in al. 2.424 +; dl contains the x coordinate, while dh contains the y coordinate. The movement directions 2.425 +; in al are as follows: 2.426 +; 0xc2: move right 2.427 +; 0xc6: move down 2.428 +; 0xca: move left 2.429 +; 0xce: move up 2.430 +; 2.431 +; The reason for these fairly strange values is that they form the 2nd byte (the ModR/M byte) 2.432 +; of the instruction updating the position: 2.433 +; inc dl (0xfe, 0xc2), inc dh (0xfe), dec dl (0xfe, 0xca), dec dh (0xfe, 0xce) 2.434 +; The code first modifies itself to the correct instruction, then executes this instruction. The 2.435 +; reason for doing it in this way is that this is a lot shorter than the traditional way of 2.436 +; doing an if / elif / elif / else chain. 2.437 +; 2.438 +; Immediately after calculating the new position we also determine the address in video RAM 2.439 +; corresponding to this position. All lines of the screen are stored one after the other in RAM, 2.440 +; starting at 0xb800:0x0000. Since each line has 40 characters, and every character takes up 2.441 +; two bytes (one for colour, one for the character code), the equation to calculate video RAM 2.442 +; offset from x, y coordinates is as follows: 2.443 +; 2.444 +; offset = 2 * (40 * y + x + 4), 2.445 +; 2.446 +; with the +4 due to the fact that the maze is in the center of the screen, with a 4 character wide 2.447 +; border to the left. 2.448 +; 2.449 +; newpos and get_screenpos used to be two separate functions but since they were almost 2.450 +; always called one after the other, combining them saved some bytes of code. 2.451 +;----------------------------------------------------------------------------------------------------- 2.452 + 2.453 +newpos_bootman: 2.454 + mov dx, [si] ; dx = current position of Boot-Man 2.455 +newpos: 2.456 + mov [.modified_instruction + 1], al ; Here the instruction to be executed is modified 2.457 +.modified_instruction: 2.458 + db 0xfe, 0xc2 ; inc dl in machine code 2.459 + and dl, 0x1f ; Deal with tunnels 2.460 +get_screenpos: 2.461 + xchg ax,di ; save ax 2.462 + mov al, 0x28 2.463 + mul dh ; multiply ax by 0x28 = 40 decimal, the screen width 2.464 + add al, dl 2.465 + adc ah, 0 2.466 + xchg ax, di ; di = y * 40 + x 2.467 + scasw ; Skip the left border by adding 4 to di 2.468 + scasw 2.469 + shl di, 1 ; Multiply di by 2 2.470 + cmp byte [es:di], 0xdb ; Check to see if the new position collides with a wall 2.471 + ; 0xdb = full block character that makes up the wall 2.472 + ret 2.473 + 2.474 +;----------------------------------------------------------------------------------------------------- 2.475 +; paint: paints a character on screen at given x, y coordinates in dx 2.476 +; simple convenience function that gets called enough to be actually worth it, in terms 2.477 +; of code length. 2.478 +;----------------------------------------------------------------------------------------------------- 2.479 +paint_bootman: 2.480 + mov dx, [si] ; dx = Boot-Man position 2.481 +paint: 2.482 + call get_screenpos ; Convert x, y coordinates in dx to video memory address 2.483 + stosw ; stosw = shorter code for mov [es:di], ax 2.484 + ; stosw also adds 2 to di, but that effect is ignored here 2.485 + ret 2.486 + 2.487 + 2.488 +bootman_data: 2.489 + db 0x0f, 0x0f ; Boot-Man's x and y position 2.490 + db 0xca ; Boot-Man's direction 2.491 + db 0xca ; Boot-Man's future direction 2.492 + 2.493 +pace_counter: db 0x10 2.494 +ghost_timer: db 0x0 ; if > 0 ghosts are invisible, and is counted backwards to 0 2.495 + 2.496 +ghostdata: 2.497 + db 0xc2 ; 1st ghost, direction 2.498 +ghostpos: 2.499 + db 0x01, 0x01 ; x and y position 2.500 +ghostterrain: 2.501 + dw 0x0ff9 ; terrain underneath 2.502 +ghostfocus: 2.503 + db 0x0, 0x0 ; focus point for movement 2.504 +secondghost: 2.505 + db 0xce ; 2nd ghost, direction 2.506 + db 0x01, 0x17 ; x and y position 2.507 + dw 0x0ff9 ; terrain underneath 2.508 + db 0x0, 0x4 ; focus point for movement 2.509 + db 0xca ; 3rd ghost, direction 2.510 + db 0x1e, 0x01 ; x and y position 2.511 + dw 0x0ff9 ; terrain underneath 2.512 + db 0xfc, 0x0 ; focus point for movement 2.513 + db 0xce ; 4th ghost, direction 2.514 + db 0x1e, 0x17 ; x and y position 2.515 + dw 0x0ff9 ; terrain underneath 2.516 + db 0x4, 0x0 ; focus point for movement 2.517 +lastghost: 2.518 + 2.519 +bm_length equ ghostdata - bootman_data 2.520 +gh_length equ secondghost - ghostdata 2.521 +gh_offset_pos equ ghostpos - ghostdata 2.522 +gh_offset_terrain equ ghostterrain - ghostdata 2.523 +gh_offset_focus equ ghostfocus - ghostdata 2.524 +pace_offset equ pace_counter - bootman_data 2.525 +timer_offset equ ghost_timer - bootman_data 2.526 + 2.527 +; The maze, as a bit array. Ones denote walls, zeroes denote food dots / corridors 2.528 +; The maze is stored upside down to save one cmp instruction in buildmaze 2.529 +maze: dw 0xffff, 0x8000, 0xbffd, 0x8081, 0xfabf, 0x8200, 0xbefd, 0x8001 2.530 + dw 0xfebf, 0x0080, 0xfebf, 0x803f, 0xaebf, 0xaebf, 0x80bf, 0xfebf 2.531 + dw 0x0080, 0xfefd, 0x8081, 0xbebf, 0x8000, 0xbefd, 0xbefd, 0x8001 2.532 + dw 0xffff 2.533 +maze_length: equ $ - maze 2.534 + 2.535 +; Collision detection flag. It is initialized by the code 2.536 +collision_detect: 2.537 + 2.538 +collision_offset equ collision_detect - bootman_data 2.539 + 2.540 +times 510 - ($ - $$) db 0 2.541 +db 0x55 2.542 +db 0xaa 2.543 \ No newline at end of file
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/bootbricks/receipt Wed Sep 20 13:08:44 2023 +0000 3.3 @@ -0,0 +1,36 @@ 3.4 +# SliTaz package receipt. 3.5 + 3.6 +PACKAGE="bootbricks" 3.7 +VERSION="slitaz" 3.8 +CATEGORY="games" 3.9 +SHORT_DESC="Bootable bricks game in a 512-byte boot sector." 3.10 +MAINTAINER="pascal.bellard@slitaz.org" 3.11 +LICENSE="unknown" 3.12 +#TARBALL="bricks.asm" 3.13 +WEB_SITE="https://github.com/nanochess/bricks" 3.14 +#WGET_URL="https://github.com/nanochess/bricks/raw/8fb4d78f50e43c19a9ba35d68692cb2abe04e1df/bricks.asm" 3.15 + 3.16 +TARGET="i486" 3.17 + 3.18 +BUILD_DEPENDS="nasm" 3.19 + 3.20 +# Rules to configure and make the package. 3.21 +compile_rules() 3.22 +{ 3.23 + mkdir -p $src 3.24 + nasm -f bin $stuff/bricks.asm -o $src/bricks.img -l $src/bricks.lst 3.25 +} 3.26 + 3.27 +# Rules to gen a SliTaz package suitable for Tazpkg. 3.28 +genpkg_rules() 3.29 +{ 3.30 + mkdir -p $fs/boot 3.31 + cp $src/bricks.img $fs/boot/$PACKAGE 3.32 +} 3.33 + 3.34 +# Post install/remove commands for Tazpkg. 3.35 +post_install() 3.36 +{ 3.37 + grep -qs ^bootbricks $1/boot/bootmenu || 3.38 + echo "bootbricks bricks Bootable bricks game (may run under DOS if renamed to bricks.com)" >> $1/boot/bootmenu 3.39 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/bootbricks/stuff/bricks.asm Wed Sep 20 13:08:44 2023 +0000 4.3 @@ -0,0 +1,312 @@ 4.4 + ; 4.5 + ; Bricks game in one boot sector 4.6 + ; 4.7 + ; by Oscar Toledo G. 4.8 + ; 4.9 + ; Creation date: Nov/02/2019. 4.10 + ; 4.11 + 4.12 + cpu 8086 4.13 + 4.14 + ; 4.15 + ; Press Left Shift to start the game 4.16 + ; Press Left Ctrl to move the paddle to the left 4.17 + ; Press Left Alt to move the paddle to the right 4.18 + ; 4.19 + 4.20 +old_time: equ 16 ; Old time 4.21 +ball_x: equ 14 ; X-coordinate of ball (8.8 fraction) 4.22 +ball_y: equ 12 ; Y-coordinate of ball (8.8 fraction) 4.23 +ball_xs: equ 10 ; X-speed of ball (8.8 fraction) 4.24 +ball_ys: equ 8 ; Y-speed of ball (8.8 fraction) 4.25 +beep: equ 6 ; Frame count to turn off sound 4.26 +bricks: equ 4 ; Remaining bricks 4.27 +score: equ 2 ; Current score 4.28 +balls: equ 0 ; Remaining balls 4.29 + 4.30 + ; 4.31 + ; Start of the game 4.32 + ; 4.33 +start: 4.34 + mov ax,0x0002 ; Text mode 80x25x16 colors 4.35 + int 0x10 ; Setup 4.36 + mov ax,0xb800 ; Address of video screen 4.37 + mov ds,ax ; Setup DS 4.38 + mov es,ax ; Setup ES 4.39 + sub sp,32 4.40 + xor ax,ax 4.41 + push ax ; Reset score 4.42 + mov al,4 4.43 + push ax ; Balls remaining 4.44 + mov bp,sp ; Setup stack frame for globals 4.45 + ; 4.46 + ; Start another level 4.47 + ; 4.48 +another_level: 4.49 + mov word [bp+bricks],273 ; 273 bricks on screen 4.50 + xor di,di 4.51 + mov ax,0x01b1 ; Draw top border 4.52 + mov cx,80 4.53 + cld 4.54 + rep stosw 4.55 + mov cl,24 ; 24 rows 4.56 +.1: 4.57 + stosw ; Draw left border 4.58 + mov ax,0x20 ; No bricks on this row 4.59 + push cx 4.60 + cmp cl,23 4.61 + jnb .2 4.62 + sub cl,15 4.63 + jbe .2 4.64 + mov al,0xdb ; Bricks on this row 4.65 + mov ah,cl 4.66 +.2: 4.67 + mov cl,39 ; 39 bricks per row 4.68 +.3: 4.69 + stosw 4.70 + stosw 4.71 + inc ah ; Increase attribute color 4.72 + cmp ah,0x08 4.73 + jne .4 4.74 + mov ah,0x01 4.75 +.4: 4.76 + loop .3 4.77 + pop cx 4.78 + 4.79 + mov ax,0x01b1 ; Draw right border 4.80 + stosw 4.81 + loop .1 4.82 + 4.83 + ; 4.84 + ; Start another ball 4.85 + ; 4.86 + mov di,0x0f4a ; Position of paddle 4.87 +another_ball: 4.88 + mov byte [bp+ball_x+1],0x28 ; Center X 4.89 + mov byte [bp+ball_y+1],0x14 ; Center Y 4.90 + xor ax,ax 4.91 + mov [bp+ball_xs],ax ; Static on screen 4.92 + mov [bp+ball_ys],ax 4.93 + mov byte [bp+beep],0x01 4.94 + 4.95 + mov si,0x0ffe ; Don't erase ball yet 4.96 +game_loop: 4.97 + call wait_frame ; Wait 1/18.2 secs. 4.98 + 4.99 + mov word [si],0x0000 ; Erase ball 4.100 + 4.101 + call update_score ; Update score 4.102 + 4.103 + mov ah,0x02 ; Read modifier keys 4.104 + int 0x16 4.105 + test al,0x04 ; Left ctrl 4.106 + je .1 4.107 + mov byte [di+6],0 ; Erase right side of paddle 4.108 + mov byte [di+8],0 4.109 + sub di,byte 4 ; Move paddle to left 4.110 + cmp di,0x0f02 ; Limit 4.111 + ja .1 4.112 + mov di,0x0f02 4.113 +.1: 4.114 + test al,0x08 ; Left alt 4.115 + je .2 4.116 + xor ax,ax ; Erase left side of paddle 4.117 + stosw 4.118 + stosw ; DI increased automatically 4.119 + cmp di,0x0f94 ; Limit 4.120 + jb .2 4.121 + mov di,0x0f94 4.122 +.2: 4.123 + test al,0x02 ; Left shift 4.124 + je .15 4.125 + mov ax,[bp+ball_xs] ; Ball moving? 4.126 + add ax,[bp+ball_ys] 4.127 + jne .15 ; Yes, jump 4.128 + ; Setup movement of ball 4.129 + mov word [bp+ball_xs],0xff40 4.130 + mov word [bp+ball_ys],0xff80 4.131 +.15: 4.132 + mov ax,0x0adf ; Paddle graphic and color 4.133 + push di 4.134 + stosw ; Draw paddle 4.135 + stosw 4.136 + stosw 4.137 + stosw 4.138 + stosw 4.139 + pop di 4.140 + 4.141 + mov bx,[bp+ball_x] ; Draw ball 4.142 + mov ax,[bp+ball_y] 4.143 + call locate_ball ; Locate on screen 4.144 + test byte [bp+ball_y],0x80 ; Y-coordinate half fraction? 4.145 + mov ah,0x60 ; Interchange colors for smooth mov. 4.146 + je .12 4.147 + mov ah,0x06 4.148 +.12: mov al,0xdc ; Graphic 4.149 + mov [bx],ax ; Draw 4.150 + push bx 4.151 + pop si 4.152 + 4.153 +.14: 4.154 + mov bx,[bp+ball_x] ; Ball position 4.155 + mov ax,[bp+ball_y] 4.156 + add bx,[bp+ball_xs] ; Add movement speed 4.157 + add ax,[bp+ball_ys] 4.158 + push ax 4.159 + push bx 4.160 + call locate_ball ; Locate on screen 4.161 + mov al,[bx] 4.162 + cmp al,0xb1 ; Touching borders 4.163 + jne .3 4.164 + mov cx,5423 ; 1193180 / 220 4.165 + call speaker ; Generate sound 4.166 + pop bx 4.167 + pop ax 4.168 + cmp bh,0x4f 4.169 + je .8 4.170 + test bh,bh 4.171 + jne .7 4.172 +.8: 4.173 + neg word [bp+ball_xs] ; Negate X-speed if it touches a side 4.174 +.7: 4.175 + test ah,ah 4.176 + jnz .9 4.177 + neg word [bp+ball_ys] ; Negate Y-speed if it touches a side 4.178 +.9: jmp .14 4.179 + 4.180 +.3: 4.181 + cmp al,0xdf ; Touching paddle 4.182 + jne .4 4.183 + sub bx,di ; Subtract paddle position 4.184 + sub bx,byte 4 4.185 + mov cl,6 ; Multiply by 64 4.186 + shl bx,cl 4.187 + mov [bp+ball_xs],bx ; New X speed for ball 4.188 + mov word [bp+ball_ys],0xff80 ; Update Y speed for ball 4.189 + mov cx,2711 ; 1193180 / 440 4.190 + call speaker ; Generate sound 4.191 + pop bx 4.192 + pop ax 4.193 + jmp .14 4.194 + 4.195 +.4: 4.196 + cmp al,0xdb ; Touching brick 4.197 + jne .5 4.198 + mov cx,1355 ; 1193180 / 880 4.199 + call speaker ; Generate sound 4.200 + test bl,2 ; Aligned with brick? 4.201 + jne .10 ; Yes, jump 4.202 + dec bx ; Align 4.203 + dec bx 4.204 +.10: xor ax,ax ; Erase brick 4.205 + mov [bx],ax 4.206 + mov [bx+2],ax 4.207 + inc word [bp+score] ; Increase score 4.208 + neg word [bp+ball_ys] ; Negate Y speed (rebound) 4.209 + pop bx 4.210 + pop ax 4.211 + dec word [bp+bricks] ; One brick less on screen 4.212 + jne .14 ; Fully completed? No, jump. 4.213 + jmp another_level ; Start another level 4.214 + 4.215 +.5: 4.216 + pop bx 4.217 + pop ax 4.218 +.6: 4.219 + mov [bp+ball_x],bx ; Update ball position 4.220 + mov [bp+ball_y],ax 4.221 + cmp ah,0x19 ; Ball exited through bottom? 4.222 + je ball_lost ; Yes, jump 4.223 + jmp game_loop ; No, repeat game loop 4.224 + 4.225 + ; 4.226 + ; Ball lost 4.227 + ; 4.228 +ball_lost: 4.229 + mov cx,10846 ; 1193180 / 110 4.230 + call speaker ; Generate sound 4.231 + 4.232 + mov word [si],0 ; Erase ball 4.233 + dec byte [bp+balls] ; One ball less 4.234 + js .1 ; All finished? Yes, jump 4.235 + jmp another_ball ; Start another ball 4.236 + 4.237 +.1: call wait_frame.2 ; Turn off sound 4.238 +exit: 4.239 + mov ax,0x0003 ; Text mode 80x25x16 colors 4.240 + int 0x10 ; Setup 4.241 + int 0x20 ; Exit to DOS / bootOS 4.242 + jmp start 4.243 + 4.244 +wait_frame: 4.245 +.0: 4.246 + mov ah,0x00 ; Read ticks 4.247 + int 0x1a ; Call BIOS 4.248 + cmp dx,[bp+old_time] ; Wait for change 4.249 + je .0 4.250 + mov [bp+old_time],dx 4.251 + 4.252 + dec byte [bp+beep] ; Decrease time to turn off beep 4.253 + jne .1 4.254 +.2: 4.255 + in al,0x61 4.256 + and al,0xfc ; Turn off 4.257 + out 0x61,al 4.258 +.1: 4.259 + 4.260 + ret 4.261 + 4.262 + ; 4.263 + ; Generate sound on PC speaker 4.264 + ; 4.265 +speaker: 4.266 + mov al,0xb6 ; Setup timer 2 4.267 + out 0x43,al 4.268 + mov al,cl ; Low byte of timer count 4.269 + out 0x42,al 4.270 + mov al,ch ; High byte of timer count 4.271 + out 0x42,al 4.272 + in al,0x61 4.273 + or al,0x03 ; Connect PC speaker to timer 2 4.274 + out 0x61,al 4.275 + mov byte [bp+beep],3 ; Duration 4.276 + ret 4.277 + 4.278 + ; 4.279 + ; Locate ball on screen 4.280 + ; 4.281 +locate_ball: 4.282 + mov al,0xa0 4.283 + mul ah ; AH = Y coordinate (row) 4.284 + mov bl,bh ; BH = X coordinate (column) 4.285 + mov bh,0 4.286 + shl bx,1 4.287 + add bx,ax 4.288 + ret 4.289 + 4.290 + ; 4.291 + ; Update score indicator (from bootRogue) 4.292 + ; 4.293 +update_score: 4.294 + mov bx,0x0f98 ; Point to bottom right corner 4.295 + mov ax,[bp+score] 4.296 + call .1 4.297 + mov al,[bp+balls] 4.298 +.1: 4.299 + xor cx,cx ; CX = Quotient 4.300 +.2: inc cx 4.301 + sub ax,10 ; Division by subtraction 4.302 + jnc .2 4.303 + add ax,0x0a3a ; Convert remainder to ASCII digit + color 4.304 + call .3 ; Put on screen 4.305 + xchg ax,cx 4.306 + dec ax ; Quotient is zero? 4.307 + jnz .1 ; No, jump to show more digits. 4.308 + 4.309 +.3: mov [bx],ax 4.310 + dec bx 4.311 + dec bx 4.312 + ret 4.313 + 4.314 + times 510-($-$$) db 0x4f 4.315 + db 0x55,0xaa ; Make it a bootable sector
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/bootfbird/receipt Wed Sep 20 13:08:44 2023 +0000 5.3 @@ -0,0 +1,35 @@ 5.4 +# SliTaz package receipt. 5.5 + 5.6 +PACKAGE="bootfbird" 5.7 +VERSION="slitaz" 5.8 +CATEGORY="games" 5.9 +SHORT_DESC="Bootable flappy bird game in a 512-byte boot sector." 5.10 +MAINTAINER="pascal.bellard@slitaz.org" 5.11 +LICENSE="unknown" 5.12 +#TARBALL="fbird.asm" 5.13 +WEB_SITE="https://github.com/nanochess/fbird" 5.14 +#WGET_URL="https://github.com/nanochess/fbird/raw/6c96031645d57b3957f1b7f892d6aa2d14cd65d9/fbird.asm" 5.15 +TARGET="i486" 5.16 + 5.17 +BUILD_DEPENDS="nasm" 5.18 + 5.19 +# Rules to configure and make the package. 5.20 +compile_rules() 5.21 +{ 5.22 + mkdir -p $src 5.23 + nasm -f bin $stuff/fbird.asm -o $src/fbird.img -l $src/fbird.lst 5.24 +} 5.25 + 5.26 +# Rules to gen a SliTaz package suitable for Tazpkg. 5.27 +genpkg_rules() 5.28 +{ 5.29 + mkdir -p $fs/boot 5.30 + cp $src/fbird.img $fs/boot/$PACKAGE 5.31 +} 5.32 + 5.33 +# Post install/remove commands for Tazpkg. 5.34 +post_install() 5.35 +{ 5.36 + grep -qs ^bootfbird $1/boot/bootmenu || 5.37 + echo "bootfbird fbird Bootable flappy bird game (may run under DOS if renamed to fbird.com)" >> $1/boot/bootmenu 5.38 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/bootfbird/stuff/fbird.asm Wed Sep 20 13:08:44 2023 +0000 6.3 @@ -0,0 +1,289 @@ 6.4 + ; 6.5 + ; F-bird text game in a bootsector 6.6 + ; 6.7 + ; by Oscar Toledo G. 6.8 + ; http://nanochess.org/ 6.9 + ; 6.10 + ; Creation date: Jun/04/2017. A messy unoptimized thing. 6.11 + ; Revision date: Jun/05/2017. Better usage of graphic charset. 6.12 + ; Removed a non-8088 long jump. Added 6.13 + ; sound. Solved bug when overwriting 6.14 + ; previous score. 6.15 + ; 6.16 + 6.17 + use16 6.18 + cpu 8086 6.19 + 6.20 +restart: 6.21 + mov ax,0x0002 ; Set 80x25 text mode 6.22 + int 0x10 ; Call BIOS 6.23 + ; 6.24 + ; Game restart 6.25 + ; 6.26 +fb21: cld ; Reset direction flag (so stosw increments registers) 6.27 + mov ax,0xb800 ; Point to video segment 6.28 + mov ds,ax ; Both the source (common access) 6.29 + mov es,ax ; and target segments 6.30 + mov di,pipe ; Init variables in video segment (saves big bytes) 6.31 + cbw 6.32 + stosw ; pipe 6.33 + stosw ; score 6.34 + stosw ; grav 6.35 + mov al,0xa0 6.36 + stosw ; next 6.37 + mov al,0x60 6.38 + stosw ; bird 6.39 + 6.40 + mov di,0x004a ; Game title 6.41 + mov ax,0x0f46 ; 'F' in white, good old ASCII 6.42 + stosw 6.43 + mov al,0x2d ; '-' 6.44 + stosw 6.45 + mov al,0x42 ; 'B' 6.46 + stosw 6.47 + mov al,0x49 ; 'I' 6.48 + stosw 6.49 + mov al,0x52 ; 'R' 6.50 + stosw 6.51 + mov al,0x44 ; 'D' 6.52 + stosw 6.53 + mov cx,80 ; Introduce 80 columns of scenery 6.54 +fb1: push cx 6.55 + call scroll_scenery 6.56 + pop cx 6.57 + loop fb1 6.58 + 6.59 +fb23: mov ah,0x01 ; Check if key pressed 6.60 + int 0x16 6.61 + pushf 6.62 + xor ax,ax ; Wait for a key 6.63 + int 0x16 6.64 + popf 6.65 + jnz fb23 ; Jump if key was accumulated, if not already waited for key ;) 6.66 + 6.67 + ; 6.68 + ; Main loop 6.69 + ; 6.70 +fb12: mov si,bird 6.71 + lodsw ; Bird falls... 6.72 + add al,[grav] ; ...because of gravity... 6.73 + mov [bird],al ; ...into new position. 6.74 + and al,0xf8 ; Row is a 5.3 fraction, nullify fraction 6.75 + mov ah,0x14 ; Given integer is x8, multiply by 20 to get 160 per line 6.76 + mul ah ; Row into screen 6.77 + add ax,$0020 ; Fixed column 6.78 + xchg ax,di ; Pass to DI (AX cannot be used as pointer) 6.79 + lodsb ; mov al,[frame] 6.80 + and al,4 ; Wing movement each 4 frames 6.81 + jz fb15 6.82 + mov al,[di-160] ; Get character below 6.83 + mov word [di-160],$0d1e ; Draw upper wing 6.84 + add al,[di] ; Add another character below 6.85 + shr al,1 ; Normalize 6.86 + mov word [di],$0d14 ; Draw body 6.87 + jmp short fb16 6.88 + 6.89 +fb15: mov al,[di] ; Get character below 6.90 + mov word [di],$0d1f ; Draw body 6.91 +fb16: add al,[di+2] ; Get character below head 6.92 + mov word [di+2],$0d10 ; Draw head 6.93 + cmp al,0x40 ; Collision with scenery? 6.94 + jz fb19 6.95 + ; 6.96 + ; Stars and game over 6.97 + ; 6.98 + mov byte [di],$2a ; '*' Asterisks to indicate crashing 6.99 + mov byte [di+2],$2a 6.100 + mov di,0x07CA 6.101 + mov ax,0x0f42 ; 'B' in white, good old ASCII 6.102 + stosw 6.103 + mov al,0x4F ; 'O' 6.104 + stosw 6.105 + mov al,0x4E ; 'N' 6.106 + stosw 6.107 + mov al,0x4B ; 'K' 6.108 + stosw 6.109 + mov al,0x21 ; '!' 6.110 + stosw 6.111 + mov cx,100 ; Wait 100 frames 6.112 +fb20: push cx 6.113 + call wait_frame 6.114 + pop cx 6.115 + loop fb20 6.116 + jmp fb21 ; Restart 6.117 + 6.118 +fb19: call wait_frame ; Wait for frame 6.119 + mov al,[frame] 6.120 + and al,7 ; 8 frames have passed? 6.121 + jnz fb17 ; No, jump 6.122 + inc word [grav] ; Increase gravity 6.123 +fb17: 6.124 + mov al,$20 6.125 + mov [di-160],al ; Delete bird from screen 6.126 + mov [di+2],al 6.127 + stosb 6.128 + call scroll_scenery ; Scroll scenery 6.129 + call scroll_scenery ; Scroll scenery 6.130 + cmp byte [0x00a0],0xb0 ; Passed a column? 6.131 + jz fb27 6.132 + cmp byte [0x00a2],0xb0 ; Passed a column? 6.133 +fb27: jnz fb24 6.134 + inc word [score] ; Increase score 6.135 + mov ax,[score] 6.136 + mov di,0x008e ; Show current score 6.137 +fb25: xor dx,dx ; Extend AX to 32 bits 6.138 + mov bx,10 ; Divisor is 10 6.139 + div bx ; Divide 6.140 + add dx,0x0c30 ; Convert remaining 0-9 to ASCII, also put color 6.141 + xchg ax,dx 6.142 + std 6.143 + stosw 6.144 + mov byte [di],0x20 ; Clean at least one character of prev. score 6.145 + cld 6.146 + xchg ax,dx 6.147 + or ax,ax ; Score digits still remain? 6.148 + jnz fb25 ; Yes, jump 6.149 +fb24: mov ah,0x01 ; Any key pressed? 6.150 + int 0x16 6.151 + jz fb26 ; No, go to main loop 6.152 + mov ah,0x00 6.153 + int 0x16 ; Get key 6.154 + dec ah ; Escape key? 6.155 + jne fb4 ; No, jump 6.156 + mov al,3 6.157 + int 0x10 6.158 + int 0x20 ; Exit to DOS or to oblivion (boot sector) 6.159 + jmp restart 6.160 +fb4: mov ax,[bird] 6.161 + sub ax,0x10 ; Move bird two rows upward 6.162 + cmp ax,0x08 ; Make sure the bird doesn't fly free outside screen 6.163 + jb fb18 6.164 + mov [bird],ax 6.165 +fb18: mov byte [grav],0 ; Reset gravity 6.166 + mov al,0xb6 ; Flap sound 6.167 + out (0x43),al 6.168 + mov al,0x90 6.169 + out (0x42),al 6.170 + mov al,0x4a 6.171 + out (0x42),al 6.172 + in al,(0x61) 6.173 + or al,0x03 ; Turn on sound 6.174 + out (0x61),al 6.175 +fb26: jmp fb12 6.176 + 6.177 + ; 6.178 + ; Scroll scenery one column at a time 6.179 + ; 6.180 +scroll_scenery: 6.181 + ; 6.182 + ; Move whole screen 6.183 + ; 6.184 + mov si,0x00a2 ; Point to row 1, column 1 in SI 6.185 + mov di,0x00a0 ; Point to row 1, column 0 in DI 6.186 +fb2: mov cx,79 ; 79 columns 6.187 + repz ; Scroll!!! 6.188 + movsw 6.189 + mov ax,0x0e20 ; Clean last character 6.190 + stosw 6.191 + lodsw ; Advance source to keep pair source/target 6.192 + cmp si,0x0fa2 ; All scrolled? 6.193 + jnz fb2 ; No, jump 6.194 + ; 6.195 + ; Insert houses 6.196 + ; 6.197 + mov word [0x0f9e],0x02df ; Terrain 6.198 + in al,(0x40) ; Get "random" number 6.199 + and al,0x70 6.200 + jz fb5 6.201 + mov bx,0x0408 ; House of one floor 6.202 + mov [0x0efe],bx 6.203 + mov di,0x0e5e 6.204 + and al,0x20 ; Check "random" number 6.205 + jz fb3 6.206 + mov [di],bx ; House of two floors 6.207 + sub di,0x00a0 6.208 +fb3: mov word [di],0x091e ; Add roof 6.209 + ; 6.210 + ; Check if it's time to insert a column 6.211 + ; 6.212 +fb5: dec word [next] ; Decrease time (column really) for next pipe 6.213 + mov bx,[next] 6.214 + cmp bx,0x03 ; bx = 3,2,1,0 for the four columns making the pipe 6.215 + ja fb6 6.216 + jne fb8 6.217 + in al,(0x40) ; Get "random" number 6.218 + and ax,0x0007 ; Between 0 and 7 6.219 + add al,0x04 ; Between 4 and 11 6.220 + mov [tall],ax ; This will tell how tall the pipe is 6.221 +fb8: mov cx,[tall] 6.222 + or bx,bx ; Rightmost? 6.223 + mov dl,0xb0 6.224 + jz fb7 ; Yes, jump 6.225 + mov dl,0xdb 6.226 + cmp bx,0x03 ; Leftmost? 6.227 + jb fb7 ; No, jump 6.228 + mov dl,0xb1 6.229 +fb7: mov di,0x013e ; Start from top of screen 6.230 + mov ah,0x0a 6.231 + mov al,dl 6.232 + mov dx,0x009e 6.233 +fb9: stosw 6.234 + add di,dx 6.235 + loop fb9 6.236 + mov al,0xc4 6.237 + stosw 6.238 + add di,0x009e*6+10 6.239 + mov al,0xdf 6.240 + stosw 6.241 + add di,dx 6.242 +fb10: mov al,dl 6.243 + stosw 6.244 + add di,dx 6.245 + cmp di,0x0f00 6.246 + jb fb10 6.247 + or bx,bx 6.248 + jnz fb6 6.249 + mov ax,[pipe] 6.250 + inc ax ; Increase total pipes shown 6.251 + mov [pipe],ax 6.252 + mov cl,3 6.253 + shr ax,cl 6.254 + mov ah,0x50 ; Decrease distance between pipes 6.255 + sub ah,al 6.256 + cmp ah,0x10 6.257 + ja fb11 6.258 + mov ah,0x10 6.259 +fb11: mov [next],ah ; Time for next pipe 6.260 +fb6: ret 6.261 + 6.262 + ; 6.263 + ; Wait for a frame 6.264 + ; 6.265 +wait_frame: 6.266 + 6.267 + mov bx, tick 6.268 +fb14: mov ah,0x00 ; Use base clock tick 6.269 + int 0x1a 6.270 + cmp dx,[bx] 6.271 + jz fb14 6.272 + mov [bx],dx 6.273 + inc word [bx+frame-tick] ; Increase frame count 6.274 + in al,(0x61) 6.275 + and al,0xfc ; Turn off sound 6.276 + out (0x61),al 6.277 + ret 6.278 + 6.279 + times 507 - ($-$$) db 0 ; pad remaining 507 bytes with zeroes 6.280 + 6.281 + db "OTG" ; 3 unused bytes 6.282 + 6.283 + db 0x55,0xaa ; Bootable signature 6.284 + 6.285 +pipe: equ 0x0fa0 6.286 +score: equ 0x0fa2 6.287 +grav: equ 0x0fa4 6.288 +next: equ 0x0fa6 6.289 +bird: equ 0x0fa8 6.290 +frame: equ 0x0faa 6.291 +tall: equ 0x0fac 6.292 +tick: equ 0x0fae
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/bootinvaders/receipt Wed Sep 20 13:08:44 2023 +0000 7.3 @@ -0,0 +1,35 @@ 7.4 +# SliTaz package receipt. 7.5 + 7.6 +PACKAGE="bootinvaders" 7.7 +VERSION="slitaz" 7.8 +CATEGORY="games" 7.9 +SHORT_DESC="Bootable invaders game in a 512-byte boot sector." 7.10 +MAINTAINER="pascal.bellard@slitaz.org" 7.11 +LICENSE="unknown" 7.12 +#TARBALL="invaders.asm" 7.13 +WEB_SITE="https://github.com/nanochess/Invaders" 7.14 +#WGET_URL="https://github.com/nanochess/Invaders/raw/d27422afdcac01eeec1dd3a887d70ba2ec81238b/invaders.asm" 7.15 +TARGET="i486" 7.16 + 7.17 +BUILD_DEPENDS="nasm" 7.18 + 7.19 +# Rules to configure and make the package. 7.20 +compile_rules() 7.21 +{ 7.22 + mkdir -p $src 7.23 + nasm -f bin $stuff/invaders.asm -o $src/invaders.img -l $src/invaders.lst 7.24 +} 7.25 + 7.26 +# Rules to gen a SliTaz package suitable for Tazpkg. 7.27 +genpkg_rules() 7.28 +{ 7.29 + mkdir -p $fs/boot 7.30 + cp $src/invaders.img $fs/boot/$PACKAGE 7.31 +} 7.32 + 7.33 +# Post install/remove commands for Tazpkg. 7.34 +post_install() 7.35 +{ 7.36 + grep -qs ^bootinvaders $1/boot/bootmenu || 7.37 + echo "bootinvaders invaders Bootable invaders game (may run under DOS if renamed to invaders.com)" >> $1/boot/bootmenu 7.38 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/bootinvaders/stuff/invaders.asm Wed Sep 20 13:08:44 2023 +0000 8.3 @@ -0,0 +1,388 @@ 8.4 + ; 8.5 + ; Invaders in 512 bytes 8.6 + ; 8.7 + ; by Oscar Toledo G. 8.8 + ; 8.9 + ; (c) Copyright 2015-2019 Oscar Toledo G. 8.10 + ; 8.11 + ; Creation: Oct/27/2015. 8.12 + ; Revision: Nov/06/2015. Adjusted bullet collision. Invaders 8.13 + ; accelerate. 8.14 + ; Revision: Apr/03/2019. Invaders now can shoot. Spaceship does 8.15 + ; explosion. 8.16 + ; Revision: May/28/2019. Invaders goes down at 11px instead 12px. 8.17 + ; Now starts another invaders wave more 8.18 + ; difficult. 8.19 + ; Revision: Jun/01/2019. Redesigned for 320x200x256 mode. 8.20 + ; Revision: Jun/02/2019. Now in color. Color carries information 8.21 + ; about thing being hit. 8.22 + ; Revision: Jun/03/2019. Optimized, 601 bytes as COM!!! 8.23 + ; Revision: Jun/04/2019. At last 512 bytes! 8.24 + ; Revision: Jun/05/2019. By popular demand added pure8088 option. Now 8.25 + ; the 8088 version also is bootable! so now 8.26 + ; 8088 is the default. 8.27 + ; Revision: Jun/06/2019. jtsiomb made the point that the COM file 8.28 + ; doesn't need to be 512 bytes, so Esc for 8.29 + ; exiting and also returns to text mode. 8.30 + ; Revision: Jun/29/2019. Now spaceship moves to left pressing Ctrl, 8.31 + ; to right pressing Alt, and shoots pressing 8.32 + ; Shift. Spaceship stops when you depress the 8.33 + ; direction key. To exit you press Scroll 8.34 + ; Lock. Used the extra bytes to implement 8.35 + ; barriers that stop the invaders' bullets. 8.36 + ; (suggested in Reddit by nils-m-holm). 8.37 + ; Revision: Sep/13/2023. Fix bootsector end of game. Position 8.38 + ; independant code. 8.39 + ; 8.40 + 8.41 + cpu 8086 8.42 + 8.43 +base: equ 0xfc80 ; Memory base (same segment as video) 8.44 + 8.45 +shots: equ base+0x00 ; Space to contain 4 shots (2 bytes each one) 8.46 + ; Plus space for a ignored shot (full table) 8.47 + ; Notice (sprites + SPRITE_SIZE) - (shots + 2) 8.48 + ; must be divisible by SPRITE_SIZE. 8.49 +old_time: equ base+0x0c ; Old time 8.50 +level: equ base+0x10 ; Current level number 8.51 +lives: equ base+0x11 ; Current lives 8.52 +sprites: equ base+0x12 ; Space to contain sprite table 8.53 + 8.54 +SHIP_ROW: equ 0x5c*OFFSET_X ; Row of spaceship 8.55 +X_WIDTH: equ 0x0140 ; X-width of video 8.56 +OFFSET_X: equ X_WIDTH*2 ; X-offset between screen rows (2 pixels) 8.57 +SPRITE_SIZE: equ 4 ; Size of each sprite in bytes 8.58 + 8.59 + ; 8.60 + ; All colors different (important to distinguish things) 8.61 + ; 8.62 +SPACESHIP_COLOR: equ 0x1c ; Must be below 0x20 8.63 +BARRIER_COLOR: equ 0x0b 8.64 +SHIP_EXPLOSION_COLOR: equ 0x0a 8.65 +INVADER_EXPLOSION_COLOR: equ 0x0e 8.66 +BULLET_COLOR: equ 0x0c 8.67 +START_COLOR: equ ((sprites+SPRITE_SIZE-(shots+2))/SPRITE_SIZE+0x20) 8.68 + 8.69 +restart_game: 8.70 + mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA) 8.71 + int 0x10 ; Call BIOS 8.72 + cld 8.73 + mov ax,0xa000 ; Point to screen memory 8.74 + mov ds,ax ; Both DS... 8.75 + mov es,ax ; ...and ES 8.76 + mov ah,0x04 8.77 + mov [level],ax ; Level = 0, Lives = 4 8.78 + mov cx,level/2 ; Clear screen and variables (except level/lives) 8.79 + xor di,di 8.80 + mul di ; Clear ax and dx 8.81 + rep 8.82 + stosw ; ch is zero from here 8.83 + 8.84 + ; 8.85 + ; Setup descend state 8.86 + ; 8.87 + mov ax,[di] ; al now contains level, ah contains lives 8.88 + inc ax ; Increase by 2 (so invaders descend correctly) 8.89 + inc ax 8.90 + stosw ; Advance level 8.91 + mov ah,al 8.92 + xchg ax,dx ; Shouldn't damage DX starting here 8.93 + 8.94 + ; 8.95 + ; Setup the spaceship 8.96 + ; 8.97 + mov ah,SPACESHIP_COLOR 8.98 + stosw 8.99 + mov ax,SHIP_ROW+0x4c*2 8.100 + stosw 8.101 + ; 8.102 + ; Setup the invaders 8.103 + ; 8.104 + mov ax,0x08*OFFSET_X+0x28 8.105 + mov bx,START_COLOR*0x0100+0x10 8.106 +in1: mov cl,0x0b ; Eleven invaders per row 8.107 +in5: stosw ; Set invader position 8.108 + add ax,0x0b*2 ; Go to next column 8.109 + xchg ax,bx 8.110 + stosw ; Set invader color and shape 8.111 + inc ah ; Go to next color 8.112 + xchg ax,bx 8.113 + loop in5 ; Loop and also make sure ch is zero 8.114 + add ax,0x09*OFFSET_X-0x000b*0x000b*2 ; Go to next row 8.115 + cmp bh,START_COLOR+55 ; Whole board finished? 8.116 + jne in1 ; No, jump 8.117 + 8.118 + ; 8.119 + ; Draw the barriers 8.120 + ; 8.121 + mov di,0x55*0x280+0x10*2 8.122 + mov cl,5 8.123 +in48: 8.124 + mov ax,BARRIER_COLOR*0x0100+0x04 8.125 + call draw_sprite 8.126 + add di,0x1e*2 8.127 + loop in48 8.128 + 8.129 + ; CH is zero 8.130 + 8.131 +in14: 8.132 + mov si,sprites+SPRITE_SIZE 8.133 + 8.134 + ; 8.135 + ; Game loop 8.136 + ; 8.137 + ; Globals: 8.138 + ; SI = Next invader to animate 8.139 + ; DL = state (0=left, 1=right, >=2 down) 8.140 + ; DH = nstate (next state) 8.141 + ; CH = dead invaders 8.142 + ; BP = frame counter 8.143 + ; 8.144 +in46: 8.145 + cmp byte [si+2],0x20 ; Current invader is cosmic debris? 8.146 + jc in2 ; No, jump 8.147 + inc ch ; Count another dead invader 8.148 + cmp ch,55 ; All invaders defeated? 8.149 + je restart_game ; Yes, jump. 8.150 + ; 8.151 + ; Yes, invaders speed up 8.152 + ; 8.153 +in6: 8.154 + lodsw ; Load position in AX 8.155 + xchg ax,di ; Move to DI 8.156 + lodsw ; Get type of sprite 8.157 + cmp al,0x20 ; Explosion? 8.158 + ja in27 ; Destroyed? jump 8.159 + jne in29 ; No explosion, jump 8.160 + mov byte [si-2],0x28 ; Don't draw again 8.161 +in29: call draw_sprite ; Draw invader on screen 8.162 +in27: cmp si,sprites+56*SPRITE_SIZE ; Whole board revised? 8.163 + jne in46 ; No, jump 8.164 + mov al,dh 8.165 + sub al,2 ; Going down? 8.166 + jc in14 ; No, preserve left/right direction 8.167 + xor al,1 ; Switch direction 8.168 + mov dl,al 8.169 + mov dh,al 8.170 + jmp in14 8.171 + 8.172 +in2: 8.173 + xor byte [si+2],8 ; Invader animation (before possible explosion) 8.174 + ; 8.175 + ; Synchronize game to 18.20648 hz. of BIOS 8.176 + ; 8.177 + inc bp 8.178 + and bp,7 ; Each 8 invaders 8.179 + push dx 8.180 + push si 8.181 + push bp 8.182 + mov si,shots ; Point to shots list 8.183 + jne in12 8.184 +in22: 8.185 + mov ah,0x00 8.186 + int 0x1a ; BIOS clock read 8.187 + cmp dx,[si+old_time-shots] ; Wait for change 8.188 + je in22 8.189 + mov [si+old_time-shots],dx ; Save new current time 8.190 +in12: 8.191 + %if 1 8.192 + ; 8.193 + ; Handle player bullet 8.194 + ; 8.195 + mov cx,4 ; 4 shots at most 8.196 + lodsw ; Read position (player) 8.197 + cmp ax,X_WIDTH ; Is it at top of screen? 8.198 + xchg ax,di 8.199 + jc in31 ; Erase bullet 8.200 + ; Doesn't mind doing it all time 8.201 + call zero ; Remove bullet 8.202 + sub di,X_WIDTH+2 8.203 + mov al,[di] ; Read pixel 8.204 + sub al,0x20 ; Hits invader? 8.205 + jc in30 ; No, jump 8.206 + push si 8.207 + push di 8.208 + mov ah,SPRITE_SIZE ; The pixel indicates the... 8.209 + mul ah ; ...invader hit. 8.210 + add si,ax 8.211 + lodsw 8.212 + xchg ax,di 8.213 + mov byte [si],0x20 ; Erase next time 8.214 + mov ax,INVADER_EXPLOSION_COLOR*0x0100+0x08 ; But explosion now 8.215 + call draw_sprite ; Draw sprite 8.216 + pop di 8.217 + pop si 8.218 + jmp in31 8.219 + 8.220 + ; 8.221 + ; Handle invader bullets 8.222 + ; 8.223 +in24: 8.224 + lodsw ; Read current coordinate 8.225 + or ax,ax ; Is it falling? 8.226 + je in23 ; No, jump 8.227 + cmp ax,0x60*OFFSET_X ; Pixel lower than spaceship? 8.228 + xchg ax,di 8.229 + jnc in31 ; Yes, remove bullet 8.230 + call zero ; Remove bullet 8.231 + add di,X_WIDTH-2 ; Bullet falls down 8.232 + 8.233 + ; Draw bullet 8.234 +in30: 8.235 + mov al,BULLET_COLOR 8.236 + mov [si-2],di ; Update position of bullet 8.237 + cmp byte [di+X_WIDTH],BARRIER_COLOR ; Barrier in path? 8.238 + jne in7 ; Yes, erase bullet and barrier pixel 8.239 + 8.240 + ; Remove bullet 8.241 +in31: xor ax,ax ; AX contains zero (DI unaffected) 8.242 + mov [si-2],ax ; Delete bullet from table 8.243 + 8.244 +in7: cmp byte [di],SPACESHIP_COLOR ; Check collision with player 8.245 + jne in41 ; No, jump 8.246 + mov word [sprites],SHIP_EXPLOSION_COLOR*0x0100+0x38 ; Player explosion 8.247 +in41: 8.248 + call big_pixel ; Draw/erase bullet 8.249 +in23: loop in24 8.250 + %endif 8.251 + 8.252 + ; 8.253 + ; Spaceship handling 8.254 + ; 8.255 + mov si,sprites ; Point to spaceship 8.256 + lodsw ; Load sprite frame / color 8.257 + or al,al ; Explosion? 8.258 + je in42 ; No, jump 8.259 + add al,0x08 ; Keep explosion 8.260 + jne in42 ; Finished? No, jump 8.261 + mov ah,SPACESHIP_COLOR ; Restore color (sprite already) 8.262 + dec byte [si-2-sprites+lives] ; Remove one life 8.263 + js in10 ; Exit if all used 8.264 +in42: mov [si-2],ax ; Save new frame / color 8.265 + mov di,[si] ; Load position 8.266 + call draw_sprite ; Draw sprite (spaceship) 8.267 + jne in43 ; Jump if still explosion 8.268 + 8.269 + mov ah,0x02 ; BIOS Get Keyboard Flags 8.270 + int 0x16 8.271 + test al,0x10 ; Test for Scroll Lock and exit 8.272 + jnz in10 8.273 + 8.274 + mov bx,12 ; Ctrl/Alt key? 8.275 + and bl,al 8.276 + jp in17 ; test Ctrl xor Alt 8.277 + lea di,[bx+di-6] ; Move 2 pixels to left/right 8.278 +in17: 8.279 + test al,0x03 ; Shift keys? 8.280 + jz in35 ; No, jump 8.281 + cmp word [shots],bx ; Bullet available? 8.282 + ja in35 ; No, jump 8.283 + lea ax,[di+(0x04*2)] ; Offset from spaceship 8.284 + mov [shots],ax ; Start bullet 8.285 +in35: 8.286 + xchg ax,di 8.287 + cmp ax,SHIP_ROW-2 ; Update if not touching border 8.288 + je in43 8.289 + cmp ax,SHIP_ROW+0x0132 8.290 + je in43 8.291 +in19: mov [si],ax ; Update position 8.292 +in43: 8.293 + pop bp 8.294 + pop si 8.295 + pop dx 8.296 + 8.297 + mov cx,4 ; ch = 0 - invader alive 8.298 + mov ax,[si] ; Get position of current invader 8.299 + cmp dl,1 ; Going down (state 2)? 8.300 + jbe in9 ; No, jump 8.301 + add ax,0x0280 ; Go down by 2 pixels 8.302 + cmp ax,0x55*0x280 ; Reaches Earth? 8.303 + jc in8 ; No, jump 8.304 +in10: 8.305 + mov ax,0x0003 ; Restore text mode 8.306 + int 0x10 8.307 + int 0x20 ; Exit to DOS 8.308 + jmp restart_game 8.309 + 8.310 +in9: dec ax ; Moving to left 8.311 + dec ax 8.312 + jc in20 8.313 + add ax,cx ; Moving to right 8.314 +in20: push ax 8.315 + shr ax,1 ; Divide position by 2... 8.316 + mov bl,0xa0 ; ...means we can get column dividing by 0xa0 8.317 + div bl ; ...instead of 0x0140 (longer code) 8.318 + dec ah ; Convert 0x00 to 0xff 8.319 + cmp ah,0x94 ; Border touched? (>= 0x94) 8.320 + pop ax 8.321 + jb in8 ; No, jump 8.322 + or dh,22 ; Goes down by 11 pixels (11 * 2) must be odd 8.323 +in8: mov [si],ax 8.324 + add ax,0x06*0x280+0x03*2 ; Offset for bullet 8.325 + xchg ax,bx 8.326 + 8.327 + in al,(0x40) ; Read timer 8.328 + cmp al,0xfc ; Random event happening? 8.329 + jc in4 ; No, jump 8.330 + 8.331 + mov di,shots 8.332 +in45: scasw ; Advance DI 8.333 + cmp [di],ch ; Search for free slot 8.334 + loopne in45 ; Until 3 slots searched 8.335 + mov [di],bx ; Start invader shot (or put in ignored slot) 8.336 +in4: 8.337 + jmp in6 8.338 + 8.339 + ; 8.340 + ; Draw pixel per Carry (use AX if Carry=1 or zero if Carry=0) 8.341 + ; 8.342 +bit: jc big_pixel 8.343 +zero: xor ax,ax 8.344 + ; Draw a big pixel (2x2 pixels) 8.345 +big_pixel: 8.346 + mov ah, al 8.347 + mov [di+X_WIDTH],ax 8.348 + stosw 8.349 + ret 8.350 + 8.351 + ; ah = sprite color 8.352 + ; al = sprite (x8) 8.353 + ; di = Target address 8.354 +draw_sprite: 8.355 + push cx 8.356 + push di 8.357 + pushf 8.358 +in3: push ax 8.359 + call get_bitmaps 8.360 + ; 8.361 + ; Bitmaps for sprites 8.362 + ; 8.363 +bitmaps: 8.364 + db 0x18,0x18,0x3c,0x24,0x3c,0x7e,0xFf,0x24 ; Spaceship 8.365 + db 0x00,0x80,0x42,0x18,0x10,0x48,0x82,0x01 ; Explosion 8.366 + db 0x00,0xbd,0xdb,0x7e,0x24,0x3c,0x66,0xc3 ; Alien (frame 1) 8.367 + db 0x00,0x3c,0x5a,0xff,0xa5,0x3c,0x66,0x66 ; Alien (frame 2) 8.368 + db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; Erase 8.369 + 8.370 +get_bitmaps: 8.371 + pop bx 8.372 + cs xlat ; Extract one byte from bitmap 8.373 + xchg ax,bx ; bl contains byte, bh contains color 8.374 + mov cx,10 ; Two extra zero pixels at left and right 8.375 + clc ; Left pixel as zero (clean) 8.376 +in0: mov al,bh ; Duplicate color in AX 8.377 + call bit ; Draw pixel 8.378 + shl bl,1 8.379 + loop in0 8.380 + add di,OFFSET_X-20 ; Go to next video line 8.381 + pop ax 8.382 + inc ax ; Next bitmap byte 8.383 + test al,7 ; Sprite complete? 8.384 + jne in3 ; No, jump 8.385 + popf 8.386 + pop di 8.387 + pop cx 8.388 + ret 8.389 + 8.390 + times 510-($-$$) db 0x4f 8.391 + db 0x55,0xaa ; Make it a bootable sector
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/bootmine/receipt Wed Sep 20 13:08:44 2023 +0000 9.3 @@ -0,0 +1,35 @@ 9.4 +# SliTaz package receipt. 9.5 + 9.6 +PACKAGE="bootmine" 9.7 +VERSION="1.1.1-slitaz" 9.8 +CATEGORY="games" 9.9 +SHORT_DESC="Bootable minesweeper game in a 512-byte boot sector." 9.10 +MAINTAINER="pascal.bellard@slitaz.org" 9.11 +LICENSE="MIT" 9.12 +WEB_SITE="https://github.com/io12/BootMine" 9.13 +#TARBALL="$PACKAGE-$VERSION.tar.gz" 9.14 +#WGET_URL="https://github.com/io12/BootMine/archive/refs/tags/$VERSION.tar.gz" 9.15 +TARGET="i486" 9.16 + 9.17 +BUILD_DEPENDS="nasm" 9.18 + 9.19 +# Rules to configure and make the package. 9.20 +compile_rules() 9.21 +{ 9.22 + mkdir -p $src 9.23 + nasm -o $src/mine.com -l $src/mine.lst $stuff/mine.asm 9.24 +} 9.25 + 9.26 +# Rules to gen a SliTaz package suitable for Tazpkg. 9.27 +genpkg_rules() 9.28 +{ 9.29 + mkdir -p $fs/boot 9.30 + cp $src/mine.com $fs/boot/$PACKAGE 9.31 +} 9.32 + 9.33 +# Post install/remove commands for Tazpkg. 9.34 +post_install() 9.35 +{ 9.36 + grep -qs ^bootmine $1/boot/bootmenu || 9.37 + echo "bootmine mine,minesweeper Bootable minesweeper game (may run under DOS if renamed to mine.com)" >> $1/boot/bootmenu 9.38 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/bootmine/stuff/mine.asm Wed Sep 20 13:08:44 2023 +0000 10.3 @@ -0,0 +1,634 @@ 10.4 +bits 16 10.5 + 10.6 +%define UNVEIL_ON_GAME_OVER 10.7 +%define CGA_DISPLAY 10.8 +%define DOS_QUIT 10.9 +%define FAT_BOOT 10.10 + 10.11 +cpu 8086 10.12 + 10.13 +;; Constants 10.14 + 10.15 +;; Boot sector size in bytes 10.16 +%assign BootSector.Size 512 10.17 + 10.18 +;; Words in 16 bit x86 are 2 bytes 10.19 +%assign WordSize 2 10.20 + 10.21 +;; This is the value to store in segment register to access the VGA text buffer. 10.22 +;; In 16 bit x86, segmented memory accesses are of the form: 10.23 +;; 10.24 +;; (segment register) * 0x10 + (offset register) 10.25 +;; 10.26 +;; The VGA text buffer is at 0xb80000, so if 0xb800 is stored in a segment 10.27 +;; register, then memory access instructions will be relative to the VGA text 10.28 +;; buffer, allowing easier access. For example, trying to access the nth byte of 10.29 +;; memory will *actually* access the nth byte of the text buffer. 10.30 +%assign TextBuf.Seg 0xb800 10.31 + 10.32 +;; Dimensions of text buffer 10.33 +%assign TextBuf.Width 40 10.34 +%assign TextBuf.Height 25 10.35 +%assign TextBuf.Size (TextBuf.Width * TextBuf.Height) 10.36 + 10.37 +;; Macro to get the index of a text buffer cell from coordinates 10.38 +%define TextBuf.Index(y, x) ((y) * TextBuf.Width * 2 + (x) * 2) 10.39 + 10.40 +;; Length of Dirs array defined below 10.41 +%assign Dirs.Len 8 10.42 + 10.43 +;; Keyboard scan codes 10.44 +;; http://www.delorie.com/djgpp/doc/rbinter/it/06/0.html 10.45 +%assign Key.ScanCode.Space 0x39 10.46 +%assign Key.ScanCode.Up 0x48 10.47 +%assign Key.ScanCode.Down 0x50 10.48 +%assign Key.ScanCode.Left 0x4b 10.49 +%assign Key.ScanCode.Right 0x4d 10.50 +%assign Key.ScanCode.Enter 0x1c 10.51 + 10.52 +;; Keyboard ASCII codes 10.53 +%assign Key.Ascii.RestartGame 'r' 10.54 +%assign Key.Ascii.QuitGame 'q' 10.55 + 10.56 +;; This is a convenience macro for creating VGA characters. VGA characters are 10.57 +;; 16 bit words, with the lower byte as the ASCII value and the upper byte 10.58 +;; holding the foreground and background colors. 10.59 +%define VgaChar(color, ascii) (((color) << 8) | (ascii)) 10.60 + 10.61 +;; VGA colors to use for game items 10.62 +;; https://wiki.osdev.org/Text_UI#Colours 10.63 +%assign Color.Veiled 0x77 10.64 +%assign Color.Unveiled 0xf0 10.65 +%assign Color.Cursor 0x00 10.66 +%assign Color.Flag 0xcc 10.67 +%assign Color.GameWinText 0x20 10.68 +%assign Color.GameOverText 0xc0 10.69 + 10.70 +;; This value is used to calculate bomb frequency. The probability that any 10.71 +;; given cell is a bomb is (1/2)^n, where n = "number of ones in the binary 10.72 +;; representation of BombFreq". 10.73 +;; 10.74 +;; In other words, when BombFreq=0, every cell is a bomb, and appending a one 10.75 +;; halves the amount of bombs. 10.76 +%assign BombFreq 0b111 10.77 + 10.78 +%ifdef FAT_BOOT 10.79 + jmp BootMine 10.80 + nop 10.81 + times 0x3B db 0 10.82 +%endif 10.83 + 10.84 +;; BootMine is supported as both a DOS game and a boot sector game :) 10.85 + 10.86 +;; Entry point: set up graphics and run game 10.87 +BootMine: 10.88 + cld 10.89 + 10.90 + ; VGA text mode 0x00 10.91 + ; 320x200 pixel resolution 10.92 + ; 40x25 text resolution 10.93 + ; 16 colors 10.94 + ; http://www.delorie.com/djgpp/doc/rbinter/id/74/0.html 10.95 + xor ax, ax 10.96 + int 0x10 10.97 + 10.98 +%ifdef CGA_DISPLAY 10.99 + ; Toggle intensity/blinking bit 10.100 + ; http://www.techhelpmanual.com/140-int_10h_1003h__select_foreground_blink_or_bold_background.html 10.101 + xor bx, bx ;blinking off 10.102 + mov ds, bx 10.103 + mov ax, 0x1003 ;toggle intensity/blinking bit (Jr, PS, TANDY 1000, EGA, VGA) 10.104 + int 0x10 10.105 + mov si, 0x463 10.106 + lodsw ;get port address for 6845 video controller chip 10.107 + add al, 4 10.108 + xchg ax, dx 10.109 + and byte [si], 0xdf ;mask value by 1101 1111 (to clear bit 5) 10.110 + lodsb ;get new value of Mode Select Register 10.111 + out dx, al ;disable blink (set for bold background) 10.112 +%else 10.113 + ; Disable blinking text 10.114 + ; https://www.reddit.com/r/osdev/comments/70fcig/blinking_text/dn2t6u8?utm_source=share&utm_medium=web2x 10.115 + ; Read I/O Address 0x03DA to reset index/data flip-flop 10.116 + mov dx, 0x03DA 10.117 + in al, dx 10.118 + ; Write index 0x30 to 0x03C0 to set register index to 0x30 10.119 + mov dl, 0xC0 10.120 + mov al, 0x30 10.121 + out dx, al 10.122 + ; Read from 0x03C1 to get register contents 10.123 + inc dx 10.124 + in al, dx 10.125 + ; Unset Bit 3 to disable Blink 10.126 + and al, 0xF7 10.127 + ; Write to 0x03C0 to update register with changed value 10.128 + dec dx 10.129 + out dx, al 10.130 +%endif 10.131 + 10.132 + ; Load VGA text buffer segment into segment registers 10.133 + mov dx, TextBuf.Seg 10.134 + mov es, dx 10.135 + mov ds, dx 10.136 + 10.137 + ; Disable VGA text mode cursor 10.138 + ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor 10.139 + mov ah, 0x01 10.140 + mov ch, 0x3f 10.141 + int 0x10 10.142 + 10.143 +;; Run game (the game is restarted by jumping here) 10.144 +RunGame: 10.145 + 10.146 +;; Set all cells of game map to veiled '0' cells 10.147 +ZeroTextBuf: 10.148 + xor di, di 10.149 + mov cx, TextBuf.Size 10.150 + mov ax, VgaChar(Color.Veiled, '0') 10.151 + rep stosw 10.152 + 10.153 +%ifndef USE_RDTSC 10.154 + ; Initialyze the simple pseudo-random number generator 10.155 + ; seed = set_system_time() 10.156 + cbw 10.157 + int 0x1a 10.158 + push dx 10.159 +%endif 10.160 + 10.161 +;; Populate text buffer with mines and digits 10.162 +;; 10.163 +;; This is done with a single triple-nested loop. The nested loops iterate over 10.164 +;; y coordinates, then x coordinates, then over the 8 adjacent cells at (y, x). 10.165 +;; 10.166 +;; Inside the 2nd loop level is bomb generation logic. Digit incrementing logic 10.167 +;; is in the 3rd loop level. 10.168 +;; 10.169 +;; Note that the coordinates on the outside border are skipped to avoid bounds 10.170 +;; checking logic. 10.171 +PopulateTextBuf: 10.172 + ; Iterate over y coordinates 10.173 + mov bx, TextBuf.Height - 2 10.174 + 10.175 +.LoopY: 10.176 + ; Iterate over x coordinates. ch = 0 form ZeroTextBuf 10.177 +%ifndef USE_RDTSC 10.178 + mov cx, TextBuf.Width - 2 10.179 +%else 10.180 + mov cl, TextBuf.Width - 2 10.181 +%endif 10.182 + 10.183 +.LoopX: 10.184 + ; di = &TextBuf[y][x] 10.185 + call GetTextBufIndex 10.186 +.si_value: 10.187 + 10.188 + ; The register dl holds a boolean that is 1 if the current cell is a bomb, 0 10.189 + ; otherwise. 10.190 +%ifndef USE_RDTSC 10.191 + ; It is calculated by bitwise and-ing the result of the simple pseudo-random number 10.192 + ; generator seed = ((seed + LARGE_PRIME1) * LARGE_PRIME2) % LARGE_PRIME3 10.193 + ; 10.194 + ; dl = ! (prng() & BombFreq) 10.195 +%assign Prime1 32749 10.196 +%assign Prime2 65519 10.197 +%assign Prime3 65521 10.198 + pop ax 10.199 + add ax, Prime1 10.200 + mov bp, Prime2 10.201 + mul bp 10.202 + inc bp 10.203 + inc bp 10.204 + div bp 10.205 + xchg ax, dx 10.206 + push ax 10.207 +%else 10.208 + ; It is calculated by bitwise and-ing the result of rdtsc. (rdtsc returns the 10.209 + ; amount of CPU cycles since boot, which works okay as a cheap random number 10.210 + ; generator, and it's apparently supported on all x86 CPUs since the Pentium line) 10.211 + ; 10.212 + ; dl = ! (rdtsc() & BombFreq) 10.213 +cpu 686 10.214 + rdtsc 10.215 +cpu 8086 10.216 +%endif 10.217 + and al, BombFreq 10.218 + mov dx, '*' * 256 + 0 10.219 + 10.220 + ; Initialize loop counter for .LoopDir 10.221 + mov bp, Dirs.Len 10.222 + 10.223 + ; If this cell isn't a bomb, then skip marking it as a bomb 10.224 + jnz .LoopDir 10.225 + 10.226 + ; Mark the current cell as a bomb 10.227 + mov byte [di], dh 10.228 + inc dx 10.229 + 10.230 + ; Iterate over adjacent cells (directions) 10.231 +.LoopDir: 10.232 + ; Load adjacent cell offset from Dirs array into ax. 10.233 + mov al, byte [cs:bp + si + Dirs - .si_value - 1] 10.234 + cbw 10.235 + ; Set di = pointer to adjacent cell 10.236 + add di, ax 10.237 + 10.238 + ; If adjacent cell is a bomb, skip digit incrementing 10.239 + cmp byte [di], dh 10.240 + je .LoopDirIsMine 10.241 + ; The adjacent cell is a 0-7 digit and not a bomb. Add dl to the cell, which 10.242 + ; is 1 if the original cell is a bomb. This gradually accumulates to the 10.243 + ; amount of neighboring bombs and represents the number cells in the 10.244 + ; minesweeper game. 10.245 + add [di], dl 10.246 +.LoopDirIsMine: 10.247 + ; Restore di to original cell pointer 10.248 + sub di, ax 10.249 + 10.250 + ; Decrement adjacent direction loop counter and continue if nonzero 10.251 + dec bp 10.252 + jnz .LoopDir 10.253 + 10.254 + ; Decrement x coordinate loop counter and continue if nonzero 10.255 + loop .LoopX 10.256 + 10.257 + ; Decrement y coordinate loop counter and continue if nonzero 10.258 + dec bx 10.259 + jnz .LoopY 10.260 +%ifndef USE_RDTSC 10.261 + pop ax 10.262 +%endif 10.263 + 10.264 +;; Done populating the text buffer 10.265 + 10.266 + ; Set the initial cursor color for game loop. The dl register is now used to 10.267 + ; store the saved cell color that the cursor is on, since the cursor 10.268 + ; overwrites the cell color with the cursor color. 10.269 + mov dl, Color.Veiled 10.270 + 10.271 +;; Main loop to process key presses and update state 10.272 +GameLoop: 10.273 + ; Get keystroke 10.274 + ; ah = BIOS scan code 10.275 + ; al = ASCII character 10.276 + ; http://www.delorie.com/djgpp/doc/rbinter/id/63/17.html 10.277 + xor ax, ax 10.278 + int 0x16 10.279 + 10.280 + ; bx and cx are zeroed from the PopulateTextBuf loops above 10.281 + ; bx = y coord 10.282 + ; cx = x coord 10.283 + 10.284 + ; di = cell pointer 10.285 + call GetTextBufIndex 10.286 + ; Apply saved cell color 10.287 + mov [di + 1], dl 10.288 + 10.289 +;; Detect win (a win occurs when every veiled cell is a mine) 10.290 +DetectWin: 10.291 + ; Use si register as cell pointer for win detection 10.292 + xor si, si 10.293 + ; Use bp as loop counter 10.294 + mov bp, TextBuf.Size 10.295 +.Loop: 10.296 + ; if (char != '*' && (color == Color.Veiled || color == Color.Flag)) { 10.297 + ; break; // Didn't win yet :( 10.298 + ; } 10.299 + ; Load VGA char into al 10.300 + lodsb 10.301 + cmp al, '*' 10.302 + ; Load VGA color into al 10.303 + lodsb 10.304 + je .Continue 10.305 + cmp al, Color.Veiled 10.306 + je Break 10.307 + cmp al, Color.Flag 10.308 + je Break 10.309 +.Continue: 10.310 + dec bp 10.311 + jnz .Loop 10.312 + ; If loop completes without breaking, then we win! :) 10.313 + 10.314 +;; Show game win screen 10.315 +;;GameWin: 10.316 + mov ah, Color.GameWinText 10.317 + call GameEndHelper 10.318 + db 'GAME WIN' 10.319 + 10.320 +;; Wait for restart key to be pressed, then restart game 10.321 +WaitRestartLoop: 10.322 + je RunGame 10.323 +WaitRestart: 10.324 + xor ax, ax 10.325 + int 0x16 10.326 +%ifdef DOS_QUIT 10.327 + cmp al, Key.Ascii.QuitGame 10.328 + jnz .notQuit 10.329 + mov ax,0x0003 ; Restore text mode 10.330 + int 0x10 10.331 + int 0x20 10.332 +.notQuit: 10.333 +%endif 10.334 + cmp al, Key.Ascii.RestartGame 10.335 + jmp WaitRestartLoop 10.336 + 10.337 +;; Array of adjacent cell offsets. A byte in this array can be added to a text 10.338 +;; buffer cell pointer to get the pointer to an adjacent cell. This is used for 10.339 +;; spawning digit cells. 10.340 +Dirs: 10.341 + db TextBuf.Index(-1, -1) 10.342 + db TextBuf.Index(-1, 0) 10.343 + db TextBuf.Index(-1, +1) 10.344 + db TextBuf.Index( 0, +1) 10.345 + db TextBuf.Index(+1, +1) 10.346 + db TextBuf.Index(+1, 0) 10.347 + db TextBuf.Index(+1, -1) 10.348 + db TextBuf.Index( 0, -1) 10.349 + 10.350 +Break: 10.351 + ; Didn't win yet 10.352 + mov al, ah 10.353 + 10.354 +;; Process key press. This is an if-else chain that runs code depending on the 10.355 +;; key pressed. 10.356 +CmpUp: 10.357 + ; Move cursor up 10.358 + dec bx 10.359 + cmp al, Key.ScanCode.Up 10.360 + je WrapCursor 10.361 + inc bx 10.362 +CmpDown: 10.363 + ; Move cursor down 10.364 + inc bx 10.365 + cmp al, Key.ScanCode.Down 10.366 + je WrapCursor 10.367 + dec bx 10.368 +CmpLeft: 10.369 + ; Move cursor left 10.370 + dec cx 10.371 + cmp al, Key.ScanCode.Left 10.372 + je WrapCursor 10.373 + inc cx 10.374 +CmpRight: 10.375 + ; Move cursor right 10.376 + inc cx 10.377 + cmp al, Key.ScanCode.Right 10.378 + je WrapCursor 10.379 + dec cx 10.380 +CmpEnter: 10.381 + cmp al, Key.ScanCode.Enter 10.382 + jne CmpSpace 10.383 + ; Place flag by coloring current cell 10.384 + mov dl, Color.Flag 10.385 + mov [di + 1], dl 10.386 +; jmp GameLoop 10.387 +CmpSpace: 10.388 + cmp al, Key.ScanCode.Space 10.389 + jne GameLoop 10.390 + 10.391 +;; If the player pressed space, clear the current cell 10.392 +ClearCell: 10.393 + ; Set ax = cell value 10.394 + mov ax, [di] 10.395 + call UnveilCell 10.396 +;; If-else chain checking the cell value 10.397 +.CmpEmpty: 10.398 + cmp al, '0' 10.399 + jne .CmpMine 10.400 + ; If cell is empty, run flood fill algorithm 10.401 + call Flood 10.402 +.jmpGameLoop: 10.403 + jmp GameLoop 10.404 +.CmpMine: 10.405 + cmp al, '*' 10.406 + ; No handling needed if cell is digit 10.407 + jne .jmpGameLoop 10.408 + ; If cell is bomb, game over :( 10.409 + 10.410 +;; Show game over screen 10.411 + 10.412 +%ifdef UNVEIL_ON_GAME_OVER 10.413 + mov cx, TextBuf.Size 10.414 + xor si, si 10.415 +.Loop: 10.416 + ; Load VGA character into ax 10.417 + lodsw 10.418 + cmp al, '*' 10.419 + jne .Next 10.420 + and byte [si-1], Color.Unveiled 10.421 +.Next: 10.422 + loop .Loop 10.423 +%endif 10.424 +;;GameOver: 10.425 + mov ah, Color.GameOverText 10.426 + call GameEndHelper 10.427 +GameOverStr: 10.428 + db 'GAME OVER' 10.429 +%assign GameOverStr.Len $ - GameOverStr 10.430 + 10.431 +;; Helper code for GameWin and GameOver; print a string in the center of the 10.432 +;; text buffer, then wait for game to be restarted. 10.433 +GameEndHelper: 10.434 + pop si 10.435 + mov di, TextBuf.Index(TextBuf.Height / 2, TextBuf.Width / 2 - GameOverStr.Len / 2) 10.436 +.Loop: 10.437 + cs lodsb 10.438 + cmp al, 'Z' 10.439 + ja WaitRestart 10.440 + stosw 10.441 + jmp .Loop 10.442 + 10.443 +;; Set y and x coordinates of cursor to zero if they are out of bounds 10.444 +WrapCursor: 10.445 +.Y: 10.446 + ; Wrap y cursor 10.447 + cmp bx, TextBuf.Height 10.448 + jb .X 10.449 + xor bx, bx 10.450 + 10.451 +.X: 10.452 + ; Wrap x cursor 10.453 + cmp cx, TextBuf.Width 10.454 + jb SetCursorPos 10.455 + xor cx, cx 10.456 + 10.457 +;; Redraw cursor in new position 10.458 +SetCursorPos: 10.459 + ; Get text buffer index (it changed) 10.460 + call GetTextBufIndex 10.461 + ; Draw cursor by changing cell to the cursor color, but save current color for 10.462 + ; restoring in the next iteration of the game loop. 10.463 + mov dl, Color.Cursor 10.464 + xchg dl, [di + 1] 10.465 + 10.466 + jmp ClearCell.jmpGameLoop 10.467 + 10.468 +;; Compute the text buffer index from y and x coordinates 10.469 +;; 10.470 +;; di = &TextBuf[bx = y][cx = x] 10.471 +;; 10.472 +;; This computes the equivalent of the TextBuf.Index(y, x) macro, but at runtime 10.473 +;; 10.474 +;; Parameters: 10.475 +;; * bx - y coordinate 10.476 +;; * cx - x coordinate 10.477 +;; Returns: 10.478 +;; * di - text buffer index 10.479 +;; * si - caller return address 10.480 +GetTextBufIndex: 10.481 + xchg ax, di 10.482 + mov al, TextBuf.Width * 2 10.483 + imul bl 10.484 + xchg ax, di 10.485 + add di, cx 10.486 + add di, cx 10.487 + ; load caller return address in si 10.488 + pop si 10.489 + push si 10.490 + ret 10.491 + 10.492 +;; Unveil a cell so it is visible on the screen 10.493 +;; 10.494 +;; Parameters: 10.495 +;; * di - cell pointer in text buffer 10.496 +;; * al - cell ASCII value 10.497 +;; Returns: 10.498 +;; * dl - written VGA color code 10.499 +UnveilCell: 10.500 + ; TLDR: Use xor magic to make the cells colored. 10.501 + ; 10.502 + ; We have three cases to consider: 10.503 + ; 10.504 + ; Case 1: the cell is a digit from 1-8 10.505 + ; 10.506 + ; The ASCII values '1', '2', '3', ..., '8' are 0x31, 0x32, 0x33, ..., 0x38. In 10.507 + ; other words, the upper nibble is always 0x3 and the lower nibble is the 10.508 + ; digit. We want the VGA color code to be `Color.Unveiled | digit`. For 10.509 + ; example, the color of a '3' cell would be 0xf3. 10.510 + ; 10.511 + ; We can accomplish this with the formula `cell_value ^ '0' ^ Color.Unveiled`. 10.512 + ; Xor-ing by '0' (0x30), clears the upper nibble of the cell value, leaving 10.513 + ; just the digit value. Xor-ing again by Color.Unveiled sets the upper nibble 10.514 + ; to 0xf, leading to the value `Color.Unveiled | digit`. 10.515 + ; 10.516 + ; Since xor is associative, this can be done in one operation, by xor-ing the 10.517 + ; cell value by ('0' ^ Color.Unveiled). 10.518 + ; 10.519 + ; Case 2: the cell is a bomb 10.520 + ; 10.521 + ; We don't really care about this case as long as the bomb is visible against 10.522 + ; the background. The bomb turns out to be green, oh well. 10.523 + ; 10.524 + ; Case 3: the cell is an empty space 10.525 + ; 10.526 + ; This ends up coloring the cell bright yellow, which isn't a big problem. 10.527 + mov dl, al 10.528 + xor dl, '0' ^ Color.Unveiled 10.529 + mov [di + 1], dl 10.530 + ret 10.531 + 10.532 +;; Flood fill empty cells 10.533 +;; 10.534 +;; Parameters: 10.535 +;; * bx - cell y coordinate 10.536 +;; * cx - cell x coordinate 10.537 +;; Clobbered registers: 10.538 +;; * ax - cell value 10.539 +;; * di - cell pointer in text buffer 10.540 +Flood: 10.541 + ; Init: get cell pointer and value 10.542 + call GetTextBufIndex 10.543 + mov ax, [di] 10.544 + 10.545 + ; Base case: bounds check y 10.546 + cmp bx, TextBuf.Height 10.547 + jae .Ret 10.548 + 10.549 + ; Base case: bounds check x 10.550 + cmp cx, TextBuf.Width 10.551 + jae .Ret 10.552 + 10.553 + cmp al, '0' 10.554 + 10.555 + ; Base case: we visited this cell already or bomb 10.556 + jb .Ret 10.557 + 10.558 + ; Base case: nonempty cell unveiled and stop recursion 10.559 + jne UnveilCell 10.560 + 10.561 + ; Body: unveil empty cell 10.562 + call UnveilCell 10.563 + 10.564 + ; Body: mark cell as visited and empty 10.565 + mov byte [di], ' ' 10.566 + 10.567 + ; Recursive case: flood adjacent cells 10.568 + 10.569 + ; Flood down 10.570 + inc bx 10.571 + call Flood 10.572 + dec bx 10.573 + 10.574 + ; Flood left 10.575 + dec cx 10.576 + call Flood 10.577 + inc cx 10.578 + 10.579 + ; Flood right 10.580 + inc cx 10.581 + call Flood 10.582 + dec cx 10.583 + 10.584 + ; Flood up-left 10.585 + dec cx 10.586 + call .Flood_up 10.587 + inc cx 10.588 + 10.589 + ; Flood up-right 10.590 + inc cx 10.591 + call .Flood_up 10.592 + dec cx 10.593 + 10.594 + ; Flood down-left 10.595 + inc bx 10.596 + dec cx 10.597 + call Flood 10.598 + inc cx 10.599 + dec bx 10.600 + 10.601 + ; Flood down-right 10.602 + inc bx 10.603 + inc cx 10.604 + call Flood 10.605 + dec cx 10.606 + dec bx 10.607 + 10.608 +.Flood_up: 10.609 + ; Flood up 10.610 + dec bx 10.611 + call Flood 10.612 + inc bx 10.613 + 10.614 +.Ret: 10.615 + ret 10.616 + 10.617 + 10.618 +;; Print program size at build time 10.619 +%assign CodeSize $ - $$ 10.620 +%warning Code is CodeSize bytes 10.621 + 10.622 +%ifdef MBR_BOOT 10.623 +%assign PartitionTable 0x1BE 10.624 +%if CodeSize > PartitionTable 10.625 +%assign OverFlow CodeSize - PartitionTable 10.626 +%error Code is OverFlow bytes too large 10.627 +%endif 10.628 +%endif 10.629 + 10.630 +CodeEnd: 10.631 + ; Pad to size of boot sector, minus the size of a word for the boot sector 10.632 + ; magic value. If the code is too big to fit in a boot sector, the `times` 10.633 + ; directive uses a negative value, causing a build error. 10.634 + times (BootSector.Size - WordSize) - CodeSize db 0 10.635 + 10.636 + ; Boot sector magic 10.637 + dw 0xaa55
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/bootpillman/receipt Wed Sep 20 13:08:44 2023 +0000 11.3 @@ -0,0 +1,35 @@ 11.4 +# SliTaz package receipt. 11.5 + 11.6 +PACKAGE="bootpillman" 11.7 +VERSION="slitaz" 11.8 +CATEGORY="games" 11.9 +SHORT_DESC="Bootable graphic pacman game in a 512-byte boot sector." 11.10 +MAINTAINER="pascal.bellard@slitaz.org" 11.11 +LICENSE="unknown" 11.12 +#TARBALL="pillman.asm" 11.13 +WEB_SITE="https://github.com/nanochess/Pillman" 11.14 +#WGET_URL="https://github.com/nanochess/Pillman/raw/d3945524359134f7db890affe64742515b1d25bd/pillman.asm" 11.15 +TARGET="i486" 11.16 + 11.17 +BUILD_DEPENDS="nasm" 11.18 + 11.19 +# Rules to configure and make the package. 11.20 +compile_rules() 11.21 +{ 11.22 + mkdir -p $src 11.23 + nasm -f bin $stuff/pillman.asm -o $src/pillman.img -l $src/pillman.lst 11.24 +} 11.25 + 11.26 +# Rules to gen a SliTaz package suitable for Tazpkg. 11.27 +genpkg_rules() 11.28 +{ 11.29 + mkdir -p $fs/boot 11.30 + cp $src/pillman.img $fs/boot/$PACKAGE 11.31 +} 11.32 + 11.33 +# Post install/remove commands for Tazpkg. 11.34 +post_install() 11.35 +{ 11.36 + grep -qs ^bootpillman $1/boot/bootmenu || 11.37 + echo "bootpillman pillman Bootable pillman game (may run under DOS if renamed to pillman.com)" >> $1/boot/bootmenu 11.38 +}
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/bootpillman/stuff/pillman.asm Wed Sep 20 13:08:44 2023 +0000 12.3 @@ -0,0 +1,402 @@ 12.4 + ; 12.5 + ; Pillman 12.6 + ; 12.7 + ; by Oscar Toledo G. 12.8 + ; http://nanochess.org/ 12.9 + ; 12.10 + ; (c) Copyright 2019 Oscar Toledo G. 12.11 + ; 12.12 + ; Creation date: Jun/11/2019. 12.13 + ; Revision date: Jun/12/2019. Draws level. 12.14 + ; Revision date: Jun/13/2019. Pillman can move. 12.15 + ; Revision date: Jun/14/2019. Now ghosts don't get stuck. Ghost are 12.16 + ; transparent. Pillman doesn't leave 12.17 + ; trash. 12.18 + ; Revision date: Jun/15/2019. Ghosts can catch pillman. Optimized. 12.19 + ; 509 bytes. 12.20 + ; Revision date: Jul/09/2019. Self-modifying code, move subroutine, 12.21 + ; cache routine address (Peter Ferrie). 12.22 + ; 504 bytes. 12.23 + ; Revision date: Jul/22/2019. Added Esc key to exit. 12.24 + ; 12.25 + ; Revision date: Sep/11/2023. Remove screen garbage, setup stack, 12.26 + ; position independant code, reset screen 12.27 + ; at exit, slow down Pillman blink. 12.28 + ; 12.29 + 12.30 + cpu 8086 12.31 + 12.32 +base: equ 0xfa00 ; Memory base (same segment as video) 12.33 +pos1: equ base 12.34 +intended_dir: equ pos1+20 ; Next direction for player 12.35 + 12.36 +X_OFFSET: equ 0x0140 12.37 + 12.38 + ; 12.39 + ; Maze should start at x,y coordinate multiple of 8 12.40 + ; 12.41 +BASE_MAZE: equ 16*X_OFFSET+32 12.42 + 12.43 +MAZE_COLOR: equ 0x37 ; No color should be higher or equal value 12.44 +PILL_COLOR: equ 0x02 ; Color for pill 12.45 +PLAYER_COLOR: equ 0x0e ; Should be unique 12.46 + 12.47 + ; 12.48 + ; XOR combination of these plus PILL_COLOR shouldn't 12.49 + ; result in PLAYER_COLOR 12.50 + ; 12.51 +GHOST1_COLOR: equ 0x21 ; Ghost 1 color 12.52 +GHOST2_COLOR: equ 0x2e ; Ghost 2 color 12.53 +GHOST3_COLOR: equ 0x28 ; Ghost 3 color 12.54 +GHOST4_COLOR: equ 0x34 ; Ghost 4 color 12.55 + 12.56 +old_time: 12.57 + cld 12.58 + push cs 12.59 +frame: 12.60 + pop ss 12.61 +restart: 12.62 + xor sp,sp 12.63 + 12.64 + mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA) 12.65 + int 0x10 ; Call BIOS 12.66 + mov ax,0xa000 ; Video segment 12.67 + mov ds,ax ; Use as source data segment 12.68 + mov es,ax ; Use as target data segment 12.69 + 12.70 + call get_tables 12.71 +tables: 12.72 + ; 12.73 + ; Ghost colors 12.74 + ; 12.75 +ghost_colors: 12.76 + db GHOST4_COLOR,GHOST3_COLOR,GHOST2_COLOR,GHOST1_COLOR 12.77 + 12.78 + ; 12.79 + ; Maze shape 12.80 + ; 12.81 +maze: 12.82 + dw 0b0000_0000_0000_0000 12.83 + dw 0b0111_1111_1111_1110 12.84 + dw 0b0100_0010_0000_0010 12.85 + dw 0b0100_0010_0000_0010 12.86 + dw 0b0111_1111_1111_1111 12.87 + dw 0b0100_0010_0100_0000 12.88 + dw 0b0111_1110_0111_1110 12.89 + dw 0b0000_0010_0000_0010 12.90 + dw 0b0000_0010_0111_1111 12.91 + dw 0b0000_0011_1100_0000 12.92 + dw 0b0000_0010_0100_0000 12.93 + dw 0b0000_0010_0111_1111 12.94 + dw 0b0000_0010_0100_0000 12.95 + dw 0b0111_1111_1111_1110 12.96 + dw 0b0100_0010_0000_0010 12.97 + dw 0b0111_1011_1111_1111 12.98 + dw 0b0000_1010_0100_0000 12.99 + dw 0b0111_1110_0111_1110 12.100 + dw 0b0100_0000_0000_0010 12.101 + dw 0b0111_1111_1111_1111 12.102 + dw 0b0000_0000_0000_0000 12.103 + 12.104 + ; 12.105 + ; Starting positions 12.106 + ; 12.107 +setup_data: 12.108 + dw BASE_MAZE+0x78*X_OFFSET+0x78 12.109 + dw BASE_MAZE+0x30*X_OFFSET+0x70 12.110 + dw BASE_MAZE+0x40*X_OFFSET+0x78 12.111 + dw BASE_MAZE+0x20*X_OFFSET+0x80 12.112 + dw BASE_MAZE+0x30*X_OFFSET+0x88 12.113 + 12.114 + ; 12.115 + ; Convert arrow codes to internal directions 12.116 + ; 12.117 +dirs: 12.118 + db 0x01 ; 0x48 = Up arrow 12.119 +go_restart: 12.120 + jmp restart ; 0x49 = Page Up ; 0x4a = Keypad - 12.121 + db 0x04 ; 0x4b = Left arrow 12.122 + db 0x00 ; 0x4c = Keypad 5 12.123 + db 0x08 ; 0x4d = Right arrow 12.124 +x_player: 12.125 + dw 0x00 ; 0x4e = Keypad + ; 0x4f = End 12.126 + db 0x02 ; 0x50 = Down arrow 12.127 + 12.128 + ; 12.129 + ; Game bitmaps 12.130 + ; 12.131 +bitmaps: 12.132 + db 0x00,0x42,0xe7,0xe7,0xff,0xff,0x7e,0x3c ; dir = 1 12.133 + db 0x3c,0x7e,0xff,0xff,0xe7,0xe7,0x42,0x00 ; dir = 2 12.134 + db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff ; Maze 12.135 + db 0x3c,0x7e,0x3f,0x0f,0x0f,0x3f,0x7e,0x3c ; dir = 4 12.136 + db 0x3c,0x7e,0xff,0xff,0xff,0xff,0x7e,0x3c ; Closed mouth 12.137 + db 0x3c,0x7e,0xdb,0xdb,0xff,0xff,0xff,0xa5 ; Ghost 12.138 + db 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00 ; Pill 12.139 + db 0x3c,0x7e,0xfc,0xf0,0xf0,0xfc,0x7e,0x3c ; dir = 8 12.140 + 12.141 + ; 12.142 + ; Move ghost 12.143 + ; bh = color 12.144 + ; 12.145 +move_ghost: 12.146 + lodsw ; Load screen position 12.147 + xchg ax,di 12.148 + lodsw ; Load direction 12.149 + test ah,ah 12.150 + xchg ax,bx ; Color now in ah 12.151 + mov al,0x30 12.152 + push ax 12.153 + mov byte [si-1],0x02 ; Remove first time setup flag 12.154 + call move_sprite3 12.155 + pop ax 12.156 + ; 12.157 + ; Draw the sprite/tile 12.158 + ; 12.159 + ; ah = sprite color 12.160 + ; al = sprite (x8) 12.161 + ; di = Target address 12.162 +draw_sprite: 12.163 + push ax 12.164 + push bx 12.165 + push cx 12.166 + push di 12.167 +ds0: push ax 12.168 + lea bx,[bp+bitmaps_-8] 12.169 + cs xlat ; Extract one byte from bitmap 12.170 + xchg ax,bx 12.171 + mov cx,8 12.172 +ds1: mov al,bh 12.173 + shl bl,1 ; Extract one bit 12.174 + jc ds2 12.175 + xor ax,ax ; Background color 12.176 +ds2: cmp bh,0x10 ; Color < 0x10 12.177 + jc ds3 ; Yes, jump 12.178 + cmp byte [di],PLAYER_COLOR ; "Eats" player? 12.179 + je go_restart ; No, it should not crash after several hundred games 12.180 + xor al,[di] ; XOR ghost again pixel 12.181 +ds3: stosb 12.182 + loop ds1 12.183 + add di,X_OFFSET-8 ; Go to next video line 12.184 + pop ax 12.185 + inc ax ; Next bitmap byte 12.186 + test al,7 ; Sprite complete? 12.187 + jne ds0 ; No, jump 12.188 + pop di 12.189 + pop cx 12.190 + pop bx 12.191 + pop ax 12.192 + ret 12.193 + 12.194 +get_tables: 12.195 + pop bp ; SS:BP = tables 12.196 + 12.197 +ghost_colors_ equ 0 12.198 +bitmaps_ equ bitmaps-ghost_colors ; Game bitmaps 12.199 +maze_ equ maze-ghost_colors ; Maze shape 12.200 +setup_data_ equ setup_data-ghost_colors ; Starting positions 12.201 +dirs_ equ dirs-ghost_colors ; Convert arrow codes to internal directions 12.202 +frame_ equ frame-ghost_colors ; Current video frame 12.203 +x_player_ equ x_player-ghost_colors ; Saved X-coordinate of player 12.204 +y_player_ equ y_player_loc-ghost_colors ; Saved Y-coordinate of player 12.205 +old_time_ equ old_time-ghost_colors ; Old time 12.206 + ; 12.207 + ; Draw the maze 12.208 + ; 12.209 + lea si,[bp+maze_] ; SI = Address of maze data 12.210 + mov di,BASE_MAZE ; DI = Address for drawing maze 12.211 +draw_maze_row: 12.212 + cs lodsw ; Load one word of data from Code Segment 12.213 + xchg ax,cx ; Put into AX 12.214 + mov bx,30*8 ; Offset of mirror position 12.215 +draw_maze_col: 12.216 + shl cx,1 ; Extract one tile of maze 12.217 + mov ax,MAZE_COLOR*0x0100+0x18 ; Carry = 0 = Wall 12.218 + jnc dm1 ; If bit was zero, jump to dm1 12.219 + mov ax,PILL_COLOR*0x0100+0x38 ; Carry = 1 = Pill 12.220 +dm1: call draw_sprite ; Draw tile 12.221 + add di,bx ; Go to mirror position 12.222 + sub bx,16 ; Mirror finished? 12.223 + jc dm2 ; Yes, jump 12.224 + call draw_sprite ; Draw tile 12.225 + sub di,bx ; Restore position 12.226 + sub di,8 ; Advance tile 12.227 + jmp draw_maze_col ; Repeat until finished 12.228 + 12.229 +dm2: 12.230 + add di,X_OFFSET*8-15*8 ; Go to next row 12.231 + lea ax,[bp+setup_data_] 12.232 + sub ax,si ; Maze completed? 12.233 + jne draw_maze_row ; No, jump 12.234 + 12.235 + ; 12.236 + ; Setup characters 12.237 + ; 12.238 + ; CX is zero at this point 12.239 + ; DI is equal to pos1 at this point 12.240 +%if pos1 != BASE_MAZE+21*8*X_OFFSET 12.241 + mov di,pos1 12.242 +%endif 12.243 + mov cl,5 ; 5 elements (player + ghosts) 12.244 + mov al,8 ; Going to right 12.245 +dm3: 12.246 + cs movsw ; Copy position from Code Segment 12.247 + stosw ; Store desired direction 12.248 + loop dm3 ; Loop 12.249 + 12.250 + ; 12.251 + ; Main game loop 12.252 + ; 12.253 +game_loop: 12.254 + mov ah,0x00 12.255 + int 0x1a ; BIOS clock read 12.256 + cmp dx,[bp+old_time_] ; Wait for time change 12.257 + je game_loop 12.258 + mov [bp+old_time_],dx ; Save new time 12.259 + 12.260 + mov ah,0x01 ; BIOS Key available 12.261 + int 0x16 12.262 + cbw ; BIOS Read Key 12.263 + je no_key 12.264 + int 0x16 12.265 +no_key: 12.266 + mov al,ah 12.267 + cmp al,0x01 ; Esc key 12.268 + jne no_esc 12.269 + mov ax,0x0003 ; Restore text mode 12.270 + int 0x10 12.271 + int 0x20 12.272 +no_esc: 12.273 + sub al,0x48 ; Code for arrow up? 12.274 + jc no_key2 ; Out of range, jump. 12.275 + cmp al,0x09 ; Farther than arrow down? 12.276 + jnc no_key2 ; Out of range, jump. 12.277 + lea bx,[bp+dirs_] 12.278 + cs xlat ; Translate direction to internal code 12.279 + mov [intended_dir],al ; Save as desired direction 12.280 +no_key2: 12.281 + mov si,pos1 ; SI points to data for player 12.282 + lodsw ; Load screen position 12.283 + xchg ax,di 12.284 + lodsw ; Load direction/type 12.285 + xchg ax,bx 12.286 + xor ax,ax ; Delete pillman 12.287 + call move_sprite2 ; Move 12.288 + mov ax,0x0e28 ; Closed mouth 12.289 + add byte [bp+frame_],al ; Alternate frame 12.290 + js close_mouth ; Jump if sign set. 12.291 + mov al,[pos1+2] ; Using current direction 12.292 + mov cl,3 ; Multiply by 8 12.293 + shl al,cl ; Show open mouth 12.294 +close_mouth: 12.295 + call draw_sprite ; Draw 12.296 + ; 12.297 + ; Move ghosts 12.298 + ; 12.299 + mov di,3 ; ghost_colors+3 12.300 +close_mouth_lp: 12.301 + mov bh,[bp+di] 12.302 + push di 12.303 + call move_ghost 12.304 + pop di 12.305 + dec di 12.306 + jns close_mouth_lp 12.307 + jmp game_loop 12.308 + 12.309 + ; 12.310 + ; DI = address on the screen 12.311 + ; BL = wanted direction 12.312 + ; 12.313 +move_sprite3: 12.314 + je move_sprite ; If zero, won't remove first time 12.315 +move_sprite2: 12.316 + call draw_sprite ; Remove ghost 12.317 +move_sprite: 12.318 + cwd ; ah = GHOST[1234]_COLOR 12.319 +; xor dx,dx 12.320 + mov ax,di ; Prepare to extract pixel row/column 12.321 + mov cx,X_OFFSET 12.322 + div cx 12.323 + ; Now AX = Row, DX = Column 12.324 + mov ah,dl 12.325 + or ah,al 12.326 + and ah,7 ; Both aligned at 8 pixels? 12.327 + jne ms0 ; No, jump because cannot change direction. 12.328 + ; AH is zero already 12.329 + ;mov ah,0 12.330 + ; 12.331 + ; Get available directions 12.332 + ; 12.333 + mov ch,MAZE_COLOR 12.334 + cmp [di+0x0008],ch ; Right 12.335 + adc ah,ah ; AH = 0000 000R 12.336 + cmp [di-0x0001],ch ; Left 12.337 + adc ah,ah ; AH = 0000 00RL 12.338 + cmp [di+X_OFFSET*8],ch ; Down 12.339 + adc ah,ah ; AH = 0000 0RLD 12.340 + cmp [di-X_OFFSET],ch ; Up 12.341 + adc ah,ah ; AH = 0000 RLDU 12.342 + 12.343 + test bh,bh ; Is it pillman? 12.344 + je ms4 ; Yes, jump 12.345 + 12.346 + ; 12.347 + ; Ghost 12.348 + ; 12.349 + test bl,0x03 ; Test BL for .... ..DU 12.350 + je ms6 ; No, jump 12.351 + ; Current direction is up/down 12.352 + cmp dx,[bp+x_player_] ; Compare X coordinate with player 12.353 + mov al,0x88 ; Go right if X ghost < X player 12.354 + jmp ms10 12.355 + 12.356 + ; Current direction is left/right 12.357 +ms6: cmp al,0x00 ; (SMC) Compare Y coordinate with player 12.358 +y_player_loc equ $-1 12.359 + mov al,0x22 ; Go down 12.360 +ms10: 12.361 + jc ms8 ; Jump if Y ghost < Y player 12.362 + shr al,1 ; Go up or right 12.363 +ms8: 12.364 + test ah,al ; Can it go in intended direction? 12.365 + jne ms1 ; Yes, go in direction 12.366 + 12.367 + mov al,bl 12.368 +ms9: test ah,al ; Can it go in current direction? 12.369 + jne ms1 ; Yes, jump 12.370 + ror al,1 ; Try another direction 12.371 + jmp ms9 12.372 + 12.373 + ; 12.374 + ; Pillman 12.375 + ; 12.376 +ms4: 12.377 + mov [bp+x_player_],dx ; Save current X coordinate 12.378 + mov [bp+y_player_],al ; Save current Y coordinate 12.379 + 12.380 + mov al,[intended_dir] 12.381 + test ah,al ; Can it go in intended direction? 12.382 + jne ms1 ; Yes, go in that direction 12.383 + 12.384 +ms5: and ah,bl ; Can it go in current direction? 12.385 + je ms2 ; No, stops 12.386 + 12.387 +ms0: mov al,bl 12.388 + 12.389 +ms1: mov [si-2],al ; Save new direction 12.390 + test al,3 ; If going up/down... 12.391 + mov bx,-X_OFFSET*2 ; ...bx = vertical movement 12.392 + jne ms3 12.393 + mov bx,1*2 ; ...bx = horizontal movement 12.394 +ms3: 12.395 + test al,6 ; If going left/down... 12.396 + je ms7 12.397 + neg bx ; ...reverse direction 12.398 +ms7: 12.399 + add di,bx ; Do move 12.400 + mov [si-4],di ; Save the new screen position 12.401 +ms2: 12.402 + ret 12.403 + 12.404 + times 510-($-$$) db 0x4f 12.405 + db 0x55,0xaa ; Make it a bootable sector
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/bootris/receipt Wed Sep 20 13:08:44 2023 +0000 13.3 @@ -0,0 +1,35 @@ 13.4 +# SliTaz package receipt. 13.5 + 13.6 +PACKAGE="bootris" 13.7 +VERSION="slitaz" 13.8 +CATEGORY="games" 13.9 +SHORT_DESC="Bootable Tetris game in a 512-byte boot sector." 13.10 +MAINTAINER="pascal.bellard@slitaz.org" 13.11 +LICENSE="unknown" 13.12 +#TARBALL="tetranglix.asm" 13.13 +WEB_SITE="https://github.com/nanochess/tetranglix" 13.14 +#WGET_URL="https://github.com/nanochess/tetranglix/raw/faebd443deaeedf9e7ac748c56f125d36531d2d4/tetranglix.asm" 13.15 +TARGET="i486" 13.16 + 13.17 +BUILD_DEPENDS="nasm" 13.18 + 13.19 +# Rules to configure and make the package. 13.20 +compile_rules() 13.21 +{ 13.22 + mkdir -p $src 13.23 + nasm -f bin $stuff/tetranglix.asm -o $src/tetranglix.img -l $src/tetranglix.lst 13.24 +} 13.25 + 13.26 +# Rules to gen a SliTaz package suitable for Tazpkg. 13.27 +genpkg_rules() 13.28 +{ 13.29 + mkdir -p $fs/boot 13.30 + cp $src/tetranglix.img $fs/boot/$PACKAGE 13.31 +} 13.32 + 13.33 +# Post install/remove commands for Tazpkg. 13.34 +post_install() 13.35 +{ 13.36 + grep -qs ^bootris $1/boot/bootmenu || 13.37 + echo "bootris tetris Bootable tetris game (may run under DOS if renamed to tetris.com)" >> $1/boot/bootmenu 13.38 +}
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/bootris/stuff/tetranglix.asm Wed Sep 20 13:08:44 2023 +0000 14.3 @@ -0,0 +1,518 @@ 14.4 +; Modified by nanochess for compatibility with VirtualBox. 14.5 +; to require only 8086 and also now it's in color. 14.6 + 14.7 +BITS 16 14.8 + 14.9 +BSS EQU 0x7E00 14.10 +BSS_SIZE EQU 438 14.11 + 14.12 +CUR_TETRAMINO EQU BSS ; 16 bytes. 14.13 +ROT_TETRAMINO EQU BSS + 16 ; 16 bytes. 14.14 +OFFSET EQU BSS + 32 ; 2 bytes. 14.15 +STACK EQU BSS + 38 ; 4 bytes reserved in beginning, 400 bytes. 14.16 + 14.17 +LEFT_SCANCODE EQU 0x4b 14.18 +RIGHT_SCANCODE EQU 0x4d 14.19 + 14.20 +UP_SCANCODE EQU 0x48 14.21 +DOWN_SCANCODE EQU 0x50 14.22 + 14.23 +SCORE_DIGITS EQU 5 14.24 + 14.25 +SCREEN_SEGMENT EQU 0xB800 14.26 + 14.27 +CPU 8086 14.28 + 14.29 +; Entry point. 14.30 +; cs:ip -> linear address (usually 0x7C00, but irrelevant because we are position independent). 14.31 +start: 14.32 + ; Stack. 14.33 + push cs 14.34 + pop ss 14.35 + mov sp, SCREEN_SEGMENT ;why not 14.36 + 14.37 + push cs 14.38 + pop ds 14.39 + push cs 14.40 + pop es 14.41 + 14.42 + ; Clear direction flag. 14.43 + cld 14.44 + 14.45 + ; Clear BSS 14.46 + mov di, BSS 14.47 + mov cx, di ;at least BSS_SIZE 14.48 + xor ax, ax 14.49 + rep stosb 14.50 + 14.51 + ; Set to mode 0x03, or 80x25 text mode (ah is zero from above). 14.52 + mov al, 0x03 14.53 + int 0x10 14.54 + 14.55 + ; Hide the hardware cursor. 14.56 + mov ch, 0x26 14.57 + mov ax, 0x103 ; Some BIOS crash without the 03. 14.58 + int 0x10 14.59 + 14.60 + mov es, sp 14.61 + 14.62 + ; White spaces on black background. 14.63 + xor di, di 14.64 + mov ax, 0x0F00 14.65 + mov cx, ax ; At least 80x25x2. 14.66 + rep stosw 14.67 + call pop_check 14.68 + 14.69 +; Detects if CUR_TETRAMINO at OFFSET is colliding with any thing. 14.70 +; si -> OFFSET. 14.71 +; Output: 14.72 +; Carry set if colliding. 14.73 +tetramino_collision_check: 14.74 + 14.75 + lea bx, [bp + check_collision - tetramino_collision_check] 14.76 + 14.77 +; Processes the current tetramino, calling bx per "tetramino pixel". 14.78 +; bx -> where to call to; al contains tetramino pixel, di the address into stack. 14.79 +tetramino_process: 14.80 + push si 14.81 + push ax 14.82 + push di 14.83 + push cx 14.84 + 14.85 +; Gets the offset into stack (i.e., address) into di. 14.86 +; si -> points at OFFSET. 14.87 +; Output: 14.88 +; si -> points at CUR_TETRAMINO. 14.89 +; di -> address into stack. 14.90 +; Trashes ax. 14.91 + 14.92 + ; Calculate first index into screen. 14.93 + lodsw 14.94 + aad 0x10 14.95 + cmp byte [si-1], 0x10 14.96 + sbb ah, ah 14.97 + xchg bx, ax 14.98 + lea di, [si + (STACK - OFFSET) + 0xFE + bx] 14.99 + xchg bx, ax 14.100 + 14.101 + mov si, CUR_TETRAMINO 14.102 + 14.103 + mov cl, 0x10 14.104 + 14.105 + .loop: 14.106 + test cl, 0x13;0b1011 14.107 + jnz .load_loop 14.108 + 14.109 + ; Go to next line in stack. 14.110 + add di, 16 - 4 14.111 + 14.112 + .load_loop: 14.113 + lodsb 14.114 + 14.115 + ; Call wherever the caller wants us to go. 14.116 + call bx 14.117 + 14.118 + inc di 14.119 + loop .loop 14.120 + 14.121 + pop cx 14.122 + pop di 14.123 + pop ax 14.124 + pop si 14.125 + ret 14.126 + 14.127 +check_collision: 14.128 + or al,al 14.129 + jz .clear_carry 14.130 + 14.131 + cmp di, STACK + 400 14.132 + jae .colliding 14.133 + 14.134 + cmp byte [di],0 14.135 + 14.136 + .clear_carry: 14.137 + ;clc 14.138 + 14.139 + je .next_iter 14.140 + 14.141 + ; Colliding! 14.142 + .colliding: 14.143 + 14.144 + stc 14.145 + mov cl, 1 14.146 + .next_iter: 14.147 + ret 14.148 + 14.149 +; Used by the stack joining part. 14.150 +merge: 14.151 + or [di], al 14.152 + ret 14.153 + 14.154 +; All tetraminos in bitmap format. 14.155 +tetraminos: 14.156 + db 0xF0;0b11110000 ; I 14.157 + db 0xE2;0b11100010 ; J 14.158 + db 0x2E;0b00101110 ; L 14.159 + db 0x66;0b01100110 ; O 14.160 + db 0x36;0b00110110 ; S 14.161 + db 0xE4;0b11100100 ; T 14.162 + db 0x63;0b01100011 ; Z 14.163 + 14.164 +pop_check: 14.165 + pop bp ; Save some bytes. 14.166 + 14.167 + .borders: 14.168 + mov si, STACK - 3 14.169 + mov ax, 0x0101 14.170 + 14.171 + .borders_init: 14.172 + mov [si], ax 14.173 + mov [si + 2], ax 14.174 + mov [si + 4], ax 14.175 + 14.176 + add si, 16 14.177 + cmp si, STACK + 400 - 3 14.178 + jbe .borders_init 14.179 + 14.180 + ; Cleared dl implies "load new tetramino". 14.181 + xor dl, dl 14.182 + 14.183 + .event_loop: 14.184 + mov si, OFFSET 14.185 + 14.186 + ; For some reason this doesn't work with BootOS over VirtualBox 5.1.22 14.187 + %if 0 14.188 + mov bx, [0x046C] 14.189 + inc bx 14.190 + inc bx ; Wait for 2 PIT ticks. 14.191 + 14.192 + .busy_loop: 14.193 + cmp [0x046C], bx 14.194 + jne .busy_loop 14.195 + %else 14.196 + push dx 14.197 + clc 14.198 + mov bx,0x1000 14.199 +.busy_loop2: 14.200 + pushf 14.201 +.busy_loop1: 14.202 + mov ah,0x00 14.203 + int 0x1a 14.204 + cmp [bx],dx 14.205 + je .busy_loop1 14.206 + mov [bx],dx 14.207 + popf 14.208 + cmc 14.209 + jc .busy_loop2 14.210 + pop dx 14.211 + xor cx,cx ; Or rotation doesn't work 14.212 + %endif 14.213 + ; If we don't need to load a new tetramino, yayy! 14.214 + test dl, dl 14.215 + jnz .input 14.216 + 14.217 + ; Load a tetramino to CUR_TETRAMINO, from the compressed bitmap format. 14.218 + 14.219 + .choose_tetramino: 14.220 + in al,(0x40) 14.221 + 14.222 + ; Only 7 tetraminos, index as 1-7. 14.223 + and ax, 7 14.224 + je .choose_tetramino 14.225 + 14.226 + ; Get the address of the tetramino (in bitmap format). 14.227 + cwd 14.228 + mov di,ax 14.229 + mov ah,al 14.230 + 14.231 + ; Load tetramino bitmap in dl. 14.232 + mov dl, [bp + di + (tetraminos - tetramino_collision_check) - 1] 14.233 + mov cl, 4 14.234 + shl dx, cl 14.235 + 14.236 + ; Convert from bitmap to array. 14.237 + mov di, CUR_TETRAMINO 14.238 + mov cl, 0x10 14.239 + 14.240 + .loop_bitmap: 14.241 + 14.242 + shl dx, 1 14.243 + 14.244 + ; If the bit we just shifted off was set, store number of tetramino. 14.245 + sbb al, al 14.246 + and al, ah 14.247 + mov [di], al 14.248 + inc di 14.249 + 14.250 + loop .loop_bitmap 14.251 + 14.252 + ; Loaded. 14.253 + mov dl, 6 14.254 + 14.255 + mov word [si], dx 14.256 + jmp .link_next_iter 14.257 + 14.258 + ; Check for input. 14.259 + .input: 14.260 + ; Check for keystroke. 14.261 + mov ah, 0x01 14.262 + int 0x16 14.263 + 14.264 + ; If no keystroke, increment vertical offset. 14.265 + jz .vertical_increment 14.266 + 14.267 + ; Clear the keyboard buffer. 14.268 + xor ah, ah 14.269 + int 0x16 14.270 + 14.271 + .exit: 14.272 + dec ah 14.273 + jne .left 14.274 + 14.275 + .exit_dos: 14.276 + mov al,0x03 ; Clear screen 14.277 + int 0x10 14.278 + int 0x20 ; Return to bootOS 14.279 + jmp start 14.280 + 14.281 + ; Go left. 14.282 + .left: 14.283 + cmp ah, LEFT_SCANCODE-1 14.284 + jne .right 14.285 + 14.286 + dec byte [si] 14.287 + jmp .call_bp 14.288 + 14.289 + ; Go right. 14.290 + .right: 14.291 + cmp ah, RIGHT_SCANCODE-1 14.292 + jne .rotate 14.293 + 14.294 + inc byte [si] 14.295 + 14.296 + .call_bp: 14.297 + xor ah, (LEFT_SCANCODE-1) ^ (RIGHT_SCANCODE-1) 14.298 + call bp 14.299 + jc .left 14.300 + 14.301 + ; Rotate it. 14.302 + .rotate: 14.303 + cmp ah, UP_SCANCODE-1 14.304 + jne .vertical_increment 14.305 + 14.306 + inc cx 14.307 + 14.308 + .rotate_loop: 14.309 + ; Rotates CUR_TETRAMINO 90 degrees clock-wise. 14.310 + ; Output: 14.311 + ; CUR_TETRAMINO -> rotated tetramino. 14.312 + push si 14.313 + push di 14.314 + push cx 14.315 + push es 14.316 + 14.317 + ; Reset ES. 14.318 + push ds 14.319 + pop es 14.320 + 14.321 + mov si, CUR_TETRAMINO 14.322 + mov di, ROT_TETRAMINO + 3 14.323 + push si 14.324 + mov cl, 4 14.325 + 14.326 + .loop: 14.327 + mov ch, 4 14.328 + 14.329 + .tetramino_line: 14.330 + movsb 14.331 + scasw 14.332 + inc di 14.333 + dec ch 14.334 + jnz .tetramino_line 14.335 + 14.336 + sub di, 4*4+1 14.337 + loop .loop 14.338 + 14.339 + pop di 14.340 + mov cl, 4*4/2 ; CH would be zero, from above. 14.341 + rep movsw 14.342 + 14.343 + pop es 14.344 + pop cx 14.345 + pop di 14.346 + pop si 14.347 + 14.348 + loop .rotate_loop 14.349 + 14.350 + call bp 14.351 + ; To restore, just rotate 3 more times. 14.352 + mov cl, 3 14.353 + jc .rotate_loop 14.354 + 14.355 + .vertical_increment: 14.356 + mov cl, 1 14.357 + call upd_score 14.358 + 14.359 + ; Check if we can go below one byte, successfully. 14.360 + inc byte [si + 1] 14.361 + call bp 14.362 + .link_next_iter: 14.363 + jnc .next_iter 14.364 + 14.365 + ; If we can't, we need a new tetramino. 14.366 + dec byte [si + 1] 14.367 + je .game_over 14.368 + cwd 14.369 + 14.370 + ; Joins the current tetramino to the stack, and any complete lines together. 14.371 + ; si -> OFFSET. 14.372 + push es 14.373 + 14.374 + push ds 14.375 + pop es 14.376 + 14.377 + lea bx, [bp + merge - tetramino_collision_check] 14.378 + call tetramino_process 14.379 + 14.380 + mov si, STACK + 15 14.381 + std 14.382 + 14.383 + .loop_lines: 14.384 + push si 14.385 + mov cl, 16 14.386 + 14.387 + .line: 14.388 + lodsb 14.389 + test al, al 14.390 + loopnz .line ; If it was a blank, exit loop to indicate failure. 14.391 + 14.392 + jz .next_line 14.393 + 14.394 + lea cx, [si - (STACK - 1)] 14.395 + lea di, [si + 16] 14.396 + rep movsb 14.397 + mov cl, 64 14.398 + call upd_score 14.399 + 14.400 + .next_line: 14.401 + pop si 14.402 + add si, 16 14.403 + cmp si, STACK + 15 + 400 14.404 + jb .loop_lines 14.405 + 14.406 + cld 14.407 + pop es 14.408 + 14.409 + jmp .borders 14.410 + 14.411 + .game_over: ; Game Over 14.412 + xor ax, ax 14.413 + int 0x16 ; wait for and key 14.414 + jmp .exit_dos 14.415 + 14.416 + .next_iter: 14.417 + ; Display the stack. 14.418 + push si 14.419 + 14.420 + ; Add 24 characters padding in the front. 14.421 + mov ah, 0x0F 14.422 + mov di, 48 14.423 + mov si, STACK 14.424 + 14.425 + .loop_stack_lines: 14.426 + ; Copy 32 characters. 14.427 + mov cl, 16 14.428 + 14.429 + .stack_line: 14.430 + lodsb 14.431 + mov ah,al 14.432 + mov al,0xdb 14.433 + ; Store one character as two -- to make stack "squarish" on 80x25 display. 14.434 + stosw 14.435 + stosw 14.436 + 14.437 + loop .stack_line 14.438 + 14.439 + ; Handle remaining 24 characters in row, and starting 24 in next row. 14.440 + add di, 96 14.441 + cmp di, (25 * 160) ; If we go beyond the last row, we're over. 14.442 + jb .loop_stack_lines 14.443 + 14.444 + pop si 14.445 + 14.446 + ; Displays CUR_TETRAMINO at current OFFSET. 14.447 + ; si -> OFFSET. 14.448 + 14.449 + ; Calculate first index into screen. 14.450 + mov al, 40 14.451 + mul byte [si+1] 14.452 + mov cl, 12 14.453 + add cl, [si] 14.454 + add ax, cx 14.455 + 14.456 + ; One character takes 2 bytes in video memory. 14.457 + shl ax, 1 14.458 + shl ax, 1 14.459 + xchg di, ax 14.460 + 14.461 + ; Loops for 16 input characters. 14.462 + mov cl, 0x10 14.463 + mov si, CUR_TETRAMINO 14.464 + 14.465 + mov ah, 0x0F 14.466 + 14.467 + .loop_tetramino: 14.468 + test cl, 0x13;0b1011 14.469 + jnz .load_tetramino 14.470 + 14.471 + ; Since each tetramino input is 4x4, we must go to next line 14.472 + ; at every multiple of 4. 14.473 + ; Since we output 2 characters for one input char, cover offset of 8. 14.474 + add di, (80 - 8) * 2 14.475 + 14.476 + .load_tetramino: 14.477 + lodsb 14.478 + or al,al 14.479 + mov ah,al 14.480 + mov al,0xdb 14.481 + ; Output two characters for "squarish" output. 14.482 + jne .load_tetramino2 14.483 + mov ax, [es:di] 14.484 + .load_tetramino2: 14.485 + stosw 14.486 + stosw 14.487 + 14.488 + loop .loop_tetramino 14.489 + 14.490 + jmp .event_loop 14.491 + 14.492 +upd_score: 14.493 + push ds 14.494 + mov bx, SCREEN_SEGMENT 14.495 + mov ds, bx 14.496 + mov bx, SCORE_DIGITS * 2 14.497 + 14.498 + .chk_score: 14.499 + dec bx 14.500 + dec bx 14.501 + js pop_check.game_over 14.502 + mov al, '0' 14.503 + xchg [bx], al 14.504 + or al, 0x30 14.505 + cmp al, '9' 14.506 + je .chk_score 14.507 + inc ax 14.508 + mov [bx], al 14.509 + pop ds 14.510 + 14.511 + loop upd_score 14.512 + ret 14.513 + 14.514 +; IT'S A SECRET TO EVERYBODY. 14.515 +db "ShNoXgSo" 14.516 + 14.517 +; Padding. 14.518 +times 510 - ($ - $$) db 0 14.519 + 14.520 +BIOS_signature: 14.521 + dw 0xAA55
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/bootsokoban/receipt Wed Sep 20 13:08:44 2023 +0000 15.3 @@ -0,0 +1,35 @@ 15.4 +# SliTaz package receipt. 15.5 + 15.6 +PACKAGE="bootsokoban" 15.7 +VERSION="slitaz" 15.8 +CATEGORY="games" 15.9 +SHORT_DESC="Bootable sokoban game in a 512-byte boot sector." 15.10 +MAINTAINER="pascal.bellard@slitaz.org" 15.11 +LICENSE="unknown" 15.12 +#TARBALL="sokoban.asm" 15.13 +WEB_SITE="https://ish.works/bootsector/bootsector.html" 15.14 +#WGET_URL="https://ish.works/bootsector/sokoban.asm" 15.15 +TARGET="i486" 15.16 + 15.17 +BUILD_DEPENDS="nasm" 15.18 + 15.19 +# Rules to configure and make the package. 15.20 +compile_rules() 15.21 +{ 15.22 + mkdir -p $src 15.23 + nasm -f bin $stuff/sokoban.asm -o $src/sokoban.img -l $src/sokoban.lst 15.24 +} 15.25 + 15.26 +# Rules to gen a SliTaz package suitable for Tazpkg. 15.27 +genpkg_rules() 15.28 +{ 15.29 + mkdir -p $fs/boot 15.30 + cp $src/sokoban.img $fs/boot/$PACKAGE 15.31 +} 15.32 + 15.33 +# Post install/remove commands for Tazpkg. 15.34 +post_install() 15.35 +{ 15.36 + grep -qs ^bootsokoban $1/boot/bootmenu || 15.37 + echo "bootsokoban sokoban,digger Bootable sokoban game (may run under DOS if renamed to sokoban.com)" >> $1/boot/bootmenu 15.38 +}
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/bootsokoban/stuff/sokoban.asm Wed Sep 20 13:08:44 2023 +0000 16.3 @@ -0,0 +1,425 @@ 16.4 +bits 16 ; tell NASM this is 16 bit code 16.5 +cpu 8086 16.6 + 16.7 +%define CURRENT_LEVEL 0x7E00 16.8 +%define CURRENT_LEVEL_4 0x7E04 16.9 +%define SCREEN_DS 0xb800 16.10 + 16.11 +boot: 16.12 + ; clear screen (re-set text mode) 16.13 + mov ax, 0x0003 ; text mode 80x25 16 colours 16.14 + int 0x10 16.15 + 16.16 + ; disable cursor 16.17 + mov ah, 0x01 16.18 + mov ch, 0x3f 16.19 + int 0x10 16.20 + 16.21 + ; set up stack 16.22 + push cs 16.23 + pop ss 16.24 + xor sp, sp 16.25 + 16.26 + push cs 16.27 + pop ds 16.28 + push cs 16.29 + pop es 16.30 + 16.31 + call get_data 16.32 + 16.33 +; data section: 16.34 + 16.35 +; 0000 0000 EMPTY 16.36 +; 0000 0001 SPOT 16.37 +; 0000 0010 BRICK 16.38 +; 0000 0011 BRICK ON SPOT 16.39 +; 0000 0100 WALL 16.40 +; 0000 1000 PLAYER 16.41 +; 0000 1001 PLAYER ON SPOT 16.42 +test_level: 16.43 + ; this was the original level format, which was quite big: 16.44 + 16.45 + ; db 9, 7 ; width, height 16.46 + ; dw 32 ; playerxy 16.47 + ; db 4,4,4,4,4,4,0,0,0 16.48 + ; db 4,0,0,0,0,4,0,0,0 16.49 + ; db 4,0,0,2,0,4,4,0,0 16.50 + ; db 4,0,2,4,1,9,4,4,4 16.51 + ; db 4,4,0,0,3,1,2,0,4 16.52 + ; db 0,4,0,0,0,0,0,0,4 16.53 + ; db 0,4,4,4,4,4,4,4,4 16.54 + ; db 14, 10 ;width, height 16.55 + ; dw 63 ;playerxy 16.56 + 16.57 + ; when i tried to put in THIS level (from https://www.youtube.com/watch?v=fg8QImlvB-k) 16.58 + ; i passed the 512 byte limit... 16.59 + 16.60 + ; db 4,4,4,4,4,4,4,4,4,4,4,4,0,0 16.61 + ; db 4,1,1,0,0,4,0,0,0,0,0,4,4,4 16.62 + ; db 4,1,1,0,0,4,0,2,0,0,2,0,0,4 16.63 + ; db 4,1,1,0,0,4,2,4,4,4,4,0,0,4 16.64 + ; db 4,1,1,0,0,0,0,8,0,4,4,0,0,4 16.65 + ; db 4,1,1,0,0,4,0,4,0,0,2,0,4,4 16.66 + ; db 4,4,4,4,4,4,0,4,4,2,0,2,0,4 16.67 + ; db 0,0,4,0,2,0,0,2,0,2,0,2,0,4 16.68 + ; db 0,0,4,0,0,0,0,4,0,0,0,0,0,4 16.69 + ; db 0,0,4,4,4,4,4,4,4,4,4,4,4,4 16.70 + 16.71 + ; so i compressed it! high nybble first, low nybble second 16.72 + db 14, 10 ;width, height 16.73 + dw 63 ;playerxy 16.74 + db 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x00 16.75 + db 0x41, 0x10, 0x04, 0x00, 0x00, 0x04, 0x44 16.76 + db 0x41, 0x10, 0x04, 0x02, 0x00, 0x20, 0x04 16.77 + db 0x41, 0x10, 0x04, 0x24, 0x44, 0x40, 0x04 16.78 + db 0x41, 0x10, 0x00, 0x08, 0x04, 0x40, 0x04 16.79 + db 0x41, 0x10, 0x04, 0x04, 0x00, 0x20, 0x44 16.80 + db 0x44, 0x44, 0x44, 0x04, 0x42, 0x02, 0x04 16.81 + db 0x00, 0x40, 0x20, 0x02, 0x02, 0x02, 0x04 16.82 + db 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x04 16.83 + db 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 16.84 + 16.85 + 16.86 + 16.87 +display_chars: db 0, 0x07 ; blank 16.88 + db 249, 0x07 ; spot 16.89 + db 4, 0x0C ; brick 16.90 + db 4, 0x0A ; brick on spot 16.91 + db 178, 0x71 ; wall 16.92 + db "5", 0x07 ; (no 5) 16.93 + db "6", 0x07 ; (no 6) 16.94 + db "7", 0x07 ; (no 7) 16.95 + db 1, 0x0F ; player 16.96 + db 1, 0x0F ; player on spot 16.97 + 16.98 +str_you_win: db 'YOU WIN! ',1,1,1,0 16.99 + 16.100 +get_data: 16.101 + pop bp 16.102 + 16.103 + ; set current level to test level by copying 16.104 + mov si, bp 16.105 + 16.106 + ; get width and height and multiply by each other 16.107 + mov ax, [cs:si] 16.108 + mul ah 16.109 + 16.110 + ; set multiplied width and height + 4 as counter 16.111 + mov cx, ax 16.112 + ;add cx, 4 16.113 + 16.114 + mov di, CURRENT_LEVEL ; next address to copy to 16.115 + 16.116 + ; copy map size and player position ("uncompressed") 16.117 + cs movsw 16.118 + cs movsw 16.119 + 16.120 +.copy_level_loop: 16.121 + ; load "compressed" byte: e.g. 0x28 or 0x44 into AL 16.122 + cs lodsb 16.123 +%if 0 16.124 + aam 16 16.125 + xchg al, ah 16.126 +%else 16.127 + mov ah, al ; AX = 0x2828 16.128 + and ax, 0x0FF0 ; AX = 0x0820 (little endian: 20 08) 16.129 + push cx 16.130 + mov cl, 4 16.131 + shr al, cl ; AX = 0x0802 (little endian: 02 08) 16.132 + pop cx 16.133 +%endif 16.134 + 16.135 + ; save "uncompressed" word: e.g. 02 08 or 04 04 from AX 16.136 + stosw 16.137 + 16.138 + loop .copy_level_loop 16.139 + 16.140 + call draw_current_level 16.141 + 16.142 +.mainloop: 16.143 + ; read key 16.144 + xor ax, ax 16.145 + int 0x16 16.146 + 16.147 + mov al, byte [CURRENT_LEVEL] ; (width of current level) to the right = 1 down 16.148 + dec ah ; esc 16.149 + jne .not_boot 16.150 +.exit: 16.151 + mov ax, 0x0003 ; text mode 80x25 16 colours 16.152 + int 0x10 16.153 + int 0x20 16.154 + jmp boot 16.155 + 16.156 +.not_boot: 16.157 + cmp ah, 0x50-1 ; down arrow 16.158 + je .try_move_down 16.159 + 16.160 + cmp ah, 0x48-1 ; up arrow 16.161 + je .try_move_up 16.162 + 16.163 + mov al, 1 16.164 + cmp ah, 0x4d-1 ; right arrow 16.165 + je .try_move_right 16.166 + 16.167 + cmp ah, 0x4b-1 ; left arrow 16.168 + jne .redraw 16.169 + 16.170 +.try_move_left: 16.171 +.try_move_up: 16.172 + neg al 16.173 +.try_move_right: 16.174 +.try_move_down: 16.175 +%if 1 16.176 +SCORE_DIGITS EQU 4 16.177 + push ax 16.178 + push ds 16.179 + mov bx, SCREEN_DS 16.180 + mov ds, bx 16.181 + mov bx, SCORE_DIGITS * 2 16.182 + 16.183 +.chk_score: 16.184 + dec bx 16.185 + dec bx 16.186 + js .exit 16.187 + mov al, '0' 16.188 + xchg [bx], al 16.189 + or al, 0x30 16.190 + cmp al, '9' 16.191 + je .chk_score 16.192 + inc ax 16.193 + mov [bx], al 16.194 + pop ds 16.195 + pop ax 16.196 +%endif 16.197 + call try_move 16.198 + 16.199 +.redraw: 16.200 + call draw_current_level 16.201 + 16.202 +.check_win: 16.203 + 16.204 + ; get width and height 16.205 + mov ax, [CURRENT_LEVEL] ; al = width; ah = height 16.206 + mul ah 16.207 + mov cx, ax ; cx = size of map 16.208 + 16.209 + xor bx, bx ; bx = number of bricks-NOT-on-a-spot 16.210 + 16.211 + mov si, CURRENT_LEVEL_4 16.212 +.check_win_loop: 16.213 + lodsb 16.214 + cmp al, 2 16.215 + jne .not_a_brick 16.216 + inc bx 16.217 +.not_a_brick: 16.218 + loop .check_win_loop 16.219 + 16.220 + ; so, did we win? is the number of spotless bricks == 0?? 16.221 + cmp bx, 0 16.222 + je win 16.223 + jmp .mainloop 16.224 + 16.225 + 16.226 +win: 16.227 + ; print a nice win message to the middle of the screen 16.228 + lea si, [bp+str_you_win-test_level] 16.229 + 16.230 + ; destination position on screen 16.231 + mov ax, SCREEN_DS 16.232 + mov es, ax 16.233 + mov di, (80 * 12 + 40 - 6) * 2 16.234 + 16.235 + mov ah, 0x0F 16.236 +.loop: 16.237 + cs lodsb 16.238 + 16.239 + cmp al, 0 16.240 + je wait_for_esc 16.241 + 16.242 + stosw 16.243 + jmp .loop 16.244 + 16.245 +wait_for_esc: 16.246 + ; read key 16.247 + xor ax, ax 16.248 + int 0x16 16.249 + 16.250 + dec ah ; esc 16.251 + je get_data.exit 16.252 + jmp wait_for_esc 16.253 +; halt: 16.254 +; cli ; clear interrupt flag 16.255 +; hlt ; halt execution 16.256 + 16.257 + 16.258 +;; functions: 16.259 + 16.260 +draw_current_level: 16.261 + ; get width and height 16.262 + mov cx, [CURRENT_LEVEL] ; cl = width; ch = height 16.263 + push cx ; put it in the stack for later reuse 16.264 + 16.265 + ; print in the middle and not in the corner 16.266 + mov di, 2000; middle of screen 16.267 + 16.268 + ; offset by half of width 16.269 + mov bx, 0x00FE 16.270 + and bl, cl 16.271 + sub di, bx 16.272 + 16.273 + ; offset by half of height 16.274 + mov cl, ch 16.275 + and cx, 0x00FE 16.276 + mov ax, 80 16.277 + mul cx 16.278 + sub di, ax 16.279 + 16.280 + 16.281 + mov si, CURRENT_LEVEL_4 ; source byte 16.282 + 16.283 + ; screen memory in text mode 16.284 + mov ax, SCREEN_DS 16.285 + mov es, ax 16.286 + 16.287 +.loop: 16.288 + push si 16.289 + lodsb 16.290 + cbw 16.291 + xchg ax, si 16.292 + add si, si 16.293 + mov ax, [cs:bp+si+display_chars-test_level] 16.294 + pop si 16.295 + 16.296 + stosw 16.297 + 16.298 + inc si 16.299 + pop cx ; get counters 16.300 + dec cl ; subtract 1 from X axis counter 16.301 + jz .nextrow 16.302 + push cx 16.303 + jmp .loop 16.304 + 16.305 +.nextrow: 16.306 + dec ch ; subtract 1 from Y axis counter 16.307 + jz .finished 16.308 + mov cl, [CURRENT_LEVEL] 16.309 + push cx 16.310 + 16.311 + ; jump to next row down 16.312 + xor ch, ch 16.313 + neg cx 16.314 + add cx, 80 16.315 + add cx, cx 16.316 + add di, cx 16.317 + 16.318 + jmp .loop 16.319 + 16.320 +.finished: 16.321 + ret 16.322 + 16.323 +try_move: 16.324 + ; try to move the player 16.325 + ; al = offset of how much to move by 16.326 + push dx 16.327 + 16.328 + ; extend al into ax (signed) 16.329 + test al, al ; check if negative 16.330 + js .negative_al 16.331 + xor ah, ah 16.332 + jmp .after_al 16.333 +.negative_al: 16.334 + mov ah, 0xFF 16.335 +.after_al: 16.336 + push ax 16.337 + 16.338 + mov di,CURRENT_LEVEL_4 16.339 + 16.340 + ; calculate total level size 16.341 + mov ax, [CURRENT_LEVEL] 16.342 + mul ah 16.343 + 16.344 + ; calculate requested destination position 16.345 + pop bx 16.346 + push bx 16.347 + mov dx, [di - 2] 16.348 + add bx, dx 16.349 + 16.350 + ; check if in bounds 16.351 + cmp bx, 0 16.352 + jl .finished 16.353 + cmp bx, ax 16.354 + jg .finished 16.355 + 16.356 + ; get value at destination position 16.357 + mov cl, [bx + di] 16.358 + cmp cl, 4 16.359 + je .cant_push ; it's a wall 16.360 + test cl, 0x02 16.361 + jz .dont_push ; it's not a brick (on spot, or not), so don't try pushing 16.362 + 16.363 + ; try pushing brick 16.364 + pop cx ; get move offset 16.365 + push bx ; store player's destination position (brick's current position) 16.366 + 16.367 + mov dx, bx ; dx = current brick position 16.368 + add bx, cx ; bx = next brick position 16.369 + 16.370 + ; check bounds 16.371 + cmp bx, 0 16.372 + jl .cant_push 16.373 + cmp bx, ax 16.374 + jg .cant_push 16.375 + 16.376 + ; get value at destination position 16.377 + mov ch, [bx + di] 16.378 + test ch, 0x0E ; test if the destination is occupied at all by ANDing with 0000 1110 16.379 + jnz .cant_push 16.380 + 16.381 + ; all checks passed! push the brick 16.382 + 16.383 + ; add new brick to screen 16.384 + or ch, 0x02 ; add brick bit, by ORing with 0000 0010 16.385 + mov [bx + di], ch 16.386 + 16.387 + ; remove old brick from screen 16.388 + add di, dx 16.389 + mov cl, [di] 16.390 + and cl, 0xFD ; remove brick bit, by ANDing with 1111 1101 16.391 + mov [di], cl 16.392 + sub di, dx 16.393 + 16.394 + mov dx, [di - 2] ; dx = current player position 16.395 + pop bx ; bx = next player position 16.396 + jmp .redraw_player 16.397 + 16.398 +.cant_push: 16.399 + pop bx 16.400 + jmp .finished 16.401 + 16.402 +.dont_push: 16.403 + pop cx ; don't need to have this offset in the stack anymore 16.404 + 16.405 +.redraw_player: 16.406 + ; remove old player from screen 16.407 + add di, dx 16.408 + mov cl, [di] 16.409 + and cl, 0xF7 ; remove player bit, by ANDing with 1111 0111 16.410 + mov [di], cl 16.411 + sub di, dx 16.412 + 16.413 + ; add new player to screen 16.414 + mov ch, [bx + di] 16.415 + or ch, 0x08 ; add player bit, by ORing with 0000 1000 16.416 + mov [bx + di], ch 16.417 + 16.418 + ; update player position in memory 16.419 + mov [di - 2], bx 16.420 + 16.421 +.finished: 16.422 + 16.423 + pop dx 16.424 + ret 16.425 + 16.426 + 16.427 +times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes 16.428 +dw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable!