wok-tiny rev 186

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