Rename and migrate all `.inc` files to `.asm`: updated references.
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Fri, 20 Feb 2026 16:23:46 +0000 (18:23 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Fri, 20 Feb 2026 16:23:46 +0000 (18:23 +0200)
26 files changed:
AGENTS.md
emulator/charput.asm [new file with mode: 0644]
emulator/charput.inc [deleted file]
emulator/emulator.asm
emulator/kbdrive.asm [new file with mode: 0644]
emulator/kbdrive.inc [deleted file]
emulator/opcodes_00_09.asm [new file with mode: 0644]
emulator/opcodes_00_09.inc [deleted file]
emulator/opcodes_10_19.asm [new file with mode: 0644]
emulator/opcodes_10_19.inc [deleted file]
emulator/opcodes_20_29.asm [new file with mode: 0644]
emulator/opcodes_20_29.inc [deleted file]
emulator/opcodes_30_39.asm [new file with mode: 0644]
emulator/opcodes_30_39.inc [deleted file]
emulator/opcodes_40_47.inc [deleted file]
emulator/opcodes_40_49.asm [new file with mode: 0644]
emulator/system.asm [new file with mode: 0644]
emulator/system.inc [deleted file]
emulator/tvidput.asm [new file with mode: 0644]
emulator/tvidput.inc [deleted file]
emulator/vidput.asm [new file with mode: 0644]
emulator/vidput.inc [deleted file]
kernel/compile.sh [new file with mode: 0755]
kernel/core.asm
kernel/define.asm [new file with mode: 0644]
kernel/define.inc [deleted file]

index 9dc5702..cfc4e11 100644 (file)
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -21,11 +21,11 @@ editor, and a graphics editor.
    the build tools handle FSCII conversion.
 
 3. *Two different assembly languages exist in this project:*
-   - =kernel/core.asm= and =kernel/define.inc= use the *virtual CPU's
-     own instruction set* (NOT x86). See the "Virtual CPU Instruction
-     Set" section below.
-   - =emulator/emulator.asm= and its =.inc= files are *x86 real-mode
-     DOS assembly* for FASM (Flat Assembler).
+    - =kernel/core.asm= and =kernel/define.asm= use
+      the *virtual CPU's* own instruction set (NOT x86). See the
+      "Virtual CPU Instruction Set" section below.
+    - =emulator/emulator.asm= and related =*.asm= files under emulator/ directory 
+      are *x86 real-mode* DOS assembly for FASM (Flat Assembler).
 
 4. *Binary files* (=.raw=, =.com=, =.dat=, =FNT_SYSTEM=,
    =I01_MCARROW=, =core.raw=, =emulator.com=) are compiled outputs
@@ -57,17 +57,22 @@ editor, and a graphics editor.
 | =kernel/font.asm=   | 8×8 bitmap font data (256 characters)          |
 | =kernel/core.raw=   | Compiled kernel binary (do not edit)           |
 
-## Emulator (x86 real-mode FASM assembly)
-| File                    | Description                                |
-|-------------------------|--------------------------------------------|
-| =emulator/emulator.asm= | Main emulator: instruction dispatch loop   |
-| =emulator/system.inc=   | XMS allocation, protected mode, A20 gate   |
-| =emulator/vidput.inc=   | Image-to-image blit (opaque)               |
-| =emulator/tvidput.inc=  | Image-to-image blit (transparent, FF=skip) |
-| =emulator/charput.inc=  | Character glyph rendering to image buffer  |
-| =emulator/kbdrive.inc=  | Keyboard interrupt handler + ring buffer   |
-| =emulator/compile.sh=   | Build script (runs =fasm emulator.asm=)    |
-| =emulator/emulator.com= | Compiled DOS COM binary (do not edit)      |
+ ## Emulator (x86 real-mode FASM assembly)
+ | File                         | Description                                                     |
+ |------------------------------|-----------------------------------------------------------------|
+ | =emulator/emulator.asm=      | Main emulator: instruction dispatch loop                        |
+ | =emulator/system.asm=        | XMS allocation, protected mode, A20 gate                        |
+ | =emulator/kbdrive.asm=       | Keyboard interrupt handler + ring buffer                        |
+ | =emulator/vidput.asm=        | Image-to-image blit (opaque)                                    |
+ | =emulator/tvidput.asm=       | Image-to-image blit (with transparency support, FF=transparent) |
+ | =emulator/charput.asm=       | Character glyph rendering to image buffer                       |
+ | =emulator/opcodes_00_09.asm= | Implementation for virtual machine opcodes 0-9.                 |
+ | =emulator/opcodes_10_19.asm= | Implementation for virtual machine opcodes 10-19.               |
+ | =emulator/opcodes_20_29.asm= | Implementation for virtual machine opcodes 20-29.               |
+ | =emulator/opcodes_30_39.asm= | Implementation for virtual machine opcodes 30-39.               |
+ | =emulator/opcodes_40_49.asm= | Implementation for virtual machine opcodes 40-49.               |
+ | =emulator/compile.sh=        | Emulator build script                                           |
+ | =emulator/emulator.com=      | Compiled DOS COM binary (do not edit)                           |
 
 
 ## High-Level Boot Code (Fifth language)
diff --git a/emulator/charput.asm b/emulator/charput.asm
new file mode 100644 (file)
index 0000000..050df56
--- /dev/null
@@ -0,0 +1,102 @@
+; Draw character
+;
+; Stack Effect: colorfg colorbg addrsrc addrdest x y --
+;
+; Draws an 8x8 character from a source memory buffer to a destination
+; image buffer at the specified (x, y) coordinates. Each byte in the
+; source buffer represents one row of the character (8 bits per row),
+; where each bit determines whether to use the foreground or background
+; color for that pixel.
+;
+; Parameters:
+; - colorfg: Foreground color value (0-255)
+; - colorbg: Background color value (0-255)
+; - addrsrc: Memory address pointing to 8 bytes of character data
+; - addrdest: Base address of the destination image buffer
+; - x: X-coordinate (0-based) where the character's left edge starts
+; - y: Y-coordinate (0-based) where the character's top edge starts
+;
+; Example:
+;   0xFF  ; White foreground
+;   0x00  ; Black background
+;   0x3000  ; Character data address
+;   0x5000  ; Video memory address
+;   20      ; y
+;   10      ; x
+;   charput
+;
+; Memory layout before execution:
+;   [es:edi] = y
+;   [es:edi+4] = x
+;   [es:edi+8] = addrdest
+;   [es:edi+12] = addrsrc
+;   [es:edi+16] = colorbg
+;   [es:edi+20] = colorfg
+;
+; Memory layout after execution:
+;   Character drawn at (x, y) in destination image
+
+op_47_xcharput:
+       mov     eax, [es:edi]       ; eax = y coordinate (top of stack)
+       mov     [chary], eax        ; store y coordinate
+       mov     ecx, [es:edi+4]     ; ecx = x coordinate (second on stack)
+       mov     eax, [es:edi+8]     ; eax = addrdest (destination image virtual address)
+       add     eax, [xms_addr]     ; convert virtual address to physical address
+       mov     ebx, [es:eax]       ; ebx = destination image width from image header
+       mov     [sizex], ebx        ; store destination image width (stride)
+       add     eax, 8              ; skip past image header (width + height = 8 bytes)
+       add     eax, ecx            ; add x offset to base of pixel data
+       push    eax                 ; save partial destination address on x86 stack
+       sub     edx, edx            ; clear edx for multiplication
+       mov     eax, [chary]        ; eax = y coordinate
+       mul     dword [sizex]       ; eax = y * image width (row offset in bytes)
+       pop     ebx                 ; ebx = base + x offset
+       add     eax, ebx            ; eax = final destination pixel address
+       mov     [addrdst], eax      ; store destination address for character drawing
+       mov     eax, [es:edi+12]    ; eax = addrsrc (source font data virtual address)
+       add     eax, [xms_addr]     ; convert virtual address to physical address
+       mov     [addrsrc], eax      ; store source font data address
+       mov     al, [es:edi+16]     ; al = background color value
+       mov     [colorbg], al       ; store background color
+       mov     al, [es:edi+20]     ; al = foreground color value
+       mov     [colorfg], al       ; store foreground color
+       add     edi, 24             ; pop all six arguments off the data stack
+
+       mov     [linenum], 8        ; linenum = 8 (number of rows in character glyph)
+charl1:
+       mov     eax, [addrsrc]      ; eax = current source font data address
+       mov     bx, [es:eax]        ; bx = current row bitmap (8 bits used)
+       mov     edx, [addrdst]      ; edx = current destination pixel address
+       mov     cx, 8               ; cx = 8 (number of pixels per row)
+charl2:
+       dec     cx                  ; decrement bit index (process bits 7 down to 0)
+       bt      bx, cx              ; test bit cx of the row bitmap
+       jnc     charl3              ; if bit is 0 (clear), use background color
+       mov     al, [colorfg]       ; bit is 1 (set): al = foreground color
+       jmp     charl4              ; skip to pixel write
+charl3:
+       mov     al, [colorbg]       ; bit is 0 (clear): al = background color
+charl4:
+       mov     [es:edx], al        ; write pixel color to destination
+       inc     edx                 ; advance destination to next pixel (next column)
+       cmp     cx, 0               ; check if all 8 pixels in this row are done
+       jne      charl2             ; if not, process next pixel
+
+       mov     eax, [sizex]        ; eax = image width (stride)
+       add     [addrdst], eax      ; advance destination to next row
+       inc     [addrsrc]           ; advance source to next byte (next row bitmap)
+       dec     [linenum]           ; decrement remaining line count
+       mov     al, [linenum]       ; al = remaining lines
+       cmp     al, 0               ; check if all 8 rows are done
+       jne     charl1              ; if not, draw next row
+
+       jmp     emu                 ; return to emulation loop
+
+colorfg db 0
+colorbg db 0
+charx  dd 0
+chary  dd 0
+addrsrc dd 0
+addrdst dd 0
+sizex  dd 0
+linenum db 0
diff --git a/emulator/charput.inc b/emulator/charput.inc
deleted file mode 100644 (file)
index 050df56..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-; Draw character
-;
-; Stack Effect: colorfg colorbg addrsrc addrdest x y --
-;
-; Draws an 8x8 character from a source memory buffer to a destination
-; image buffer at the specified (x, y) coordinates. Each byte in the
-; source buffer represents one row of the character (8 bits per row),
-; where each bit determines whether to use the foreground or background
-; color for that pixel.
-;
-; Parameters:
-; - colorfg: Foreground color value (0-255)
-; - colorbg: Background color value (0-255)
-; - addrsrc: Memory address pointing to 8 bytes of character data
-; - addrdest: Base address of the destination image buffer
-; - x: X-coordinate (0-based) where the character's left edge starts
-; - y: Y-coordinate (0-based) where the character's top edge starts
-;
-; Example:
-;   0xFF  ; White foreground
-;   0x00  ; Black background
-;   0x3000  ; Character data address
-;   0x5000  ; Video memory address
-;   20      ; y
-;   10      ; x
-;   charput
-;
-; Memory layout before execution:
-;   [es:edi] = y
-;   [es:edi+4] = x
-;   [es:edi+8] = addrdest
-;   [es:edi+12] = addrsrc
-;   [es:edi+16] = colorbg
-;   [es:edi+20] = colorfg
-;
-; Memory layout after execution:
-;   Character drawn at (x, y) in destination image
-
-op_47_xcharput:
-       mov     eax, [es:edi]       ; eax = y coordinate (top of stack)
-       mov     [chary], eax        ; store y coordinate
-       mov     ecx, [es:edi+4]     ; ecx = x coordinate (second on stack)
-       mov     eax, [es:edi+8]     ; eax = addrdest (destination image virtual address)
-       add     eax, [xms_addr]     ; convert virtual address to physical address
-       mov     ebx, [es:eax]       ; ebx = destination image width from image header
-       mov     [sizex], ebx        ; store destination image width (stride)
-       add     eax, 8              ; skip past image header (width + height = 8 bytes)
-       add     eax, ecx            ; add x offset to base of pixel data
-       push    eax                 ; save partial destination address on x86 stack
-       sub     edx, edx            ; clear edx for multiplication
-       mov     eax, [chary]        ; eax = y coordinate
-       mul     dword [sizex]       ; eax = y * image width (row offset in bytes)
-       pop     ebx                 ; ebx = base + x offset
-       add     eax, ebx            ; eax = final destination pixel address
-       mov     [addrdst], eax      ; store destination address for character drawing
-       mov     eax, [es:edi+12]    ; eax = addrsrc (source font data virtual address)
-       add     eax, [xms_addr]     ; convert virtual address to physical address
-       mov     [addrsrc], eax      ; store source font data address
-       mov     al, [es:edi+16]     ; al = background color value
-       mov     [colorbg], al       ; store background color
-       mov     al, [es:edi+20]     ; al = foreground color value
-       mov     [colorfg], al       ; store foreground color
-       add     edi, 24             ; pop all six arguments off the data stack
-
-       mov     [linenum], 8        ; linenum = 8 (number of rows in character glyph)
-charl1:
-       mov     eax, [addrsrc]      ; eax = current source font data address
-       mov     bx, [es:eax]        ; bx = current row bitmap (8 bits used)
-       mov     edx, [addrdst]      ; edx = current destination pixel address
-       mov     cx, 8               ; cx = 8 (number of pixels per row)
-charl2:
-       dec     cx                  ; decrement bit index (process bits 7 down to 0)
-       bt      bx, cx              ; test bit cx of the row bitmap
-       jnc     charl3              ; if bit is 0 (clear), use background color
-       mov     al, [colorfg]       ; bit is 1 (set): al = foreground color
-       jmp     charl4              ; skip to pixel write
-charl3:
-       mov     al, [colorbg]       ; bit is 0 (clear): al = background color
-charl4:
-       mov     [es:edx], al        ; write pixel color to destination
-       inc     edx                 ; advance destination to next pixel (next column)
-       cmp     cx, 0               ; check if all 8 pixels in this row are done
-       jne      charl2             ; if not, process next pixel
-
-       mov     eax, [sizex]        ; eax = image width (stride)
-       add     [addrdst], eax      ; advance destination to next row
-       inc     [addrsrc]           ; advance source to next byte (next row bitmap)
-       dec     [linenum]           ; decrement remaining line count
-       mov     al, [linenum]       ; al = remaining lines
-       cmp     al, 0               ; check if all 8 rows are done
-       jne     charl1              ; if not, draw next row
-
-       jmp     emu                 ; return to emulation loop
-
-colorfg db 0
-colorbg db 0
-charx  dd 0
-chary  dd 0
-addrsrc dd 0
-addrdst dd 0
-sizex  dd 0
-linenum db 0
index 6185e1c..bcacad2 100644 (file)
@@ -126,11 +126,11 @@ table:
     dw op_47_xcharput   ; 47 - render character glyph
 
 
-include 'opcodes_00_09.inc'
-include 'opcodes_10_19.inc'
-include 'opcodes_20_29.inc'
-include 'opcodes_30_39.inc'
-include 'opcodes_40_47.inc'
+include 'opcodes_00_09.asm'
+include 'opcodes_10_19.asm'
+include 'opcodes_20_29.asm'
+include 'opcodes_30_39.asm'
+include 'opcodes_40_49.asm'
 
 file_seek:     ; ( ecx - pointer to seek )
        mov     eax, 1024
@@ -186,7 +186,7 @@ diskload:   ; ecx-fromdisk ebx-tomem
        call    memmove  ; ebx - from, edx - to, ecx - amount
        ret
 
-include 'system.inc'
-include 'kbdrive.inc'
+include 'system.asm'
+include 'kbdrive.asm'
 
 buf:                   ; pointer to end of the code
diff --git a/emulator/kbdrive.asm b/emulator/kbdrive.asm
new file mode 100644 (file)
index 0000000..9291131
--- /dev/null
@@ -0,0 +1,83 @@
+; Keyboard driver routines
+
+; KB_init
+;
+; Initializes the keyboard interrupt handler.
+; Saves the old interrupt vector for INT 9h and sets up the new handler.
+;
+; Notes:
+; - INT 9h is the keyboard interrupt vector.
+; - The handler stores scan codes in a circular buffer.
+
+KB_init:
+        push    es              
+        push    0
+        pop     es
+        mov     eax, [es:9*4]           ; save old int vector
+        mov     [KB_OldVect], eax 
+        mov     ax, cs                  ; set new int vector
+        shl     eax, 16
+        mov     ax, KB_IntHandler
+        mov     [es:9*4], eax
+        pop     es
+        ret
+
+; KB_restore
+;
+; Restores the original keyboard interrupt handler.
+; Used when exiting the emulator to restore the system's default handler.
+
+KB_restore:
+        mov     eax, [KB_OldVect]
+        push    es
+        push    0
+        pop     es
+        mov     [es:9*4], eax 
+        pop     es
+        ret
+
+; KB_IntHandler
+;
+; Keyboard interrupt handler.
+; Reads scan code from port 60h and stores it in the circular buffer.
+; Then calls the original interrupt handler.
+
+KB_IntHandler:
+        pusha
+        in      al, 60h
+        mov     bx, [cs:KB_pntin]
+        mov     byte [cs:bx+KB_buf], al
+       inc     bx
+       cmp     bx, 128
+       jng     KB_l1
+       mov     bx, 0
+KB_l1: mov     [cs:KB_pntin], bx
+        popa
+        pushf                   ; Execute default code in old int vector
+        call    dword [cs:KB_OldVect]
+        iret
+
+; KB_read
+;
+; Reads a scan code from the keyboard buffer.
+; Returns the scan code in DL.
+; If buffer is empty, returns 0.
+
+KB_read:       ; returns scan code in: dl
+       mov     dl, 0
+       mov     bx, [KB_pntout]
+       cmp     bx, [KB_pntin]
+       je      KB_l2
+       mov     dl, [bx+KB_buf]
+       inc     bx
+       cmp     bx, 128
+       jng     KB_l3
+       mov     bx, 0
+KB_l3: mov     [KB_pntout], bx
+KB_l2: ret
+
+KB_OldVect dd 0
+KB_pntin dw 0
+KB_pntout dw 0
+KB_buf db 0
+times 127 db 0
diff --git a/emulator/kbdrive.inc b/emulator/kbdrive.inc
deleted file mode 100644 (file)
index 9291131..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-; Keyboard driver routines
-
-; KB_init
-;
-; Initializes the keyboard interrupt handler.
-; Saves the old interrupt vector for INT 9h and sets up the new handler.
-;
-; Notes:
-; - INT 9h is the keyboard interrupt vector.
-; - The handler stores scan codes in a circular buffer.
-
-KB_init:
-        push    es              
-        push    0
-        pop     es
-        mov     eax, [es:9*4]           ; save old int vector
-        mov     [KB_OldVect], eax 
-        mov     ax, cs                  ; set new int vector
-        shl     eax, 16
-        mov     ax, KB_IntHandler
-        mov     [es:9*4], eax
-        pop     es
-        ret
-
-; KB_restore
-;
-; Restores the original keyboard interrupt handler.
-; Used when exiting the emulator to restore the system's default handler.
-
-KB_restore:
-        mov     eax, [KB_OldVect]
-        push    es
-        push    0
-        pop     es
-        mov     [es:9*4], eax 
-        pop     es
-        ret
-
-; KB_IntHandler
-;
-; Keyboard interrupt handler.
-; Reads scan code from port 60h and stores it in the circular buffer.
-; Then calls the original interrupt handler.
-
-KB_IntHandler:
-        pusha
-        in      al, 60h
-        mov     bx, [cs:KB_pntin]
-        mov     byte [cs:bx+KB_buf], al
-       inc     bx
-       cmp     bx, 128
-       jng     KB_l1
-       mov     bx, 0
-KB_l1: mov     [cs:KB_pntin], bx
-        popa
-        pushf                   ; Execute default code in old int vector
-        call    dword [cs:KB_OldVect]
-        iret
-
-; KB_read
-;
-; Reads a scan code from the keyboard buffer.
-; Returns the scan code in DL.
-; If buffer is empty, returns 0.
-
-KB_read:       ; returns scan code in: dl
-       mov     dl, 0
-       mov     bx, [KB_pntout]
-       cmp     bx, [KB_pntin]
-       je      KB_l2
-       mov     dl, [bx+KB_buf]
-       inc     bx
-       cmp     bx, 128
-       jng     KB_l3
-       mov     bx, 0
-KB_l3: mov     [KB_pntout], bx
-KB_l2: ret
-
-KB_OldVect dd 0
-KB_pntin dw 0
-KB_pntout dw 0
-KB_buf db 0
-times 127 db 0
diff --git a/emulator/opcodes_00_09.asm b/emulator/opcodes_00_09.asm
new file mode 100644 (file)
index 0000000..eadc940
--- /dev/null
@@ -0,0 +1,89 @@
+; Opcodes 0-9: nop, halt, kbd@, num, jmp, call, inc, dec, dup, drop
+; Opcode 0 (xnop) is handled inline as the emu loop entry point.
+; Opcode 1 (xhalt/quit) is handled in the main emulator file.
+
+op_02_kbd@:                     ; Read Keyboard: ( -- scancode )
+       call    KB_read             ; read scancode from keyboard ring buffer into dl
+       sub         edi, 4              ; make room on data stack for one element
+       mov         [es:edi], dl        ; push scancode onto data stack
+
+    mov     ah, 0bh             ; DOS: check if key waiting in keyboard buffer
+       int         21h                 ; call DOS interrupt
+       cmp         al, 0h              ; al=0 means no key waiting
+    je      emu                 ; if no key waiting, return to emulation loop
+    mov     ah, 0               ; BIOS: read key from keyboard buffer
+       int         16h                 ; call BIOS interrupt (consume the key)
+       jmp     emu                 ; return to emulation loop
+
+op_03_xnum:                         ; Push Literal: ( -- n )
+       mov         edx, dword [es:esi] ; read 32-bit literal from instruction stream
+       sub         edi, 4              ; make room on data stack for one element
+       mov         [es:edi], edx       ; push the literal value onto data stack
+       add         esi, 4              ; advance instruction pointer past the 32-bit literal
+       jmp         emu                 ; return to emulation loop
+
+op_04_xjmp:                     ; Unconditional Jump: ( -- )
+       mov         esi, dword [es:esi] ; read 32-bit target address from instruction stream
+       add         esi, [xms_addr]     ; convert virtual address to physical address
+       jmp         emu                 ; return to emulation loop
+
+op_05_xcall:                    ; Call Subroutine: ( -- ) R:( -- ret-addr )
+       mov         edx, dword [es:esi] ; read 32-bit target address from instruction stream
+       mov         eax, [resp]         ; load return stack pointer
+       sub         eax, 4              ; make room on return stack for one element
+       mov         ebx, esi            ; copy current instruction pointer
+       add         ebx, 4              ; advance past the 32-bit argument (return address)
+       sub         ebx, [xms_addr]     ; convert physical address to virtual address
+       mov         [es:eax], ebx       ; push return address onto return stack
+       mov         [resp], eax         ; store updated return stack pointer
+       mov         esi, edx            ; set instruction pointer to target address
+       add         esi, [xms_addr]     ; convert virtual address to physical address
+       jmp         emu                 ; return to emulation loop
+
+op_06_xinc:                     ; Increment: ( n -- n+1 )
+; Increments the top of the data stack by 1.
+
+       inc         dword [es:edi]      ; increment the top of the data stack in place
+       jmp         emu                 ; return to emulation loop
+
+op_07_xdec:                     ; Decrement: ( n -- n-1 )
+;
+; Decrements the top of the data stack by 1.
+;
+; Memory layout before execution:
+;   [es:edi] = n
+;
+; Memory layout after execution:
+;   [es:edi] = n-1
+
+       dec         dword [es:edi]      ; decrement the top of the data stack in place
+       jmp         emu                 ; return to emulation loop
+
+op_08_xdup:                     ; Duplicate: ( n -- n n )
+;
+; Duplicates the top element of the data stack.
+;
+; Memory layout before execution:
+;   [es:edi] = n
+;
+; Memory layout after execution:
+;   [es:edi]   = n (new top)
+;   [es:edi+4] = n (original top)
+
+       mov         eax, [es:edi]       ; copy top element to eax
+       sub         edi, 4              ; make room on data stack for one element
+       mov         [es:edi], eax       ; push copy of top element onto stack
+       jmp         emu                 ; return to emulation loop
+
+op_09_xdrop:                    ; Drop: ( n -- )
+;
+; Removes the top element from the data stack.
+;
+; Memory layout before execution:
+;   [es:edi] = n
+;
+; Memory layout after execution:
+;   Stack pointer is adjusted; n is no longer on stack
+
+       add         edi, 4              ; pop top element off the stack
+       jmp         emu                 ; return to emulation loop
diff --git a/emulator/opcodes_00_09.inc b/emulator/opcodes_00_09.inc
deleted file mode 100644 (file)
index d7a721c..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-; Opcodes 0-9: nop, halt, kbd@, num, jmp, call, inc, dec, dup, drop
-; Opcode 0 (xnop) is handled inline as the emu loop entry point.
-; Opcode 1 (xhalt/quit) is handled in the main emulator file.
-
-op_02_kbd@:                     ; Read Keyboard: ( -- scancode )
-       call    KB_read             ; read scancode from keyboard ring buffer into dl
-       sub         edi, 4              ; make room on data stack for one element
-       mov         [es:edi], dl        ; push scancode onto data stack
-
-    mov     ah, 0bh             ; DOS: check if key waiting in keyboard buffer
-       int         21h                 ; call DOS interrupt
-       cmp         al, 0h              ; al=0 means no key waiting
-    je      emu                 ; if no key waiting, return to emulation loop
-    mov     ah, 0               ; BIOS: read key from keyboard buffer
-       int         16h                 ; call BIOS interrupt (consume the key)
-       jmp     emu                 ; return to emulation loop
-
-op_03_xnum:                         ; Push Literal: ( -- n )
-       mov         edx, dword [es:esi] ; read 32-bit literal from instruction stream
-       sub         edi, 4              ; make room on data stack for one element
-       mov         [es:edi], edx       ; push the literal value onto data stack
-       add         esi, 4              ; advance instruction pointer past the 32-bit literal
-       jmp         emu                 ; return to emulation loop
-
-op_04_xjmp:                     ; Unconditional Jump: ( -- )
-       mov         esi, dword [es:esi] ; read 32-bit target address from instruction stream
-       add     e   si, [xms_addr]      ; convert virtual address to physical address
-       jmp         emu                 ; return to emulation loop
-
-op_05_xcall:                    ; Call Subroutine: ( -- ) R:( -- ret-addr )
-       mov         edx, dword [es:esi] ; read 32-bit target address from instruction stream
-       mov         eax, [resp]         ; load return stack pointer
-       sub         eax, 4              ; make room on return stack for one element
-       mov         ebx, esi            ; copy current instruction pointer
-       add         ebx, 4              ; advance past the 32-bit argument (return address)
-       sub         ebx, [xms_addr]     ; convert physical address to virtual address
-       mov         [es:eax], ebx       ; push return address onto return stack
-       mov         [resp], eax         ; store updated return stack pointer
-       mov         esi, edx            ; set instruction pointer to target address
-       add         esi, [xms_addr]     ; convert virtual address to physical address
-       jmp         emu                 ; return to emulation loop
-
-op_06_xinc:                     ; Increment: ( n -- n+1 )
-; Increments the top of the data stack by 1.
-
-       inc         dword [es:edi]      ; increment the top of the data stack in place
-       jmp         emu                 ; return to emulation loop
-
-op_07_xdec:                     ; Decrement: ( n -- n-1 )
-;
-; Decrements the top of the data stack by 1.
-;
-; Memory layout before execution:
-;   [es:edi] = n
-;
-; Memory layout after execution:
-;   [es:edi] = n-1
-
-       dec         dword [es:edi]      ; decrement the top of the data stack in place
-       jmp         emu                 ; return to emulation loop
-
-op_08_xdup:                     ; Duplicate: ( n -- n n )
-;
-; Duplicates the top element of the data stack.
-;
-; Memory layout before execution:
-;   [es:edi] = n
-;
-; Memory layout after execution:
-;   [es:edi]   = n (new top)
-;   [es:edi+4] = n (original top)
-
-       mov         eax, [es:edi]       ; copy top element to eax
-       sub         edi, 4              ; make room on data stack for one element
-       mov         [es:edi], eax       ; push copy of top element onto stack
-       jmp         emu                 ; return to emulation loop
-
-op_09_xdrop:                    ; Drop: ( n -- )
-;
-; Removes the top element from the data stack.
-;
-; Memory layout before execution:
-;   [es:edi] = n
-;
-; Memory layout after execution:
-;   Stack pointer is adjusted; n is no longer on stack
-
-       add         edi, 4              ; pop top element off the stack
-       jmp         emu                 ; return to emulation loop
diff --git a/emulator/opcodes_10_19.asm b/emulator/opcodes_10_19.asm
new file mode 100644 (file)
index 0000000..11e5d4a
--- /dev/null
@@ -0,0 +1,92 @@
+; Opcodes 10-19: if, ret, c@, c!, push, pop, (unused), rot, disk@, disk!
+
+op_10_xif:                          ; Conditional Jump: ( flag -- )
+       mov     eax, [es:edi]       ; eax = flag (top of data stack)
+       add     edi, 4              ; pop flag off the data stack
+       cmp     eax, 0              ; test if flag is zero
+       jne     l2                  ; if flag is non-zero, skip the jump
+       mov     esi, [es:esi]       ; read 32-bit target address from instruction stream
+       add     esi, [xms_addr]     ; convert virtual address to physical address
+       jmp     emu                 ; return to emulation loop
+l2:
+       add     esi, 4              ; skip past the 32-bit jump target in instruction stream
+       jmp     emu                 ; return to emulation loop
+
+op_11_xret:                         ; Return: ( -- ) R:( ret-addr -- )
+       mov     eax, [resp]         ; load return stack pointer
+       mov     esi, [es:eax]       ; pop return address from return stack
+       add     esi, [xms_addr]     ; convert virtual address to physical address
+       add     eax, 4              ; adjust return stack pointer (pop)
+       mov     [resp], eax         ; store updated return stack pointer
+       jmp     emu                 ; return to emulation loop
+
+op_12_xc@:                          ; Fetch Byte: ( addr -- byte )
+       mov     eax, [es:edi]       ; eax = virtual address from top of data stack
+       add     eax, [xms_addr]     ; convert virtual address to physical address
+       sub     ecx, ecx            ; clear ecx (zero-extend the byte)
+       mov     cl, [es:eax]        ; read one byte from memory into cl
+       mov     [es:edi], ecx       ; replace address on stack with the fetched byte value
+       jmp     emu                 ; return to emulation loop
+
+op_13_xc!:                          ; Store Byte: ( byte addr -- )
+       mov     ebx, [es:edi]       ; ebx = virtual address (top of stack)
+       add     edi, 4              ; pop address off the data stack
+       mov     ecx, [es:edi]       ; ecx = byte value (second on stack, only cl used)
+       add     edi, 4              ; pop byte value off the data stack
+       add     ebx, [xms_addr]     ; convert virtual address to physical address
+       mov     [es:ebx], cl        ; store low byte of ecx to memory
+       jmp     emu                 ; return to emulation loop
+
+op_14_xpush:                        ; Push to Return Stack: ( n -- ) R:( -- n )
+       mov     ebx, [es:edi]       ; ebx = top of data stack
+       add     edi, 4              ; pop value off the data stack
+       mov     eax, [resp]         ; load return stack pointer
+       sub     eax, 4              ; make room on return stack for one element
+       mov     [es:eax], ebx       ; push value onto return stack
+       mov     [resp], eax         ; store updated return stack pointer
+       jmp     emu                 ; return to emulation loop
+
+op_15_xpop:                         ; Pop from Return Stack: ( -- n ) R:( n -- )
+       mov     eax, [resp]         ; load return stack pointer
+       mov     ebx, [es:eax]       ; read value from top of return stack
+       add     eax, 4              ; adjust return stack pointer (pop)
+       mov     [resp], eax         ; store updated return stack pointer
+       sub     edi, 4              ; make room on data stack for one element
+       mov     [es:edi], ebx       ; push value onto data stack
+       jmp     emu                 ; return to emulation loop
+
+op_17_xrot:                         ; Rotate: ( a b c -- b c a )
+       mov     ebx, [es:edi]       ; ebx = c (top of stack)
+       mov     ecx, [es:edi+4]     ; ecx = b (second on stack)
+       mov     edx, [es:edi+8]     ; edx = a (third on stack)
+       mov     [es:edi+8], ecx     ; third slot = b
+       mov     [es:edi+4], ebx     ; second slot = c
+       mov     [es:edi], edx       ; top slot = a
+       jmp     emu                 ; return to emulation loop
+
+op_18_xdisk@:                       ; Disk Load: ( sector mem -- )
+       mov     ebx, [es:edi]       ; ebx = destination memory address (virtual)
+       add     ebx, [xms_addr]     ; convert virtual address to physical address
+       mov     ecx, [es:edi+4]     ; ecx = sector number
+       add     edi, 8              ; pop both arguments off the data stack
+       call    diskload            ; load 1024 bytes: ecx=sector, ebx=destination
+       jmp     emu                 ; return to emulation loop
+
+op_19_xdisk!:                       ; Disk Save: ( mem sector -- )
+       mov     ecx, [es:edi]       ; ecx = sector number (top of stack)
+       call    file_seek           ; seek to the correct position in disk file
+       mov     ecx, 1024           ; number of bytes to write
+       mov     ebx, [es:edi+4]     ; ebx = source memory address (virtual)
+       add     edi, 8              ; pop both arguments off the data stack
+       add     ebx, [xms_addr]     ; convert virtual address to physical address
+       sub     edx, edx            ; clear edx
+       mov     dx, cs              ; edx = code segment
+       shl     edx, 4              ; convert segment to linear address
+       add     edx, buf            ; edx = physical address of temporary buffer
+       call    memmove             ; copy from XMS to temporary buffer
+       mov     ah, 40h             ; DOS: write to file
+       mov     bx, [fileh]         ; file handle
+       mov     cx, 1024            ; number of bytes to write
+       mov     dx, buf             ; source buffer address
+       int     21h                 ; call DOS interrupt
+       jmp     emu                 ; return to emulation loop
diff --git a/emulator/opcodes_10_19.inc b/emulator/opcodes_10_19.inc
deleted file mode 100644 (file)
index 11e5d4a..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-; Opcodes 10-19: if, ret, c@, c!, push, pop, (unused), rot, disk@, disk!
-
-op_10_xif:                          ; Conditional Jump: ( flag -- )
-       mov     eax, [es:edi]       ; eax = flag (top of data stack)
-       add     edi, 4              ; pop flag off the data stack
-       cmp     eax, 0              ; test if flag is zero
-       jne     l2                  ; if flag is non-zero, skip the jump
-       mov     esi, [es:esi]       ; read 32-bit target address from instruction stream
-       add     esi, [xms_addr]     ; convert virtual address to physical address
-       jmp     emu                 ; return to emulation loop
-l2:
-       add     esi, 4              ; skip past the 32-bit jump target in instruction stream
-       jmp     emu                 ; return to emulation loop
-
-op_11_xret:                         ; Return: ( -- ) R:( ret-addr -- )
-       mov     eax, [resp]         ; load return stack pointer
-       mov     esi, [es:eax]       ; pop return address from return stack
-       add     esi, [xms_addr]     ; convert virtual address to physical address
-       add     eax, 4              ; adjust return stack pointer (pop)
-       mov     [resp], eax         ; store updated return stack pointer
-       jmp     emu                 ; return to emulation loop
-
-op_12_xc@:                          ; Fetch Byte: ( addr -- byte )
-       mov     eax, [es:edi]       ; eax = virtual address from top of data stack
-       add     eax, [xms_addr]     ; convert virtual address to physical address
-       sub     ecx, ecx            ; clear ecx (zero-extend the byte)
-       mov     cl, [es:eax]        ; read one byte from memory into cl
-       mov     [es:edi], ecx       ; replace address on stack with the fetched byte value
-       jmp     emu                 ; return to emulation loop
-
-op_13_xc!:                          ; Store Byte: ( byte addr -- )
-       mov     ebx, [es:edi]       ; ebx = virtual address (top of stack)
-       add     edi, 4              ; pop address off the data stack
-       mov     ecx, [es:edi]       ; ecx = byte value (second on stack, only cl used)
-       add     edi, 4              ; pop byte value off the data stack
-       add     ebx, [xms_addr]     ; convert virtual address to physical address
-       mov     [es:ebx], cl        ; store low byte of ecx to memory
-       jmp     emu                 ; return to emulation loop
-
-op_14_xpush:                        ; Push to Return Stack: ( n -- ) R:( -- n )
-       mov     ebx, [es:edi]       ; ebx = top of data stack
-       add     edi, 4              ; pop value off the data stack
-       mov     eax, [resp]         ; load return stack pointer
-       sub     eax, 4              ; make room on return stack for one element
-       mov     [es:eax], ebx       ; push value onto return stack
-       mov     [resp], eax         ; store updated return stack pointer
-       jmp     emu                 ; return to emulation loop
-
-op_15_xpop:                         ; Pop from Return Stack: ( -- n ) R:( n -- )
-       mov     eax, [resp]         ; load return stack pointer
-       mov     ebx, [es:eax]       ; read value from top of return stack
-       add     eax, 4              ; adjust return stack pointer (pop)
-       mov     [resp], eax         ; store updated return stack pointer
-       sub     edi, 4              ; make room on data stack for one element
-       mov     [es:edi], ebx       ; push value onto data stack
-       jmp     emu                 ; return to emulation loop
-
-op_17_xrot:                         ; Rotate: ( a b c -- b c a )
-       mov     ebx, [es:edi]       ; ebx = c (top of stack)
-       mov     ecx, [es:edi+4]     ; ecx = b (second on stack)
-       mov     edx, [es:edi+8]     ; edx = a (third on stack)
-       mov     [es:edi+8], ecx     ; third slot = b
-       mov     [es:edi+4], ebx     ; second slot = c
-       mov     [es:edi], edx       ; top slot = a
-       jmp     emu                 ; return to emulation loop
-
-op_18_xdisk@:                       ; Disk Load: ( sector mem -- )
-       mov     ebx, [es:edi]       ; ebx = destination memory address (virtual)
-       add     ebx, [xms_addr]     ; convert virtual address to physical address
-       mov     ecx, [es:edi+4]     ; ecx = sector number
-       add     edi, 8              ; pop both arguments off the data stack
-       call    diskload            ; load 1024 bytes: ecx=sector, ebx=destination
-       jmp     emu                 ; return to emulation loop
-
-op_19_xdisk!:                       ; Disk Save: ( mem sector -- )
-       mov     ecx, [es:edi]       ; ecx = sector number (top of stack)
-       call    file_seek           ; seek to the correct position in disk file
-       mov     ecx, 1024           ; number of bytes to write
-       mov     ebx, [es:edi+4]     ; ebx = source memory address (virtual)
-       add     edi, 8              ; pop both arguments off the data stack
-       add     ebx, [xms_addr]     ; convert virtual address to physical address
-       sub     edx, edx            ; clear edx
-       mov     dx, cs              ; edx = code segment
-       shl     edx, 4              ; convert segment to linear address
-       add     edx, buf            ; edx = physical address of temporary buffer
-       call    memmove             ; copy from XMS to temporary buffer
-       mov     ah, 40h             ; DOS: write to file
-       mov     bx, [fileh]         ; file handle
-       mov     cx, 1024            ; number of bytes to write
-       mov     dx, buf             ; source buffer address
-       int     21h                 ; call DOS interrupt
-       jmp     emu                 ; return to emulation loop
diff --git a/emulator/opcodes_20_29.asm b/emulator/opcodes_20_29.asm
new file mode 100644 (file)
index 0000000..03aba5b
--- /dev/null
@@ -0,0 +1,95 @@
+; Opcodes 20-29: @, !, over, swap, plus, minus, mul, div, great, less
+
+op_20_x@:                       ; Fetch 32-bit: ( addr -- n )
+       mov     eax, [es:edi]           ; eax = virtual address from top of data stack
+       add     eax, [xms_addr]         ; convert virtual address to physical address
+       mov     eax, [es:eax]           ; read 32-bit value from memory
+       mov     [es:edi], eax           ; replace address on stack with the fetched value
+       jmp     emu                     ; return to emulation loop
+
+op_21_x!:                       ; Store 32-bit: ( n addr -- )
+       mov         eax, [es:edi]       ; eax = virtual address (top of stack)
+       add         eax, [xms_addr]     ; convert virtual address to physical address
+       mov         ecx, [es:edi+4]     ; ecx = value to store (second on stack)
+       add         edi, 8              ; pop both arguments off the data stack
+       mov         [es:eax], ecx       ; store 32-bit value to memory
+       jmp         emu                 ; return to emulation loop
+
+op_22_xover:                    ; Over: ( a b -- a b a )
+       mov         ebx, [es:edi+4]     ; ebx = a (second element on stack)
+       sub         edi, 4              ; make room on data stack for one element
+       mov         [es:edi], ebx       ; push copy of a onto top of stack
+       jmp         emu                 ; return to emulation loop
+
+op_23_xswap:                    ; Swap: ( a b -- b a )
+       mov         ebx, [es:edi]       ; ebx = b (top of stack)
+       xchg    ebx, [es:edi+4]     ; exchange ebx with a (second on stack); ebx=a, [edi+4]=b
+       mov         [es:edi], ebx       ; top of stack = a
+       jmp         emu                 ; return to emulation loop
+
+op_24_xplus:                    ; Add: ( n1 n2 -- n1+n2 )
+       mov         ebx, [es:edi]       ; ebx = n2 (the addend — top of stack)
+       add         edi, 4              ; pop n2 off the stack; n1 is now the new top
+       add         [es:edi], ebx       ; [es:edi] = n1 + n2 (add n2 to n1 in place)
+       jmp         emu                 ; return to emulation loop
+
+op_25_xminus:                   ; Subtract: ( n1 n2 -- n1-n2 )
+;
+; Pops the top two elements from the data stack, subtracts the top
+; element (n2) from the second element (n1), and pushes the result.
+;
+; In Fifth source code, this corresponds to the "-" word:
+;
+;     10 3 -       \ result is 7  (10 minus 3)
+;
+; Argument order:
+;   n1 is pushed first  (sits deeper in the stack, at [es:edi+4])
+;   n2 is pushed second (sits on top of the stack,  at [es:edi])
+;
+; Memory layout before execution:
+;   [es:edi]   = n2  (top of stack — the value being subtracted)
+;   [es:edi+4] = n1  (second on stack — the value subtracted from)
+;
+; Memory layout after execution:
+;   [es:edi]   = n1 - n2  (edi has been adjusted; old n2 slot is freed)
+
+       mov         ebx, [es:edi]       ; ebx = n2 (the subtrahend — value to subtract)
+       add         edi, 4              ; pop n2 off the stack; n1 is now the new top
+       sub         [es:edi], ebx       ; [es:edi] = n1 - n2 (subtract n2 from n1 in place)
+       jmp         emu                 ; return to emulation loop
+
+op_26_xmul:                     ; Multiply (signed): ( n1 n2 -- n1*n2 )
+       mov         eax, [es:edi]       ; eax = n2 (top of stack)
+       add         edi, 4              ; pop n2 off the stack; n1 is now the new top
+       sub         edx, edx            ; clear edx before signed multiply
+       imul    dword [es:edi]      ; edx:eax = eax * n1 (signed multiply)
+       mov         [es:edi], eax       ; store low 32 bits of result as new top of stack
+       jmp         emu                 ; return to emulation loop
+
+op_27_xdiv:                     ; Divide (signed): ( n1 n2 -- n1/n2 )
+       add         edi, 4              ; pop n2; n1 is now the new top (but we read n2 from edi-4)
+       mov         eax, [es:edi]       ; eax = n1 (the dividend)
+       cdq                         ; sign-extend eax into edx:eax for signed division
+       idiv    dword [es:edi-4]    ; eax = edx:eax / n2 (signed divide)
+       mov         [es:edi], eax       ; store quotient as new top of stack
+       jmp         emu                 ; return to emulation loop
+
+op_28_xgreat:                   ; Greater Than: ( a b -- flag )
+       mov         eax, [es:edi]       ; eax = b (top of stack)
+       add         edi, 4              ; pop b off the stack; a is now the new top
+       mov         edx, 0              ; edx = 0 (default: false)
+       cmp         [es:edi], eax       ; compare a with b
+       jng         l3                  ; if a is NOT greater than b, skip setting flag
+       dec         edx                 ; edx = -1 (true flag, all bits set)
+l3:    mov         [es:edi], edx       ; store flag as new top of stack
+       jmp         emu                 ; return to emulation loop
+
+op_29_xless:                    ; Less Than: ( a b -- flag )
+       mov         eax, [es:edi]       ; eax = b (top of stack)
+       add         edi, 4              ; pop b off the stack; a is now the new top
+       mov         edx, 0              ; edx = 0 (default: false)
+       cmp         [es:edi], eax       ; compare a with b
+       jnl         l4                  ; if a is NOT less than b, skip setting flag
+       dec         edx                 ; edx = -1 (true flag, all bits set)
+l4:    mov         [es:edi], edx       ; store flag as new top of stack
+       jmp         emu                 ; return to emulation loop
diff --git a/emulator/opcodes_20_29.inc b/emulator/opcodes_20_29.inc
deleted file mode 100644 (file)
index 03aba5b..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-; Opcodes 20-29: @, !, over, swap, plus, minus, mul, div, great, less
-
-op_20_x@:                       ; Fetch 32-bit: ( addr -- n )
-       mov     eax, [es:edi]           ; eax = virtual address from top of data stack
-       add     eax, [xms_addr]         ; convert virtual address to physical address
-       mov     eax, [es:eax]           ; read 32-bit value from memory
-       mov     [es:edi], eax           ; replace address on stack with the fetched value
-       jmp     emu                     ; return to emulation loop
-
-op_21_x!:                       ; Store 32-bit: ( n addr -- )
-       mov         eax, [es:edi]       ; eax = virtual address (top of stack)
-       add         eax, [xms_addr]     ; convert virtual address to physical address
-       mov         ecx, [es:edi+4]     ; ecx = value to store (second on stack)
-       add         edi, 8              ; pop both arguments off the data stack
-       mov         [es:eax], ecx       ; store 32-bit value to memory
-       jmp         emu                 ; return to emulation loop
-
-op_22_xover:                    ; Over: ( a b -- a b a )
-       mov         ebx, [es:edi+4]     ; ebx = a (second element on stack)
-       sub         edi, 4              ; make room on data stack for one element
-       mov         [es:edi], ebx       ; push copy of a onto top of stack
-       jmp         emu                 ; return to emulation loop
-
-op_23_xswap:                    ; Swap: ( a b -- b a )
-       mov         ebx, [es:edi]       ; ebx = b (top of stack)
-       xchg    ebx, [es:edi+4]     ; exchange ebx with a (second on stack); ebx=a, [edi+4]=b
-       mov         [es:edi], ebx       ; top of stack = a
-       jmp         emu                 ; return to emulation loop
-
-op_24_xplus:                    ; Add: ( n1 n2 -- n1+n2 )
-       mov         ebx, [es:edi]       ; ebx = n2 (the addend — top of stack)
-       add         edi, 4              ; pop n2 off the stack; n1 is now the new top
-       add         [es:edi], ebx       ; [es:edi] = n1 + n2 (add n2 to n1 in place)
-       jmp         emu                 ; return to emulation loop
-
-op_25_xminus:                   ; Subtract: ( n1 n2 -- n1-n2 )
-;
-; Pops the top two elements from the data stack, subtracts the top
-; element (n2) from the second element (n1), and pushes the result.
-;
-; In Fifth source code, this corresponds to the "-" word:
-;
-;     10 3 -       \ result is 7  (10 minus 3)
-;
-; Argument order:
-;   n1 is pushed first  (sits deeper in the stack, at [es:edi+4])
-;   n2 is pushed second (sits on top of the stack,  at [es:edi])
-;
-; Memory layout before execution:
-;   [es:edi]   = n2  (top of stack — the value being subtracted)
-;   [es:edi+4] = n1  (second on stack — the value subtracted from)
-;
-; Memory layout after execution:
-;   [es:edi]   = n1 - n2  (edi has been adjusted; old n2 slot is freed)
-
-       mov         ebx, [es:edi]       ; ebx = n2 (the subtrahend — value to subtract)
-       add         edi, 4              ; pop n2 off the stack; n1 is now the new top
-       sub         [es:edi], ebx       ; [es:edi] = n1 - n2 (subtract n2 from n1 in place)
-       jmp         emu                 ; return to emulation loop
-
-op_26_xmul:                     ; Multiply (signed): ( n1 n2 -- n1*n2 )
-       mov         eax, [es:edi]       ; eax = n2 (top of stack)
-       add         edi, 4              ; pop n2 off the stack; n1 is now the new top
-       sub         edx, edx            ; clear edx before signed multiply
-       imul    dword [es:edi]      ; edx:eax = eax * n1 (signed multiply)
-       mov         [es:edi], eax       ; store low 32 bits of result as new top of stack
-       jmp         emu                 ; return to emulation loop
-
-op_27_xdiv:                     ; Divide (signed): ( n1 n2 -- n1/n2 )
-       add         edi, 4              ; pop n2; n1 is now the new top (but we read n2 from edi-4)
-       mov         eax, [es:edi]       ; eax = n1 (the dividend)
-       cdq                         ; sign-extend eax into edx:eax for signed division
-       idiv    dword [es:edi-4]    ; eax = edx:eax / n2 (signed divide)
-       mov         [es:edi], eax       ; store quotient as new top of stack
-       jmp         emu                 ; return to emulation loop
-
-op_28_xgreat:                   ; Greater Than: ( a b -- flag )
-       mov         eax, [es:edi]       ; eax = b (top of stack)
-       add         edi, 4              ; pop b off the stack; a is now the new top
-       mov         edx, 0              ; edx = 0 (default: false)
-       cmp         [es:edi], eax       ; compare a with b
-       jng         l3                  ; if a is NOT greater than b, skip setting flag
-       dec         edx                 ; edx = -1 (true flag, all bits set)
-l3:    mov         [es:edi], edx       ; store flag as new top of stack
-       jmp         emu                 ; return to emulation loop
-
-op_29_xless:                    ; Less Than: ( a b -- flag )
-       mov         eax, [es:edi]       ; eax = b (top of stack)
-       add         edi, 4              ; pop b off the stack; a is now the new top
-       mov         edx, 0              ; edx = 0 (default: false)
-       cmp         [es:edi], eax       ; compare a with b
-       jnl         l4                  ; if a is NOT less than b, skip setting flag
-       dec         edx                 ; edx = -1 (true flag, all bits set)
-l4:    mov         [es:edi], edx       ; store flag as new top of stack
-       jmp         emu                 ; return to emulation loop
diff --git a/emulator/opcodes_30_39.asm b/emulator/opcodes_30_39.asm
new file mode 100644 (file)
index 0000000..2461061
--- /dev/null
@@ -0,0 +1,65 @@
+; Opcodes 30-39: not, i, cprt@, cprt!, i2, i3, shl, shr, or, xor
+
+op_30_xnot:                 ; Bitwise NOT: ( n -- ~n )
+       not     dword [es:edi]      ; invert all bits of top of data stack in place
+       jmp     emu                 ; return to emulation loop
+
+op_31_xi:                   ; Copy Return Stack TOS: ( -- n ) R:( n -- n )
+       mov     ebx, [resp]         ; ebx = return stack pointer
+       mov     eax, [es:ebx]       ; eax = value at top of return stack
+       sub     edi, 4              ; make room on data stack for one element
+       mov     [es:edi], eax       ; push copy of return stack top onto data stack
+       jmp     emu                 ; return to emulation loop
+
+op_32_xcprt@:               ; Read I/O Port: ( port -- byte )
+       mov     dx, [es:edi]        ; dx = port number from top of data stack
+       in      al, dx              ; read one byte from the I/O port
+       sub     ecx, ecx            ; clear ecx (zero-extend the byte)
+       mov     cl, al              ; cl = byte read from port
+       mov     [es:edi], ecx       ; replace port number on stack with the read byte
+       jmp     emu                 ; return to emulation loop
+
+op_33_xcprt!:               ; Write I/O Port: ( byte port -- )
+       mov     dx, [es:edi]        ; dx = port number (top of stack)
+       mov     al, [es:edi+4]      ; al = byte value to write (second on stack)
+       add     edi, 8              ; pop both arguments off the data stack
+       out     dx, al              ; write byte to the I/O port
+       jmp     emu                 ; return to emulation loop
+
+op_34_xi2:                  ; Copy Return Stack 2nd: ( -- n )
+       mov     ebx, [resp]         ; ebx = return stack pointer
+       mov     eax, [es:ebx+4]     ; eax = second element on return stack
+       sub     edi, 4              ; make room on data stack for one element
+       mov     [es:edi], eax       ; push value onto data stack
+       jmp     emu                 ; return to emulation loop
+
+op_35_xi3:                  ; Copy Return Stack 3rd: ( -- n )
+       mov     ebx, [resp]         ; ebx = return stack pointer
+       mov     eax, [es:ebx+8]     ; eax = third element on return stack
+       sub     edi, 4              ; make room on data stack for one element
+       mov     [es:edi], eax       ; push value onto data stack
+       jmp     emu                 ; return to emulation loop
+
+op_36_xshl:                 ; Shift Left: ( n count -- n<<count )
+       mov     cl, [es:edi]        ; cl = shift count (top of stack, only low byte used)
+       add     edi, 4              ; pop count off the stack; n is now the new top
+       shl     dword [es:edi], cl  ; shift n left by cl bits in place
+       jmp     emu                 ; return to emulation loop
+
+op_37_xshr:                 ; Shift Right: ( n count -- n>>count )
+       mov     cl, [es:edi]        ; cl = shift count (top of stack, only low byte used)
+       add     edi, 4              ; pop count off the stack; n is now the new top
+       shr     dword [es:edi], cl  ; shift n right by cl bits in place (unsigned)
+       jmp     emu                 ; return to emulation loop
+
+op_38_lor:                  ; Bitwise OR: ( a b -- a|b )
+       mov     eax, [es:edi]       ; eax = b (top of stack)
+       add     edi, 4              ; pop b off the stack; a is now the new top
+       or      [es:edi], eax       ; [es:edi] = a OR b (bitwise OR in place)
+       jmp     emu                 ; return to emulation loop
+
+op_39_lxor:                 ; Bitwise XOR: ( a b -- a^b )
+       mov     eax, [es:edi]       ; eax = b (top of stack)
+       add     edi, 4              ; pop b off the stack; a is now the new top
+       xor     [es:edi], eax       ; [es:edi] = a XOR b (bitwise XOR in place)
+       jmp     emu                 ; return to emulation loop
diff --git a/emulator/opcodes_30_39.inc b/emulator/opcodes_30_39.inc
deleted file mode 100644 (file)
index 2461061..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-; Opcodes 30-39: not, i, cprt@, cprt!, i2, i3, shl, shr, or, xor
-
-op_30_xnot:                 ; Bitwise NOT: ( n -- ~n )
-       not     dword [es:edi]      ; invert all bits of top of data stack in place
-       jmp     emu                 ; return to emulation loop
-
-op_31_xi:                   ; Copy Return Stack TOS: ( -- n ) R:( n -- n )
-       mov     ebx, [resp]         ; ebx = return stack pointer
-       mov     eax, [es:ebx]       ; eax = value at top of return stack
-       sub     edi, 4              ; make room on data stack for one element
-       mov     [es:edi], eax       ; push copy of return stack top onto data stack
-       jmp     emu                 ; return to emulation loop
-
-op_32_xcprt@:               ; Read I/O Port: ( port -- byte )
-       mov     dx, [es:edi]        ; dx = port number from top of data stack
-       in      al, dx              ; read one byte from the I/O port
-       sub     ecx, ecx            ; clear ecx (zero-extend the byte)
-       mov     cl, al              ; cl = byte read from port
-       mov     [es:edi], ecx       ; replace port number on stack with the read byte
-       jmp     emu                 ; return to emulation loop
-
-op_33_xcprt!:               ; Write I/O Port: ( byte port -- )
-       mov     dx, [es:edi]        ; dx = port number (top of stack)
-       mov     al, [es:edi+4]      ; al = byte value to write (second on stack)
-       add     edi, 8              ; pop both arguments off the data stack
-       out     dx, al              ; write byte to the I/O port
-       jmp     emu                 ; return to emulation loop
-
-op_34_xi2:                  ; Copy Return Stack 2nd: ( -- n )
-       mov     ebx, [resp]         ; ebx = return stack pointer
-       mov     eax, [es:ebx+4]     ; eax = second element on return stack
-       sub     edi, 4              ; make room on data stack for one element
-       mov     [es:edi], eax       ; push value onto data stack
-       jmp     emu                 ; return to emulation loop
-
-op_35_xi3:                  ; Copy Return Stack 3rd: ( -- n )
-       mov     ebx, [resp]         ; ebx = return stack pointer
-       mov     eax, [es:ebx+8]     ; eax = third element on return stack
-       sub     edi, 4              ; make room on data stack for one element
-       mov     [es:edi], eax       ; push value onto data stack
-       jmp     emu                 ; return to emulation loop
-
-op_36_xshl:                 ; Shift Left: ( n count -- n<<count )
-       mov     cl, [es:edi]        ; cl = shift count (top of stack, only low byte used)
-       add     edi, 4              ; pop count off the stack; n is now the new top
-       shl     dword [es:edi], cl  ; shift n left by cl bits in place
-       jmp     emu                 ; return to emulation loop
-
-op_37_xshr:                 ; Shift Right: ( n count -- n>>count )
-       mov     cl, [es:edi]        ; cl = shift count (top of stack, only low byte used)
-       add     edi, 4              ; pop count off the stack; n is now the new top
-       shr     dword [es:edi], cl  ; shift n right by cl bits in place (unsigned)
-       jmp     emu                 ; return to emulation loop
-
-op_38_lor:                  ; Bitwise OR: ( a b -- a|b )
-       mov     eax, [es:edi]       ; eax = b (top of stack)
-       add     edi, 4              ; pop b off the stack; a is now the new top
-       or      [es:edi], eax       ; [es:edi] = a OR b (bitwise OR in place)
-       jmp     emu                 ; return to emulation loop
-
-op_39_lxor:                 ; Bitwise XOR: ( a b -- a^b )
-       mov     eax, [es:edi]       ; eax = b (top of stack)
-       add     edi, 4              ; pop b off the stack; a is now the new top
-       xor     [es:edi], eax       ; [es:edi] = a XOR b (bitwise XOR in place)
-       jmp     emu                 ; return to emulation loop
diff --git a/emulator/opcodes_40_47.inc b/emulator/opcodes_40_47.inc
deleted file mode 100644 (file)
index f21d87c..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-; Opcodes 40-47: vidmap, mouse@, vidput, cmove, cfill, tvidput, dep, charput
-
-op_40_xvidmap:                  ; Video Map: ( addr -- )
-       mov         edx, [es:edi]       ; edx = virtual address of image buffer
-       add         edx, [xms_addr]     ; convert virtual address to physical address
-       add         edi, 4              ; pop address off the data stack
-       push    edi                 ; save data stack pointer
-       push    esi                 ; save instruction pointer
-       push    0a000h              ; push video memory segment
-       pop         es                  ; es = video memory segment (A000h)
-       mov         word [ds:gra], 0    ; reset VESA granule counter to 0
-       push    0                   ; push zero segment
-       pop         ds                  ; ds = 0 (flat memory access)
-       mov         esi, edx            ; esi = source address (image buffer in XMS)
-mapl1:
-    mov            dx, [cs:gra]        ; dx = current VESA granule number
-       xor         bx, bx              ; bx = 0 (window A)
-       mov         ax, 4f05h           ; VESA: set memory window position
-       int         10h                 ; call VESA BIOS
-       mov         edi, 0              ; edi = 0 (start of video window)
-       mov         cx, 4096            ; cx = number of dwords to copy (4096 * 4 = 16384 bytes)
-mapl2:
-    mov            eax, [ds:esi]       ; read 4 bytes from source image buffer
-       add         esi, 4              ; advance source pointer by 4 bytes
-       stosd                       ; write 4 bytes to video memory, advance edi
-       loop    mapl2               ; repeat for all 4096 dwords in this granule
-       inc         word [cs:gra]       ; advance to next VESA granule
-       cmp         word [cs:gra], 19   ; check if all 19 granules are done (19*16384=311296>=307200)
-       jne         mapl1               ; if not done, process next granule
-       push    0                   ; push zero segment
-       pop         es                  ; restore es = 0
-       push    cs                  ; push code segment
-       pop         ds                  ; restore ds = cs
-       pop         esi                 ; restore instruction pointer
-       pop         edi                 ; restore data stack pointer
-       jmp         emu                 ; return to emulation loop
-gra    dw 0                        ; current VESA granule number
-
-
-op_41_xmouse@:                  ; Read Mouse: ( -- buttons dy dx )
-       mov         ax, 0bh             ; mouse function: read motion counters
-       int         33h                 ; call mouse driver; cx=dx_motion, dx=dy_motion
-       push    dx                  ; save dy (vertical motion) on x86 stack
-       sub         eax, eax            ; clear eax
-       mov         ax, cx              ; ax = dx_motion (horizontal motion)
-       cwd                         ; sign-extend ax into dx:ax
-       shl         edx, 16             ; shift sign extension to upper 16 bits
-       add         edx, eax            ; edx = sign-extended 32-bit dx_motion
-       mov         [es:edi-4], edx     ; store dx (horizontal motion) below current stack top
-       pop         ax                  ; restore dy (vertical motion) into ax
-       cwd                         ; sign-extend ax into dx:ax
-       shl         edx, 16             ; shift sign extension to upper 16 bits
-       add         edx, eax            ; edx = sign-extended 32-bit dy_motion
-       mov         [es:edi-8], edx     ; store dy (vertical motion) further below stack top
-       mov         ax, 3               ; mouse function: read button status
-       int         33h                 ; call mouse driver; bx=button state
-       sub         eax, eax            ; clear eax
-       mov         ax, bx              ; ax = button state
-       sub         edi, 12             ; allocate 3 stack slots (buttons, dy, dx)
-       mov         [es:edi], eax       ; push button state as bottom of the three new elements
-       jmp         emu                 ; return to emulation loop
-
-op_43_xcmove:                   ; Copy Memory: ( src dst count -- )
-       mov         ecx, [es:edi]       ; ecx = byte count (top of stack)
-       add         edi, 12             ; pop all three arguments off the data stack
-       mov         edx, [es:edi-8]     ; edx = destination address (virtual)
-       add         edx, [xms_addr]     ; convert destination virtual address to physical
-       mov         ebx, [es:edi-4]     ; ebx = source address (virtual)
-       add         ebx, [xms_addr]     ; convert source virtual address to physical
-       cmp         ecx, 0              ; check if count is zero
-       je          emu                 ; if zero bytes, nothing to do
-       cmp         ebx, edx            ; compare source and destination addresses
-       ja          l8                  ; if source > dest, copy forward (no overlap issue)
-       call    memmove2            ; otherwise copy backward (handles overlapping regions)
-       jmp         emu                 ; return to emulation loop
-l8:    call    memmove             ; copy forward: ebx=from, edx=to, ecx=count
-       jmp         emu                 ; return to emulation loop
-
-op_44_xcfill:                   ; Fill Memory: ( byte addr count -- )
-       mov         ecx, [es:edi]       ; ecx = byte count (top of stack)
-       mov         edx, [es:edi+4]     ; edx = destination address (virtual, second on stack)
-       add         edx, [xms_addr]     ; convert virtual address to physical address
-       mov         eax, [es:edi+8]     ; eax = fill byte value (third on stack, only al used)
-       add         edi, 12             ; pop all three arguments off the data stack
-l9:
-    cmp            ecx, 0              ; check if remaining count is zero
-       je          emu                 ; if zero, we are done
-       mov         [es:edx], al        ; store fill byte at current destination
-       inc         edx                 ; advance destination pointer
-       dec         ecx                 ; decrement remaining count
-       jmp         l9                  ; repeat for next byte
-
-op_46_xdep:                     ; Stack Depth: ( -- depth )
-       sub         eax, eax            ; clear eax
-       mov         ax, cs              ; ax = code segment
-       shl         eax, 4              ; convert segment to linear address
-       add         eax, buf            ; eax = linear address of buffer area
-       add         eax, 20000          ; eax = base of data stack (stack bottom)
-       sub         eax, edi            ; eax = number of bytes used on stack
-       shr         eax, 2              ; convert bytes to 32-bit cell count (divide by 4)
-       sub         edi, 4              ; make room on data stack for one element
-       mov         [es:edi], eax       ; push stack depth onto data stack
-       jmp         emu                 ; return to emulation loop
-
-include 'vidput.inc'
-include 'tvidput.inc'
-include 'charput.inc'
diff --git a/emulator/opcodes_40_49.asm b/emulator/opcodes_40_49.asm
new file mode 100644 (file)
index 0000000..68c871b
--- /dev/null
@@ -0,0 +1,107 @@
+; Opcodes 40-47: vidmap, mouse@, vidput, cmove, cfill, tvidput, dep, charput
+
+op_40_xvidmap:                  ; Video Map: ( addr -- )
+       mov         edx, [es:edi]       ; edx = virtual address of image buffer
+       add         edx, [xms_addr]     ; convert virtual address to physical address
+       add         edi, 4              ; pop address off the data stack
+       push    edi                 ; save data stack pointer
+       push    esi                 ; save instruction pointer
+       push    0a000h              ; push video memory segment
+       pop         es                  ; es = video memory segment (A000h)
+       mov         word [ds:gra], 0    ; reset VESA granule counter to 0
+       push    0                   ; push zero segment
+       pop         ds                  ; ds = 0 (flat memory access)
+       mov         esi, edx            ; esi = source address (image buffer in XMS)
+mapl1:
+    mov            dx, [cs:gra]        ; dx = current VESA granule number
+       xor         bx, bx              ; bx = 0 (window A)
+       mov         ax, 4f05h           ; VESA: set memory window position
+       int         10h                 ; call VESA BIOS
+       mov         edi, 0              ; edi = 0 (start of video window)
+       mov         cx, 4096            ; cx = number of dwords to copy (4096 * 4 = 16384 bytes)
+mapl2:
+    mov            eax, [ds:esi]       ; read 4 bytes from source image buffer
+       add         esi, 4              ; advance source pointer by 4 bytes
+       stosd                       ; write 4 bytes to video memory, advance edi
+       loop    mapl2               ; repeat for all 4096 dwords in this granule
+       inc         word [cs:gra]       ; advance to next VESA granule
+       cmp         word [cs:gra], 19   ; check if all 19 granules are done (19*16384=311296>=307200)
+       jne         mapl1               ; if not done, process next granule
+       push    0                   ; push zero segment
+       pop         es                  ; restore es = 0
+       push    cs                  ; push code segment
+       pop         ds                  ; restore ds = cs
+       pop         esi                 ; restore instruction pointer
+       pop         edi                 ; restore data stack pointer
+       jmp         emu                 ; return to emulation loop
+gra    dw 0                        ; current VESA granule number
+
+
+op_41_xmouse@:                  ; Read Mouse: ( -- buttons dy dx )
+       mov         ax, 0bh             ; mouse function: read motion counters
+       int         33h                 ; call mouse driver; cx=dx_motion, dx=dy_motion
+       push    dx                  ; save dy (vertical motion) on x86 stack
+       sub         eax, eax            ; clear eax
+       mov         ax, cx              ; ax = dx_motion (horizontal motion)
+       cwd                         ; sign-extend ax into dx:ax
+       shl         edx, 16             ; shift sign extension to upper 16 bits
+       add         edx, eax            ; edx = sign-extended 32-bit dx_motion
+       mov         [es:edi-4], edx     ; store dx (horizontal motion) below current stack top
+       pop         ax                  ; restore dy (vertical motion) into ax
+       cwd                         ; sign-extend ax into dx:ax
+       shl         edx, 16             ; shift sign extension to upper 16 bits
+       add         edx, eax            ; edx = sign-extended 32-bit dy_motion
+       mov         [es:edi-8], edx     ; store dy (vertical motion) further below stack top
+       mov         ax, 3               ; mouse function: read button status
+       int         33h                 ; call mouse driver; bx=button state
+       sub         eax, eax            ; clear eax
+       mov         ax, bx              ; ax = button state
+       sub         edi, 12             ; allocate 3 stack slots (buttons, dy, dx)
+       mov         [es:edi], eax       ; push button state as bottom of the three new elements
+       jmp         emu                 ; return to emulation loop
+
+op_43_xcmove:                   ; Copy Memory: ( src dst count -- )
+       mov         ecx, [es:edi]       ; ecx = byte count (top of stack)
+       add         edi, 12             ; pop all three arguments off the data stack
+       mov         edx, [es:edi-8]     ; edx = destination address (virtual)
+       add         edx, [xms_addr]     ; convert destination virtual address to physical
+       mov         ebx, [es:edi-4]     ; ebx = source address (virtual)
+       add         ebx, [xms_addr]     ; convert source virtual address to physical
+       cmp         ecx, 0              ; check if count is zero
+       je          emu                 ; if zero bytes, nothing to do
+       cmp         ebx, edx            ; compare source and destination addresses
+       ja          l8                  ; if source > dest, copy forward (no overlap issue)
+       call    memmove2            ; otherwise copy backward (handles overlapping regions)
+       jmp         emu                 ; return to emulation loop
+l8:    call    memmove             ; copy forward: ebx=from, edx=to, ecx=count
+       jmp         emu                 ; return to emulation loop
+
+op_44_xcfill:                   ; Fill Memory: ( byte addr count -- )
+       mov         ecx, [es:edi]       ; ecx = byte count (top of stack)
+       mov         edx, [es:edi+4]     ; edx = destination address (virtual, second on stack)
+       add         edx, [xms_addr]     ; convert virtual address to physical address
+       mov         eax, [es:edi+8]     ; eax = fill byte value (third on stack, only al used)
+       add         edi, 12             ; pop all three arguments off the data stack
+l9:
+    cmp            ecx, 0              ; check if remaining count is zero
+       je          emu                 ; if zero, we are done
+       mov         [es:edx], al        ; store fill byte at current destination
+       inc         edx                 ; advance destination pointer
+       dec         ecx                 ; decrement remaining count
+       jmp         l9                  ; repeat for next byte
+
+op_46_xdep:                     ; Stack Depth: ( -- depth )
+       sub         eax, eax            ; clear eax
+       mov         ax, cs              ; ax = code segment
+       shl         eax, 4              ; convert segment to linear address
+       add         eax, buf            ; eax = linear address of buffer area
+       add         eax, 20000          ; eax = base of data stack (stack bottom)
+       sub         eax, edi            ; eax = number of bytes used on stack
+       shr         eax, 2              ; convert bytes to 32-bit cell count (divide by 4)
+       sub         edi, 4              ; make room on data stack for one element
+       mov         [es:edi], eax       ; push stack depth onto data stack
+       jmp         emu                 ; return to emulation loop
+
+include 'vidput.asm'
+include 'tvidput.asm'
+include 'charput.asm'
diff --git a/emulator/system.asm b/emulator/system.asm
new file mode 100644 (file)
index 0000000..2028a0e
--- /dev/null
@@ -0,0 +1,162 @@
+; System initialization and management routines
+
+; system_init
+;
+; Initializes the system by:
+; - Checking for 80386 or better CPU
+; - Checking for protected mode
+; - Checking for XMS (Extended Memory Specification) support
+; - Allocating and locking extended memory
+; - Setting up A20 gate
+;
+; Notes:
+; - Uses DOS interrupts for XMS operations.
+; - Allocates the largest possible block of extended memory.
+
+system_init:
+       mov     ax,7202h
+       push    ax
+       popf
+       pushf
+       pop     bx
+       cmp     ax,bx
+       je      processor_ok
+       mov     dx, badcpu
+       mov     ah,9
+       int     21h
+       mov     ah,4Ch
+       int     21h
+       badcpu db 'required 80386 or better',24h
+processor_ok:
+       smsw    ax
+       test    al,1
+       jz      mode_ok
+       mov     dx, badmode
+       mov     ah,9
+       int     21h
+       mov     ah,4Ch
+       int     21h
+       badmode db 'error: CPU in protected mode',24h
+mode_ok:
+       mov     ax,4300h                ; check for XMS
+       int     2Fh
+       cmp     al,80h                  ; XMS present?
+       je      xms_ok
+       mov     dx, badxms
+       mov     ah,9
+       int     21h
+       jmp     system_exit
+       badxms db 'error: HIMEM.SYS not loaded',24h
+xms_ok:
+       mov     ax,350Dh
+       int     21h
+       mov     word [irq_5],bx
+       mov     word [irq_5+2],es
+       push    cs
+       pop     es
+       mov     ax,250Dh
+       mov     dx,int_13
+       int     21h
+
+       push    es
+       mov     ax,4310h                ; get XMS driver address
+       int     2Fh
+       mov     word [xms_call],bx      ; store XMS driver address
+       mov     word [xms_call+2],es
+       pop     es
+       mov     ah,3                    ; enable A20
+       call    far dword [xms_call]
+       mov     ah,8                    ; get free extended memory size
+       xor     bl,bl
+       call    far dword [xms_call]
+       or      bl,bl
+       mov     dx,ax
+       movzx   eax,ax
+       shl     eax,10
+       mov     ah,9                    ; allocate largest memory block
+       call    far dword [xms_call]
+       mov     [xms_handle],dx
+       mov     ah,0Ch                  ; lock extended memory block
+       call    far dword [xms_call]
+       shl     edx,16
+       mov     dx,bx
+       mov     [xms_addr],edx          ; store memory block address
+       ret
+
+
+; system_exit
+;
+; Cleans up system resources before exiting:
+; - Unlocks and frees extended memory
+; - Restores original interrupt handler for INT 13h
+; - Exits to DOS
+
+system_exit:
+       cmp     [xms_handle],0
+       je      sys_exit
+       mov     ah, 0dh                 ; unlock extended memory block
+       mov     dx, [xms_handle]
+       call    far dword [xms_call]
+       mov     ah, 0ah                 ; free extended memory block
+       call    far dword [xms_call]
+sys_exit:
+       mov     ax, 250dh
+       mov     dx, word [irq_5]
+       mov     ds, word [irq_5+2]
+       int     21h
+       pop     ax
+       mov     ah, 4ch
+       int     21h
+
+; int_13
+;
+; Interrupt handler for INT 13h (disk services)
+; Used for switching to protected mode and back.
+
+int_13: push   eax
+       mov     al, 00001011b           ; OCW3 - read IRQ in-service register
+       out     20h, al
+       in      al, 20h
+       test    al, 00100000b           ; is IRQ 5 in service?
+       jz      exception
+       pop     eax
+       jmp     dword 0:0
+       label   irq_5 dword at $-4
+exception:
+       push    ds es fs gs
+       cli                             ; disable interrupts
+       xor     eax, eax                ; calculate linear address of GDT
+       mov     ax, cs
+       shl     eax, 4
+       add     eax, GDT
+       mov     dword [cs:GDTR+2],eax
+       lgdt    pword [cs:GDTR]         ; load GDT register
+       mov     eax, cr0                ; switch to protected mode
+       or      al, 1
+       mov     cr0, eax
+       jmp     pm_start
+    pm_start:
+       mov     ax, 1 shl 3             ; load 32-bit data descriptor
+       mov     ds, ax                  ; to all data segment registers
+       mov     es, ax
+       mov     fs, ax
+       mov     gs, ax
+       mov     eax, cr0                ; switch back to real mode
+       and     al, not 1
+       mov     cr0, eax
+       jmp     pm_end
+    pm_end:
+       sti                             ; enable interrupts
+       pop     gs fs es ds
+       pop     eax
+       iret
+
+GDTR dw 2*8-1                          ; limit of GDT
+     dd ?                              ; linear address of GDT
+
+GDT rw 4                               ; null descriptor
+    dw 0FFFFh, 0, 9200h, 8Fh           ; 32-bit data descriptor
+
+xms_call dd 0                          ; XMS driver pointer
+xms_handle dw 0                        ; handle of XMS memory block
+xms_addr dd 0                          ; address to XMS block
diff --git a/emulator/system.inc b/emulator/system.inc
deleted file mode 100644 (file)
index 2028a0e..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-; System initialization and management routines
-
-; system_init
-;
-; Initializes the system by:
-; - Checking for 80386 or better CPU
-; - Checking for protected mode
-; - Checking for XMS (Extended Memory Specification) support
-; - Allocating and locking extended memory
-; - Setting up A20 gate
-;
-; Notes:
-; - Uses DOS interrupts for XMS operations.
-; - Allocates the largest possible block of extended memory.
-
-system_init:
-       mov     ax,7202h
-       push    ax
-       popf
-       pushf
-       pop     bx
-       cmp     ax,bx
-       je      processor_ok
-       mov     dx, badcpu
-       mov     ah,9
-       int     21h
-       mov     ah,4Ch
-       int     21h
-       badcpu db 'required 80386 or better',24h
-processor_ok:
-       smsw    ax
-       test    al,1
-       jz      mode_ok
-       mov     dx, badmode
-       mov     ah,9
-       int     21h
-       mov     ah,4Ch
-       int     21h
-       badmode db 'error: CPU in protected mode',24h
-mode_ok:
-       mov     ax,4300h                ; check for XMS
-       int     2Fh
-       cmp     al,80h                  ; XMS present?
-       je      xms_ok
-       mov     dx, badxms
-       mov     ah,9
-       int     21h
-       jmp     system_exit
-       badxms db 'error: HIMEM.SYS not loaded',24h
-xms_ok:
-       mov     ax,350Dh
-       int     21h
-       mov     word [irq_5],bx
-       mov     word [irq_5+2],es
-       push    cs
-       pop     es
-       mov     ax,250Dh
-       mov     dx,int_13
-       int     21h
-
-       push    es
-       mov     ax,4310h                ; get XMS driver address
-       int     2Fh
-       mov     word [xms_call],bx      ; store XMS driver address
-       mov     word [xms_call+2],es
-       pop     es
-       mov     ah,3                    ; enable A20
-       call    far dword [xms_call]
-       mov     ah,8                    ; get free extended memory size
-       xor     bl,bl
-       call    far dword [xms_call]
-       or      bl,bl
-       mov     dx,ax
-       movzx   eax,ax
-       shl     eax,10
-       mov     ah,9                    ; allocate largest memory block
-       call    far dword [xms_call]
-       mov     [xms_handle],dx
-       mov     ah,0Ch                  ; lock extended memory block
-       call    far dword [xms_call]
-       shl     edx,16
-       mov     dx,bx
-       mov     [xms_addr],edx          ; store memory block address
-       ret
-
-
-; system_exit
-;
-; Cleans up system resources before exiting:
-; - Unlocks and frees extended memory
-; - Restores original interrupt handler for INT 13h
-; - Exits to DOS
-
-system_exit:
-       cmp     [xms_handle],0
-       je      sys_exit
-       mov     ah, 0dh                 ; unlock extended memory block
-       mov     dx, [xms_handle]
-       call    far dword [xms_call]
-       mov     ah, 0ah                 ; free extended memory block
-       call    far dword [xms_call]
-sys_exit:
-       mov     ax, 250dh
-       mov     dx, word [irq_5]
-       mov     ds, word [irq_5+2]
-       int     21h
-       pop     ax
-       mov     ah, 4ch
-       int     21h
-
-; int_13
-;
-; Interrupt handler for INT 13h (disk services)
-; Used for switching to protected mode and back.
-
-int_13: push   eax
-       mov     al, 00001011b           ; OCW3 - read IRQ in-service register
-       out     20h, al
-       in      al, 20h
-       test    al, 00100000b           ; is IRQ 5 in service?
-       jz      exception
-       pop     eax
-       jmp     dword 0:0
-       label   irq_5 dword at $-4
-exception:
-       push    ds es fs gs
-       cli                             ; disable interrupts
-       xor     eax, eax                ; calculate linear address of GDT
-       mov     ax, cs
-       shl     eax, 4
-       add     eax, GDT
-       mov     dword [cs:GDTR+2],eax
-       lgdt    pword [cs:GDTR]         ; load GDT register
-       mov     eax, cr0                ; switch to protected mode
-       or      al, 1
-       mov     cr0, eax
-       jmp     pm_start
-    pm_start:
-       mov     ax, 1 shl 3             ; load 32-bit data descriptor
-       mov     ds, ax                  ; to all data segment registers
-       mov     es, ax
-       mov     fs, ax
-       mov     gs, ax
-       mov     eax, cr0                ; switch back to real mode
-       and     al, not 1
-       mov     cr0, eax
-       jmp     pm_end
-    pm_end:
-       sti                             ; enable interrupts
-       pop     gs fs es ds
-       pop     eax
-       iret
-
-GDTR dw 2*8-1                          ; limit of GDT
-     dd ?                              ; linear address of GDT
-
-GDT rw 4                               ; null descriptor
-    dw 0FFFFh, 0, 9200h, 8Fh           ; 32-bit data descriptor
-
-xms_call dd 0                          ; XMS driver pointer
-xms_handle dw 0                        ; handle of XMS memory block
-xms_addr dd 0                          ; address to XMS block
diff --git a/emulator/tvidput.asm b/emulator/tvidput.asm
new file mode 100644 (file)
index 0000000..3d100d1
--- /dev/null
@@ -0,0 +1,173 @@
+; Blit image with transparency support
+;
+; Stack Effect: addr1 addr2 x y --
+;
+; Copies a portion of image1 to image2 at the specified (x, y)
+; coordinates with transparency support. Pixels in image1 with value
+; 255 (0xFF) are treated as transparent and are not copied to the
+; destination.
+;
+; Parameters:
+; - addr1: Address of the source image structure (width, height, data)
+; - addr2: Address of the destination image structure
+; - x: X-coordinate in destination where to start copying
+; - y: Y-coordinate in destination where to start copying
+;
+; Image Structure Format:
+; - Offset 0: Image width (32-bit)
+; - Offset 4: Image height (32-bit)
+; - Offset 8: Base address of image data (32-bit pointer)
+;
+; Example:
+;   0x1000  ; Source image structure
+;   0x2000  ; Destination image structure
+;   50      ; x
+;   50      ; y
+;   tvidput
+;
+; Memory layout before execution:
+;   [es:edi] = y
+;   [es:edi+4] = x
+;   [es:edi+8] = addr2
+;   [es:edi+12] = addr1
+;
+; Memory layout after execution:
+;   Destination image updated with source image data (transparent pixels skipped)
+
+op_45_xtvidput:
+mov        ebx, edi               ; ebx = saved data stack pointer for reading arguments
+mov        eax, [es:ebx]          ; eax = y coordinate
+mov        [cory], eax             ; store y coordinate
+add        ebx, 4                 ; advance to next stack slot
+mov        eax, [es:ebx]          ; eax = x coordinate
+mov        [corx], eax             ; store x coordinate
+
+add        ebx, 4                 ; advance to next stack slot
+mov        eax, [es:ebx]          ; eax = addr2 (destination image virtual address)
+add        eax, [xms_addr]        ; convert virtual address to physical address
+mov        ecx, [es:eax]          ; ecx = destination image width
+mov        [img2x], ecx           ; store destination image width
+add        eax, 4                 ; advance to height field
+mov        ecx, [es:eax]          ; ecx = destination image height
+mov        [img2y], ecx           ; store destination image height
+add        eax, 4                 ; advance past header to pixel data
+mov        [img2a], eax            ; store destination image data address
+
+add        ebx, 4                 ; advance to next stack slot
+mov        eax, [es:ebx]          ; eax = addr1 (source image virtual address)
+add        eax, [xms_addr]        ; convert virtual address to physical address
+mov        ecx, [es:eax]          ; ecx = source image width
+mov        [img1x], ecx           ; store source image width
+add        eax, 4                 ; advance to height field
+mov        ecx, [es:eax]          ; ecx = source image height
+mov        [img1y], ecx           ; store source image height
+add        eax, 4                 ; advance past header to pixel data
+mov        [img1a], eax            ; store source image data address
+
+add        ebx, 4                 ; advance past all arguments
+mov        edi, ebx               ; pop all four arguments off the data stack
+
+cmp        dword [cory] , 0        ; calculate Y start: is y >= 0?
+jl         tvidl1                  ; if y < 0, need to clip from top
+mov        dword [starty], 0      ; y >= 0: start copying from row 0 of source
+jmp        tvidl2
+tvidl1:
+mov        eax, [cory]             ; eax = negative y offset
+neg        eax                    ; eax = number of rows to skip in source
+mov        [starty], eax           ; store start row (skip clipped rows)
+tvidl2:
+
+cmp        dword [corx] , 0        ; calculate X start: is x >= 0?
+jl         tvidl3                  ; if x < 0, need to clip from left
+mov        dword [startx], 0      ; x >= 0: start copying from column 0 of source
+jmp        tvidl4
+tvidl3:
+mov        eax, [corx]             ; eax = negative x offset
+neg        eax                    ; eax = number of columns to skip in source
+mov        [startx], eax           ; store start column (skip clipped columns)
+tvidl4:
+
+mov        eax, [cory]             ; calculate Y end: does source extend past destination?
+add        eax, [img1y]           ; eax = y + source height
+cmp        eax, [img2y]           ; compare with destination height
+jg         tvidl5                  ; if exceeds destination, clip
+mov        eax, [img1y]           ; no clipping needed: end = source height
+mov        [endy], eax             ; store Y end
+jmp    tvidl6
+tvidl5:
+mov        eax, [img2y]           ; eax = destination height
+sub        eax, [cory]            ; eax = destination height - y (max rows that fit)
+mov        [endy], eax             ; store clipped Y end
+tvidl6:
+
+mov    eax, [corx]             ; calculate X end: does source extend past destination?
+add    eax, [img1x]           ; eax = x + source width
+cmp        eax, [img2x]           ; compare with destination width
+jg         tvidl7                  ; if exceeds destination, clip
+mov        eax, [img1x]           ; no clipping needed: end = source width
+mov        [endx], eax             ; store X end
+jmp        tvidl8
+tvidl7:
+mov        eax, [img2x]           ; eax = destination width
+sub        eax, [corx]            ; eax = destination width - x (max columns that fit)
+mov        [endx], eax             ; store clipped X end
+tvidl8:
+
+mov        eax, [endy]             ; calculate Y length (number of rows to copy)
+sub        eax, [starty]          ; eax = endy - starty
+cmp        eax, 0                 ; check if any rows to copy
+jle        emu                     ; if zero or negative, nothing to draw
+mov        [lengthy], eax          ; store row count
+
+mov    eax, [endx]             ; calculate X length (number of columns to copy)
+sub        eax, [startx]          ; eax = endx - startx
+cmp        eax, 0                 ; check if any columns to copy
+jle        emu                     ; if zero or negative, nothing to draw
+mov        [lengthx], eax          ; store column count
+
+mov        eax, [starty]           ; calculate source image start address
+mov        ebx, [img1x]           ; ebx = source image width (stride)
+sub        edx, edx               ; clear edx for multiplication
+mul        ebx                    ; eax = starty * source width
+add        eax, [img1a]           ; add source data base address
+add        eax, [startx]          ; add starting column offset
+mov        [img1start], eax        ; store source start address
+
+mov        eax, [cory]             ; calculate destination image start address
+add        eax, [starty]          ; eax = y + starty (destination row)
+mov        ebx, [img2x]           ; ebx = destination image width (stride)
+sub        edx, edx               ; clear edx for multiplication
+mul        ebx                    ; eax = destination row * destination width
+add        eax, [img2a]           ; add destination data base address
+add        eax, [corx]            ; add x coordinate offset
+add        eax, [startx]          ; add starting column offset
+mov        [img2start], eax        ; store destination start address
+
+tvidl9:
+mov        ebx, [img1start]       ; ebx = current source row address
+mov    ecx, [lengthx]         ; ecx = number of pixels to copy per row
+mov        edx, [img2start]       ; edx = current destination row address
+
+tmemmove:                          ; transparent copy: ebx=from, edx=to, ecx=count
+cmp        ecx, 0                 ; check if remaining count is zero
+je         tl11                    ; if zero, done with this row
+mov        al, [es:ebx]           ; al = source pixel
+cmp        al, 255                ; is pixel transparent (0xFF)?
+je         tl12                    ; if transparent, skip writing to destination
+mov        [es:edx], al           ; copy non-transparent pixel to destination
+tl12:
+inc        ebx                    ; advance source pointer
+inc        edx                    ; advance destination pointer
+dec        ecx                    ; decrement remaining pixel count
+jmp        tmemmove                ; repeat for next pixel
+tl11:
+
+mov        eax, [img1x]           ; eax = source image width (stride)
+add        [img1start], eax        ; advance source address to next row
+mov        eax, [img2x]           ; eax = destination image width (stride)
+add        [img2start], eax        ; advance destination address to next row
+dec        dword [lengthy]         ; decrement remaining row count
+cmp        [lengthy], 0            ; check if more rows to copy
+jg         tvidl9                  ; if yes, copy next row
+
+jmp    emu                     ; return to emulation loop
diff --git a/emulator/tvidput.inc b/emulator/tvidput.inc
deleted file mode 100644 (file)
index 3d100d1..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-; Blit image with transparency support
-;
-; Stack Effect: addr1 addr2 x y --
-;
-; Copies a portion of image1 to image2 at the specified (x, y)
-; coordinates with transparency support. Pixels in image1 with value
-; 255 (0xFF) are treated as transparent and are not copied to the
-; destination.
-;
-; Parameters:
-; - addr1: Address of the source image structure (width, height, data)
-; - addr2: Address of the destination image structure
-; - x: X-coordinate in destination where to start copying
-; - y: Y-coordinate in destination where to start copying
-;
-; Image Structure Format:
-; - Offset 0: Image width (32-bit)
-; - Offset 4: Image height (32-bit)
-; - Offset 8: Base address of image data (32-bit pointer)
-;
-; Example:
-;   0x1000  ; Source image structure
-;   0x2000  ; Destination image structure
-;   50      ; x
-;   50      ; y
-;   tvidput
-;
-; Memory layout before execution:
-;   [es:edi] = y
-;   [es:edi+4] = x
-;   [es:edi+8] = addr2
-;   [es:edi+12] = addr1
-;
-; Memory layout after execution:
-;   Destination image updated with source image data (transparent pixels skipped)
-
-op_45_xtvidput:
-mov        ebx, edi               ; ebx = saved data stack pointer for reading arguments
-mov        eax, [es:ebx]          ; eax = y coordinate
-mov        [cory], eax             ; store y coordinate
-add        ebx, 4                 ; advance to next stack slot
-mov        eax, [es:ebx]          ; eax = x coordinate
-mov        [corx], eax             ; store x coordinate
-
-add        ebx, 4                 ; advance to next stack slot
-mov        eax, [es:ebx]          ; eax = addr2 (destination image virtual address)
-add        eax, [xms_addr]        ; convert virtual address to physical address
-mov        ecx, [es:eax]          ; ecx = destination image width
-mov        [img2x], ecx           ; store destination image width
-add        eax, 4                 ; advance to height field
-mov        ecx, [es:eax]          ; ecx = destination image height
-mov        [img2y], ecx           ; store destination image height
-add        eax, 4                 ; advance past header to pixel data
-mov        [img2a], eax            ; store destination image data address
-
-add        ebx, 4                 ; advance to next stack slot
-mov        eax, [es:ebx]          ; eax = addr1 (source image virtual address)
-add        eax, [xms_addr]        ; convert virtual address to physical address
-mov        ecx, [es:eax]          ; ecx = source image width
-mov        [img1x], ecx           ; store source image width
-add        eax, 4                 ; advance to height field
-mov        ecx, [es:eax]          ; ecx = source image height
-mov        [img1y], ecx           ; store source image height
-add        eax, 4                 ; advance past header to pixel data
-mov        [img1a], eax            ; store source image data address
-
-add        ebx, 4                 ; advance past all arguments
-mov        edi, ebx               ; pop all four arguments off the data stack
-
-cmp        dword [cory] , 0        ; calculate Y start: is y >= 0?
-jl         tvidl1                  ; if y < 0, need to clip from top
-mov        dword [starty], 0      ; y >= 0: start copying from row 0 of source
-jmp        tvidl2
-tvidl1:
-mov        eax, [cory]             ; eax = negative y offset
-neg        eax                    ; eax = number of rows to skip in source
-mov        [starty], eax           ; store start row (skip clipped rows)
-tvidl2:
-
-cmp        dword [corx] , 0        ; calculate X start: is x >= 0?
-jl         tvidl3                  ; if x < 0, need to clip from left
-mov        dword [startx], 0      ; x >= 0: start copying from column 0 of source
-jmp        tvidl4
-tvidl3:
-mov        eax, [corx]             ; eax = negative x offset
-neg        eax                    ; eax = number of columns to skip in source
-mov        [startx], eax           ; store start column (skip clipped columns)
-tvidl4:
-
-mov        eax, [cory]             ; calculate Y end: does source extend past destination?
-add        eax, [img1y]           ; eax = y + source height
-cmp        eax, [img2y]           ; compare with destination height
-jg         tvidl5                  ; if exceeds destination, clip
-mov        eax, [img1y]           ; no clipping needed: end = source height
-mov        [endy], eax             ; store Y end
-jmp    tvidl6
-tvidl5:
-mov        eax, [img2y]           ; eax = destination height
-sub        eax, [cory]            ; eax = destination height - y (max rows that fit)
-mov        [endy], eax             ; store clipped Y end
-tvidl6:
-
-mov    eax, [corx]             ; calculate X end: does source extend past destination?
-add    eax, [img1x]           ; eax = x + source width
-cmp        eax, [img2x]           ; compare with destination width
-jg         tvidl7                  ; if exceeds destination, clip
-mov        eax, [img1x]           ; no clipping needed: end = source width
-mov        [endx], eax             ; store X end
-jmp        tvidl8
-tvidl7:
-mov        eax, [img2x]           ; eax = destination width
-sub        eax, [corx]            ; eax = destination width - x (max columns that fit)
-mov        [endx], eax             ; store clipped X end
-tvidl8:
-
-mov        eax, [endy]             ; calculate Y length (number of rows to copy)
-sub        eax, [starty]          ; eax = endy - starty
-cmp        eax, 0                 ; check if any rows to copy
-jle        emu                     ; if zero or negative, nothing to draw
-mov        [lengthy], eax          ; store row count
-
-mov    eax, [endx]             ; calculate X length (number of columns to copy)
-sub        eax, [startx]          ; eax = endx - startx
-cmp        eax, 0                 ; check if any columns to copy
-jle        emu                     ; if zero or negative, nothing to draw
-mov        [lengthx], eax          ; store column count
-
-mov        eax, [starty]           ; calculate source image start address
-mov        ebx, [img1x]           ; ebx = source image width (stride)
-sub        edx, edx               ; clear edx for multiplication
-mul        ebx                    ; eax = starty * source width
-add        eax, [img1a]           ; add source data base address
-add        eax, [startx]          ; add starting column offset
-mov        [img1start], eax        ; store source start address
-
-mov        eax, [cory]             ; calculate destination image start address
-add        eax, [starty]          ; eax = y + starty (destination row)
-mov        ebx, [img2x]           ; ebx = destination image width (stride)
-sub        edx, edx               ; clear edx for multiplication
-mul        ebx                    ; eax = destination row * destination width
-add        eax, [img2a]           ; add destination data base address
-add        eax, [corx]            ; add x coordinate offset
-add        eax, [startx]          ; add starting column offset
-mov        [img2start], eax        ; store destination start address
-
-tvidl9:
-mov        ebx, [img1start]       ; ebx = current source row address
-mov    ecx, [lengthx]         ; ecx = number of pixels to copy per row
-mov        edx, [img2start]       ; edx = current destination row address
-
-tmemmove:                          ; transparent copy: ebx=from, edx=to, ecx=count
-cmp        ecx, 0                 ; check if remaining count is zero
-je         tl11                    ; if zero, done with this row
-mov        al, [es:ebx]           ; al = source pixel
-cmp        al, 255                ; is pixel transparent (0xFF)?
-je         tl12                    ; if transparent, skip writing to destination
-mov        [es:edx], al           ; copy non-transparent pixel to destination
-tl12:
-inc        ebx                    ; advance source pointer
-inc        edx                    ; advance destination pointer
-dec        ecx                    ; decrement remaining pixel count
-jmp        tmemmove                ; repeat for next pixel
-tl11:
-
-mov        eax, [img1x]           ; eax = source image width (stride)
-add        [img1start], eax        ; advance source address to next row
-mov        eax, [img2x]           ; eax = destination image width (stride)
-add        [img2start], eax        ; advance destination address to next row
-dec        dword [lengthy]         ; decrement remaining row count
-cmp        [lengthy], 0            ; check if more rows to copy
-jg         tvidl9                  ; if yes, copy next row
-
-jmp    emu                     ; return to emulation loop
diff --git a/emulator/vidput.asm b/emulator/vidput.asm
new file mode 100644 (file)
index 0000000..c9151bf
--- /dev/null
@@ -0,0 +1,177 @@
+; Blit image without transparency
+;
+; Stack Effect: addr1 addr2 x y --
+;
+; Copies a portion of image1 to image2 at the specified (x, y)
+; coordinates with clipping. Every pixel is copied regardless of value.
+;
+; Parameters:
+; - addr1: Address of the source image structure (width, height, data)
+; - addr2: Address of the destination image structure
+; - x: X-coordinate in destination where to start copying
+; - y: Y-coordinate in destination where to start copying
+;
+; Image Structure Format:
+; - Offset 0: Image width (32-bit)
+; - Offset 4: Image height (32-bit)
+; - Offset 8: Base address of image data (32-bit pointer)
+;
+; Example:
+;   0x1000  ; Source image structure
+;   0x2000  ; Destination image structure
+;   50      ; x
+;   50      ; y
+;   vidput
+;
+; Memory layout before execution:
+;   [es:edi] = y
+;   [es:edi+4] = x
+;   [es:edi+8] = addr2
+;   [es:edi+12] = addr1
+;
+; Memory layout after execution:
+;   Destination image updated with source image data
+
+op_42_xvidput:
+mov        ebx, edi              ; ebx = saved data stack pointer for reading arguments
+mov        eax, [es:ebx]         ; eax = y coordinate
+mov        [cory], eax           ; store y coordinate
+add        ebx, 4                ; advance to next stack slot
+mov        eax, [es:ebx]         ; eax = x coordinate
+mov        [corx], eax           ; store x coordinate
+
+add        ebx, 4                ; advance to next stack slot
+mov        eax, [es:ebx]         ; eax = addr2 (destination image virtual address)
+add        eax, [xms_addr]       ; convert virtual address to physical address
+mov        ecx, [es:eax]         ; ecx = destination image width
+mov        [img2x], ecx          ; store destination image width
+add        eax, 4                ; advance to height field
+mov        ecx, [es:eax]         ; ecx = destination image height
+mov        [img2y], ecx          ; store destination image height
+add        eax, 4                ; advance past header to pixel data
+mov        [img2a], eax          ; store destination image data address
+
+add        ebx, 4                ; advance to next stack slot
+mov        eax, [es:ebx]         ; eax = addr1 (source image virtual address)
+add        eax, [xms_addr]       ; convert virtual address to physical address
+mov        ecx, [es:eax]         ; ecx = source image width
+mov        [img1x], ecx          ; store source image width
+add        eax, 4                ; advance to height field
+mov        ecx, [es:eax]         ; ecx = source image height
+mov        [img1y], ecx          ; store source image height
+add        eax, 4                ; advance past header to pixel data
+mov        [img1a], eax          ; store source image data address
+
+add        ebx, 4                ; advance past all arguments
+mov        edi, ebx              ; pop all four arguments off the data stack
+
+cmp        dword [cory] , 0      ; calculate Y start: is y >= 0?
+jl         vidl1                 ; if y < 0, need to clip from top
+mov        dword [starty], 0     ; y >= 0: start copying from row 0 of source
+jmp        vidl2
+vidl1:
+mov        eax, [cory]           ; eax = negative y offset
+neg        eax                   ; eax = number of rows to skip in source
+mov        [starty], eax         ; store start row (skip clipped rows)
+vidl2:
+
+cmp        dword [corx] , 0      ; calculate X start: is x >= 0?
+jl         vidl3                 ; if x < 0, need to clip from left
+mov        dword [startx], 0     ; x >= 0: start copying from column 0 of source
+jmp        vidl4
+vidl3:
+mov        eax, [corx]           ; eax = negative x offset
+neg        eax                   ; eax = number of columns to skip in source
+mov        [startx], eax         ; store start column (skip clipped columns)
+vidl4:
+
+mov        eax, [cory]           ; calculate Y end: does source extend past destination?
+add        eax, [img1y]          ; eax = y + source height
+cmp        eax, [img2y]          ; compare with destination height
+jg         vidl5                 ; if exceeds destination, clip
+mov        eax, [img1y]          ; no clipping needed: end = source height
+mov        [endy], eax           ; store Y end
+jmp    vidl6
+vidl5:
+mov        eax, [img2y]          ; eax = destination height
+sub        eax, [cory]           ; eax = destination height - y (max rows that fit)
+mov        [endy], eax           ; store clipped Y end
+vidl6:
+
+mov        eax, [corx]           ; calculate X end: does source extend past destination?
+add        eax, [img1x]          ; eax = x + source width
+cmp        eax, [img2x]          ; compare with destination width
+jg         vidl7                 ; if exceeds destination, clip
+mov        eax, [img1x]          ; no clipping needed: end = source width
+mov        [endx], eax           ; store X end
+jmp    vidl8
+vidl7:
+mov        eax, [img2x]          ; eax = destination width
+sub        eax, [corx]           ; eax = destination width - x (max columns that fit)
+mov        [endx], eax           ; store clipped X end
+vidl8:
+
+mov        eax, [endy]           ; calculate Y length (number of rows to copy)
+sub        eax, [starty]         ; eax = endy - starty
+cmp        eax, 0                ; check if any rows to copy
+jle        emu                   ; if zero or negative, nothing to draw
+mov        [lengthy], eax        ; store row count
+
+mov        eax, [endx]           ; calculate X length (number of columns to copy)
+sub        eax, [startx]         ; eax = endx - startx
+cmp        eax, 0                ; check if any columns to copy
+jle        emu                   ; if zero or negative, nothing to draw
+mov        [lengthx], eax        ; store column count
+
+mov        eax, [starty]         ; calculate source image start address
+mov        ebx, [img1x]          ; ebx = source image width (stride)
+sub        edx, edx              ; clear edx for multiplication
+mul        ebx                   ; eax = starty * source width
+add        eax, [img1a]          ; add source data base address
+add        eax, [startx]         ; add starting column offset
+mov        [img1start], eax      ; store source start address
+
+mov        eax, [cory]           ; calculate destination image start address
+add        eax, [starty]         ; eax = y + starty (destination row)
+mov        ebx, [img2x]          ; ebx = destination image width (stride)
+sub        edx, edx              ; clear edx for multiplication
+mul        ebx                   ; eax = destination row * destination width
+add        eax, [img2a]          ; add destination data base address
+add        eax, [corx]           ; add x coordinate offset
+add        eax, [startx]         ; add starting column offset
+mov        [img2start], eax      ; store destination start address
+
+vidl9:
+mov        ebx, [img1start]      ; ebx = current source row address
+mov        ecx, [lengthx]        ; ecx = number of bytes (pixels) to copy per row
+mov        edx, [img2start]      ; edx = current destination row address
+call   memmove               ; copy one row of pixels: ebx=from, edx=to, ecx=count
+
+mov        eax, [img1x]          ; eax = source image width (stride)
+add        [img1start], eax      ; advance source address to next row
+mov        eax, [img2x]          ; eax = destination image width (stride)
+add        [img2start], eax      ; advance destination address to next row
+dec        dword [lengthy]       ; decrement remaining row count
+cmp        [lengthy], 0          ; check if more rows to copy
+jg         vidl9                 ; if yes, copy next row
+
+jmp    emu                       ; return to emulation loop
+
+cory   dd 0
+corx   dd 0
+img2x  dd 0
+img2y  dd 0
+img2a  dd 0
+img1x  dd 0
+img1y  dd 0
+img1a  dd 0
+
+starty dd 0
+startx dd 0
+endy   dd 0
+endx   dd 0
+lengthx dd 0
+lengthy dd 0
+
+img1start dd 0
+img2start dd 0
diff --git a/emulator/vidput.inc b/emulator/vidput.inc
deleted file mode 100644 (file)
index c9151bf..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-; Blit image without transparency
-;
-; Stack Effect: addr1 addr2 x y --
-;
-; Copies a portion of image1 to image2 at the specified (x, y)
-; coordinates with clipping. Every pixel is copied regardless of value.
-;
-; Parameters:
-; - addr1: Address of the source image structure (width, height, data)
-; - addr2: Address of the destination image structure
-; - x: X-coordinate in destination where to start copying
-; - y: Y-coordinate in destination where to start copying
-;
-; Image Structure Format:
-; - Offset 0: Image width (32-bit)
-; - Offset 4: Image height (32-bit)
-; - Offset 8: Base address of image data (32-bit pointer)
-;
-; Example:
-;   0x1000  ; Source image structure
-;   0x2000  ; Destination image structure
-;   50      ; x
-;   50      ; y
-;   vidput
-;
-; Memory layout before execution:
-;   [es:edi] = y
-;   [es:edi+4] = x
-;   [es:edi+8] = addr2
-;   [es:edi+12] = addr1
-;
-; Memory layout after execution:
-;   Destination image updated with source image data
-
-op_42_xvidput:
-mov        ebx, edi              ; ebx = saved data stack pointer for reading arguments
-mov        eax, [es:ebx]         ; eax = y coordinate
-mov        [cory], eax           ; store y coordinate
-add        ebx, 4                ; advance to next stack slot
-mov        eax, [es:ebx]         ; eax = x coordinate
-mov        [corx], eax           ; store x coordinate
-
-add        ebx, 4                ; advance to next stack slot
-mov        eax, [es:ebx]         ; eax = addr2 (destination image virtual address)
-add        eax, [xms_addr]       ; convert virtual address to physical address
-mov        ecx, [es:eax]         ; ecx = destination image width
-mov        [img2x], ecx          ; store destination image width
-add        eax, 4                ; advance to height field
-mov        ecx, [es:eax]         ; ecx = destination image height
-mov        [img2y], ecx          ; store destination image height
-add        eax, 4                ; advance past header to pixel data
-mov        [img2a], eax          ; store destination image data address
-
-add        ebx, 4                ; advance to next stack slot
-mov        eax, [es:ebx]         ; eax = addr1 (source image virtual address)
-add        eax, [xms_addr]       ; convert virtual address to physical address
-mov        ecx, [es:eax]         ; ecx = source image width
-mov        [img1x], ecx          ; store source image width
-add        eax, 4                ; advance to height field
-mov        ecx, [es:eax]         ; ecx = source image height
-mov        [img1y], ecx          ; store source image height
-add        eax, 4                ; advance past header to pixel data
-mov        [img1a], eax          ; store source image data address
-
-add        ebx, 4                ; advance past all arguments
-mov        edi, ebx              ; pop all four arguments off the data stack
-
-cmp        dword [cory] , 0      ; calculate Y start: is y >= 0?
-jl         vidl1                 ; if y < 0, need to clip from top
-mov        dword [starty], 0     ; y >= 0: start copying from row 0 of source
-jmp        vidl2
-vidl1:
-mov        eax, [cory]           ; eax = negative y offset
-neg        eax                   ; eax = number of rows to skip in source
-mov        [starty], eax         ; store start row (skip clipped rows)
-vidl2:
-
-cmp        dword [corx] , 0      ; calculate X start: is x >= 0?
-jl         vidl3                 ; if x < 0, need to clip from left
-mov        dword [startx], 0     ; x >= 0: start copying from column 0 of source
-jmp        vidl4
-vidl3:
-mov        eax, [corx]           ; eax = negative x offset
-neg        eax                   ; eax = number of columns to skip in source
-mov        [startx], eax         ; store start column (skip clipped columns)
-vidl4:
-
-mov        eax, [cory]           ; calculate Y end: does source extend past destination?
-add        eax, [img1y]          ; eax = y + source height
-cmp        eax, [img2y]          ; compare with destination height
-jg         vidl5                 ; if exceeds destination, clip
-mov        eax, [img1y]          ; no clipping needed: end = source height
-mov        [endy], eax           ; store Y end
-jmp    vidl6
-vidl5:
-mov        eax, [img2y]          ; eax = destination height
-sub        eax, [cory]           ; eax = destination height - y (max rows that fit)
-mov        [endy], eax           ; store clipped Y end
-vidl6:
-
-mov        eax, [corx]           ; calculate X end: does source extend past destination?
-add        eax, [img1x]          ; eax = x + source width
-cmp        eax, [img2x]          ; compare with destination width
-jg         vidl7                 ; if exceeds destination, clip
-mov        eax, [img1x]          ; no clipping needed: end = source width
-mov        [endx], eax           ; store X end
-jmp    vidl8
-vidl7:
-mov        eax, [img2x]          ; eax = destination width
-sub        eax, [corx]           ; eax = destination width - x (max columns that fit)
-mov        [endx], eax           ; store clipped X end
-vidl8:
-
-mov        eax, [endy]           ; calculate Y length (number of rows to copy)
-sub        eax, [starty]         ; eax = endy - starty
-cmp        eax, 0                ; check if any rows to copy
-jle        emu                   ; if zero or negative, nothing to draw
-mov        [lengthy], eax        ; store row count
-
-mov        eax, [endx]           ; calculate X length (number of columns to copy)
-sub        eax, [startx]         ; eax = endx - startx
-cmp        eax, 0                ; check if any columns to copy
-jle        emu                   ; if zero or negative, nothing to draw
-mov        [lengthx], eax        ; store column count
-
-mov        eax, [starty]         ; calculate source image start address
-mov        ebx, [img1x]          ; ebx = source image width (stride)
-sub        edx, edx              ; clear edx for multiplication
-mul        ebx                   ; eax = starty * source width
-add        eax, [img1a]          ; add source data base address
-add        eax, [startx]         ; add starting column offset
-mov        [img1start], eax      ; store source start address
-
-mov        eax, [cory]           ; calculate destination image start address
-add        eax, [starty]         ; eax = y + starty (destination row)
-mov        ebx, [img2x]          ; ebx = destination image width (stride)
-sub        edx, edx              ; clear edx for multiplication
-mul        ebx                   ; eax = destination row * destination width
-add        eax, [img2a]          ; add destination data base address
-add        eax, [corx]           ; add x coordinate offset
-add        eax, [startx]         ; add starting column offset
-mov        [img2start], eax      ; store destination start address
-
-vidl9:
-mov        ebx, [img1start]      ; ebx = current source row address
-mov        ecx, [lengthx]        ; ecx = number of bytes (pixels) to copy per row
-mov        edx, [img2start]      ; edx = current destination row address
-call   memmove               ; copy one row of pixels: ebx=from, edx=to, ecx=count
-
-mov        eax, [img1x]          ; eax = source image width (stride)
-add        [img1start], eax      ; advance source address to next row
-mov        eax, [img2x]          ; eax = destination image width (stride)
-add        [img2start], eax      ; advance destination address to next row
-dec        dword [lengthy]       ; decrement remaining row count
-cmp        [lengthy], 0          ; check if more rows to copy
-jg         vidl9                 ; if yes, copy next row
-
-jmp    emu                       ; return to emulation loop
-
-cory   dd 0
-corx   dd 0
-img2x  dd 0
-img2y  dd 0
-img2a  dd 0
-img1x  dd 0
-img1y  dd 0
-img1a  dd 0
-
-starty dd 0
-startx dd 0
-endy   dd 0
-endx   dd 0
-lengthx dd 0
-lengthy dd 0
-
-img1start dd 0
-img2start dd 0
diff --git a/kernel/compile.sh b/kernel/compile.sh
new file mode 100755 (executable)
index 0000000..33caaac
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Compile the Fifth virtual machine emulator using FASM (Flat Assembler).
+# This script checks whether fasm is installed and offers to install it
+# via apt-get if it is missing, then assembles emulator.asm into the
+# DOS COM binary (emulator.com).
+
+if ! command -v fasm &> /dev/null; then
+    echo "fasm (flat assembler) is not installed."
+    read -p "Would you like to install it? (y/n): " choice
+    if [[ "$choice" =~ ^[Yy]$ ]]; then
+        sudo apt-get update && sudo apt-get install -y fasm
+        if [ $? -ne 0 ]; then
+            echo "Failed to install fasm."
+            exit 1
+        fi
+    else
+        echo "fasm is required to continue. Exiting."
+        exit 1
+    fi
+fi
+
+fasm core.asm
index 2cb74c4..a6c6583 100644 (file)
@@ -1,6 +1,6 @@
 ; core of Fifth\r
 \r
-include  'define.inc'\r
+include  'define.asm'\r
 \r
 link = 0\r
 org 0\r
diff --git a/kernel/define.asm b/kernel/define.asm
new file mode 100644 (file)
index 0000000..d8c4aa6
--- /dev/null
@@ -0,0 +1,81 @@
+; Virtual machine CPU opcodes definitions, so that we can use these opcodes to compile
+; initial binary kernel using Flat Assembler.
+;
+; Note: Virtual CPU supports more opcodes, but we don't need them
+; to build initial kernel, therefore they are not defined here.
+
+macro xnop
+{      db 0 }
+macro xhalt
+{      db 1 }
+macro kbd@
+{      db 2 }
+macro xnum arg1
+{      db 3
+       dd arg1 }
+macro head arg1, arg2, arg3, arg4
+{      dd link
+       link = $-4
+       db arg1
+       db arg2
+       len = (24-5)-($-link)
+       times len db 177
+       db arg3
+       dd arg4 }
+macro xjmp arg1
+{      db 4
+       dd arg1 }
+macro xcall arg1
+{      db 5
+       dd arg1 }
+macro xinc
+{      db 6 }
+macro xdec
+{      db 7 }
+macro xdup
+{      db 8 }
+macro xdrop
+{      db 9 }
+macro xif arg1
+{      db 10
+       dd arg1 }
+macro xret
+{      db 11 }
+macro xc@
+{      db 12 }
+macro xc!
+{      db 13 }
+macro xpush
+{      db 14 }
+macro xpop
+{      db 15 }
+macro xrot
+{      db 17 }
+macro xdisk@
+{      db 18 }
+macro xdisk!
+{      db 19 }
+macro x@
+{      db 20 }
+macro x!
+{      db 21 }
+macro xover
+{      db 22 }
+macro xswap
+{      db 23 }
+macro xplus
+{      db 24 }
+macro xminus
+{      db 25 }
+macro xmul
+{      db 26 }
+macro xcmpg
+{      db 28 }
+macro xcmpl
+{      db 29 }
+macro xcprt!
+{       db 33 }
+macro xcmove
+{      db 43 }
+macro xcfill
+{      db 44 }
diff --git a/kernel/define.inc b/kernel/define.inc
deleted file mode 100644 (file)
index e8785af..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-; Virtual CPU machine code definitions\r
-; For assembling with FASM (Flat Assembler).\r
-; Rest is defined in highlevel code.\r
-\r
-macro xnop\r
-{      db 0 }\r
-macro xhalt\r
-{      db 1 }\r
-macro kbd@\r
-{      db 2 }\r
-macro xnum arg1\r
-{      db 3\r
-       dd arg1 }\r
-macro head arg1, arg2, arg3, arg4\r
-{      dd link\r
-       link = $-4\r
-       db arg1\r
-       db arg2\r
-       len = (24-5)-($-link)\r
-       times len db 177\r
-       db arg3\r
-       dd arg4 }\r
-macro xjmp arg1\r
-{      db 4\r
-       dd arg1 }\r
-macro xcall arg1\r
-{      db 5\r
-       dd arg1 }\r
-macro xinc\r
-{      db 6 }\r
-macro xdec\r
-{      db 7 }\r
-macro xdup\r
-{      db 8 }\r
-macro xdrop\r
-{      db 9 }\r
-macro xif arg1\r
-{      db 10\r
-       dd arg1 }\r
-macro xret\r
-{      db 11 }\r
-macro xc@\r
-{      db 12 }\r
-macro xc!\r
-{      db 13 }\r
-macro xpush\r
-{      db 14 }\r
-macro xpop\r
-{      db 15 }\r
-macro xrot\r
-{      db 17 }\r
-macro xdisk@\r
-{      db 18 }\r
-macro xdisk!\r
-{      db 19 }\r
-macro x@\r
-{      db 20 }\r
-macro x!\r
-{      db 21 }\r
-macro xover\r
-{      db 22 }\r
-macro xswap\r
-{      db 23 }\r
-macro xplus\r
-{      db 24 }\r
-macro xminus\r
-{      db 25 }\r
-macro xmul\r
-{      db 26 }\r
-macro xcmpg\r
-{      db 28 }\r
-macro xcmpl\r
-{      db 29 }\r
-macro xcprt!\r
-{       db 33 }\r
-macro xcmove\r
-{      db 43 }\r
-macro xcfill\r
-{      db 44 }\r