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!