-; 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.
+; =============================================================================
+; Virtual CPU Opcodes 0-9: nop, halt, kbd@, num, jmp, call, 1+, 1-, dup, drop
+; =============================================================================
+;
+; This file contains the implementation of virtual machine opcodes 0 through 9.
+; Each opcode handler is responsible for:
+; 1. Performing its operation
+; 2. Jumping back to the emulation loop (emu label in emulator.asm)
+;
+; Register conventions used throughout:
+; esi - Virtual CPU instruction pointer (points to next bytecode)
+; edi - Virtual CPU data stack pointer (grows downward, pre-decrement)
+; [resp] - Virtual CPU return stack pointer
+; es - Set to 0 for flat memory access
+; [xms_addr] - Base address of virtual RAM in host memory
+;
+; Data Stack Memory Layout:
+; The data stack grows DOWNWARD from high memory toward low memory.
+; edi points to the TOP element (most recently pushed).
+; To push: decrement edi by 4, then store at [es:edi]
+; To pop: read from [es:edi], then increment edi by 4
+;
+; Note: Opcode 0 (nop) and Opcode 1 (halt) are handled inline in emulator.asm.
+; - nop simply falls through to the next instruction fetch
+; - halt jumps to the quit routine to exit the emulator
-op_02_kbd@: ; Read Keyboard: ( -- scancode )
+; =============================================================================
+; Opcode 2: kbd@ (Keyboard Read)
+; =============================================================================
+; Stack effect: ( -- scancode )
+;
+; Reads a keyboard scancode from the internal ring buffer and pushes it onto
+; the data stack. If no key is available, pushes 0.
+;
+; When to use:
+; - Reading raw keyboard input for games, text editors, or interactive programs
+; - Building custom keyboard drivers or input handlers
+; - Detecting specific key presses or releases (each key generates two
+; scan codes: one for press, one for release)
+;
+; Scancode behavior:
+; - Each key press generates a make code (scancode 1-127 typically)
+; - Each key release generates a break code (make code + 128, i.e., high bit set)
+; - Example: Pressing 'A' might give scancode 1E, releasing gives 9E
+;
+; Returns:
+; 0 if no key is pending
+; Otherwise, the scancode byte (1-255)
+;
+op_02_kbd@:
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 [es:edi], dl ; push scancode onto data stack (as 32-bit value, zero-extended)
- mov ah, 0bh ; DOS: check if key waiting in keyboard buffer
+ ; Drain the DOS keyboard buffer to prevent key bleeding into the emulator.
+ ; Without this, keys pressed during emulation would appear in the DOS
+ ; command prompt after exit.
+ 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)
+ mov ah, 0 ; BIOS: read key from keyboard buffer (consume it)
+ int 16h ; call BIOS interrupt
jmp emu ; return to emulation loop
-op_03_xnum: ; Push Literal: ( -- n )
+; =============================================================================
+; Opcode 3: num (Push Literal Number)
+; =============================================================================
+; Stack effect: ( -- n )
+;
+; Pushes a 32-bit literal value onto the data stack. The value is read from
+; the instruction stream immediately following the opcode.
+;
+; Instruction format: [opcode 03] [32-bit value, little-endian]
+;
+; When to use:
+; - Pushing constant values for calculations
+; - Loading addresses into the stack
+; - Setting up parameters for other operations
+;
+; Example bytecode sequence:
+; 03 ; num opcode
+; 78 56 34 12 ; 32-bit value 0x12345678 (little-endian)
+; ; Result: stack contains 0x12345678
+;
+
+op_03_xnum:
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
+ add esi, 4 ; advance instruction pointer past the literal
jmp emu ; return to emulation loop
-op_04_xjmp: ; Unconditional Jump: ( -- )
+; =============================================================================
+; Opcode 4: jmp (Unconditional Jump)
+; =============================================================================
+; Stack effect: ( -- )
+;
+; Jumps unconditionally to the specified address. The target address is read
+; from the instruction stream immediately following the opcode.
+;
+; Instruction format: [opcode 04] [32-bit target address, little-endian]
+;
+; When to use:
+; - Creating infinite loops
+; - Skipping over code blocks
+; - Implementing control flow structures (loops, conditionals with if)
+;
+; Example bytecode sequence:
+; 04 ; jmp opcode
+; 00 10 00 00 ; target address 0x1000
+; ; Result: instruction pointer set to 0x1000
+;
+; Note: The address is a VIRTUAL address in the virtual CPU's address space.
+; The emulator converts it to a physical address internally.
+;
+op_04_xjmp:
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 )
+; =============================================================================
+; Opcode 5: call (Call Subroutine)
+; =============================================================================
+; Stack effect: ( -- ) Return stack: ( -- ret-addr )
+;
+; Calls a subroutine at the specified address. The return address (address of
+; the instruction after the call) is pushed onto the return stack.
+;
+; Instruction format: [opcode 05] [32-bit target address, little-endian]
+;
+; When to use:
+; - Calling reusable code blocks (functions, procedures)
+; - Implementing Forth words that call other words
+; - Building modular programs with subroutines
+;
+; Example bytecode sequence:
+; 05 ; call opcode
+; 00 20 00 00 ; target address 0x2000
+; ... ; code here is skipped, return address points here
+;
+; After execution:
+; - Return stack has the return address pushed
+; - Instruction pointer is at the target address
+; - Use 'ret' (opcode 11) to return
+;
+; Note: The return stack is separate from the data stack and is used
+; exclusively for return addresses and loop control variables.
+;
+op_05_xcall:
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
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.
-
+; =============================================================================
+; Opcode 6: 1+ (Increment)
+; =============================================================================
+; Stack effect: ( n -- n+1 )
+;
+; Adds 1 to the value on top of the data stack, modifying it in place.
+;
+; When to use:
+; - Incrementing counters and loop indices
+; - Advancing pointers through arrays or buffers
+; - Computing successor values
+;
+; Example:
+; Stack before: [ ... 5 ]
+; After 1+: [ ... 6 ]
+;
+; Note: This is an efficient single-increment operation. For adding larger
+; values, use the '+' (add) opcode instead.
+;
+op_06_xinc:
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 )
+; =============================================================================
+; Opcode 7: 1- (Decrement)
+; =============================================================================
+; Stack effect: ( n -- n-1 )
;
-; Decrements the top of the data stack by 1.
+; Subtracts 1 from the value on top of the data stack, modifying it in place.
;
-; Memory layout before execution:
-; [es:edi] = n
+; When to use:
+; - Decrementing counters and loop indices
+; - Moving pointers backward through arrays or buffers
+; - Computing predecessor values
;
-; Memory layout after execution:
-; [es:edi] = n-1
-
+; Example:
+; Stack before: [ ... 10 ]
+; After 1-: [ ... 9 ]
+;
+; Note: This is an efficient single-decrement operation. For subtracting
+; larger values, use the '-' (subtract) opcode instead.
+;
+op_07_xdec:
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 )
+; =============================================================================
+; Opcode 8: dup (Duplicate)
+; =============================================================================
+; Stack effect: ( n -- n n )
;
-; Duplicates the top element of the data stack.
+; Duplicates the value on top of the data stack, pushing a copy.
;
-; Memory layout before execution:
-; [es:edi] = n
+; When to use:
+; - Preserving a value before modifying it
+; - Using the same value multiple times in a calculation
+; - Displaying or logging values without removing them
;
-; Memory layout after execution:
-; [es:edi] = n (new top)
-; [es:edi+4] = n (original top)
-
+; Example:
+; Stack before: [ ... 42 ]
+; After dup: [ ... 42 42 ]
+;
+; Common patterns:
+; - dup @ : read from an address while keeping the address on stack
+; - dup . : display a value while keeping it for further use
+; - dup 1+ : create a copy and increment the copy
+;
+op_08_xdup:
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 -- )
+; =============================================================================
+; Opcode 9: drop (Discard)
+; =============================================================================
+; Stack effect: ( n -- )
;
-; Removes the top element from the data stack.
+; Removes the value from the top of the data stack, discarding it.
;
-; Memory layout before execution:
-; [es:edi] = n
+; When to use:
+; - Discarding values that are no longer needed
+; - Cleaning up the stack after a calculation
+; - Consuming values from operations whose results aren't needed
;
-; Memory layout after execution:
-; Stack pointer is adjusted; n is no longer on stack
-
- add edi, 4 ; pop top element off the stack
+; Example:
+; Stack before: [ ... 100 42 ]
+; After drop: [ ... 100 ]
+;
+; Common patterns:
+; - addr @ drop : read from memory, discard result (side effect only)
+; - Used after conditionals when the test result is no longer needed
+;
+op_09_xdrop:
+ add edi, 4 ; pop top element off the data stack
jmp emu ; return to emulation loop