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
| =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)
--- /dev/null
+; 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
+++ /dev/null
-; 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
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
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
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+; 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
+++ /dev/null
-; 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
+++ /dev/null
-; 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'
--- /dev/null
+; 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'
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+; 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
+++ /dev/null
-; 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
--- /dev/null
+#!/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
; core of Fifth\r
\r
-include 'define.inc'\r
+include 'define.asm'\r
\r
link = 0\r
org 0\r
--- /dev/null
+; 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 }
+++ /dev/null
-; 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