From: Svjatoslav Agejenko Date: Sun, 11 Oct 2026 07:27:31 +0000 (+0300) Subject: docs: correct opcode documentation against emulator implementation X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=2ed8c23a0069b47cae9b476f1b3a9358bc4ec395;p=fifth.git docs: correct opcode documentation against emulator implementation Full audit of all 48 opcode entries against emulator/*.asm; every doc claim now matches the silicon. Highlights: - opcodes_20_29.org: fix c@/c! cross-references (opcodes 12/13, not 16/17), fix inverted ">" bytecode example result, drop wrong swap in division pattern, replace broken <=/<> formulas with verified ones (> not; 2dup < rot rot > or), add 2dup to underflowing sort patterns, note idiv quotient-overflow crash. - opcodes_40_49.org: vidmap copies 16 KB per granule (4096 dwords), not 4 KB; mouse@ returns int33h motion deltas, not absolute coordinates; cmove overlap direction was inverted and its example pushed args in wrong order; image structure offset 8 is inline pixels, not a data pointer; charput example had x/y swapped; title range corrected to 40-47; dangling TODO removed. - opcodes_10_19.org: disk@/disk! parameter bullets swapped which argument is TOS; disk file is disk.raw; past-EOF reads return stale data, not zeros. - opcodes_30_39.org: fix i2/i3 example results (2 and 3, not 1), fix false shr-by-31 corner case, remove stray unclosed #+begin_example, correct or/xor TOS labels, note cprt@ port width and zero-extension. - opcodes_00_09.org: kbd@ writes only the low byte of the pushed cell; document host DOS keyboard-buffer drain side effect. - virtual machine.org: mouse@ row shows deltas; precise opcode count (48 slots, 16 unused). - emulator comments: fix the wrong comments the docs had copied (opcode numbers, backwards ">" examples, broken <> formula, kbd@ "zero-extended" claim, misleading IMUL edx note). HTML re-exports clean for all 6 changed pages (incremental exporter). --- diff --git a/Documentation/opcodes_00_09.org b/Documentation/opcodes_00_09.org index 7610959..797fbb0 100644 --- a/Documentation/opcodes_00_09.org +++ b/Documentation/opcodes_00_09.org @@ -144,9 +144,8 @@ onto the data stack. This is a non-blocking operation: if no key is pending, it pushes 0 immediately. Only the low byte of the pushed stack cell is written; the upper three -bytes keep whatever the stack memory previously held. Read the result -with =c@=-style byte semantics (e.g. mask with =FF and=) before -comparing as a full 32-bit value. +bytes keep whatever the stack memory previously held. Compare or mask +only the low byte (=FF=) rather than the full 32-bit cell. ** Scan Code Behavior diff --git a/Documentation/opcodes_10_19.org b/Documentation/opcodes_10_19.org index 336b62b..0a07955 100644 --- a/Documentation/opcodes_10_19.org +++ b/Documentation/opcodes_10_19.org @@ -410,8 +410,8 @@ specified address. *Parameters:* -- *sector* (top of stack): Sector number to read from (0-based) -- *addr* (second): Destination memory address for the data +- *sector* (second on stack): Sector number to read from (0-based) +- *addr* (top of stack): Destination memory address for the data *When to Use:* @@ -435,12 +435,15 @@ Read sector 16 into memory at 0x2000: - One sector = 1024 bytes (1 KB) - Sector numbering starts at 0 - Uses DOS file I/O to access the virtual disk image -- The virtual disk is typically a file named =disk.img= or similar +- The virtual disk is a file named =disk.raw= in the emulator's working + directory ** Important Notes - The address is a virtual address in the VM's address space -- Reading beyond the end of the disk file may return zeros or cause errors +- Reading beyond the end of the disk file is not detected — the DOS + read result is never checked, so the destination receives stale data + left over from the previous sector read - The disk is accessed through DOS file handles, not BIOS interrupts * 19: disk! (Write Disk Sector) @@ -464,8 +467,8 @@ specified sector number. *Parameters:* -- *addr* (top of stack): Source memory address -- *sector* (second): Sector number to write to (0-based) +- *addr* (second on stack): Source memory address +- *sector* (top of stack): Sector number to write to (0-based) *When to Use:* diff --git a/Documentation/opcodes_20_29.org b/Documentation/opcodes_20_29.org index 35adc9b..ce8b280 100644 --- a/Documentation/opcodes_20_29.org +++ b/Documentation/opcodes_20_29.org @@ -101,7 +101,7 @@ In Fifth source code: ** Important Notes -- This is the 32-bit equivalent of =c@= (opcode 16) +- This is the 32-bit equivalent of =c@= (opcode 12) - Virtual address translation provides memory management - Reads full 32-bit words, not bytes - The address is consumed and replaced with the value @@ -179,8 +179,9 @@ In Fifth source code: ** Important Notes -- This is the 32-bit equivalent of =c!= (opcode 17) -- Virtual address translation provides memory protection +- This is the 32-bit equivalent of =c!= (opcode 13) +- Virtual address translation relocates VM addresses into the XMS + memory block (it is not memory protection) - Order is value then addr (Forth standard: value to address) - Both arguments are consumed (stack cleaned up) - No bounds checking is performed @@ -270,12 +271,6 @@ In Fifth source code: ** Common Patterns -*** Compare Without Consuming - -#+BEGIN_SRC fifth -a b over > \ compare a with b, keeping both on stack -#+END_SRC - *** Use Value Twice #+BEGIN_SRC fifth @@ -288,7 +283,6 @@ a b over + \ compute a + b, keeping a on stack addr over @ \ read from addr, keeping addr for later use #+END_SRC ---------------------------------------------------------------------- * 23: swap (Swap) :PROPERTIES: @@ -721,12 +715,13 @@ total parts / \ divide total into equal parts *** Correct Division Order #+BEGIN_SRC fifth -dividend divisor swap / \ compute dividend / divisor +dividend divisor / \ compute dividend / divisor (divisor on top) #+END_SRC ** Edge Cases - Division by zero: CPU exception (crash) +- Quotient overflow (e.g. =80000000 / -1=) also raises a CPU exception - =Anything / 1 = anything= - =0 / anything = 0= - Negative division follows standard signed rules @@ -784,7 +779,7 @@ Compare two numbers: #+BEGIN_SRC 03 05 00 00 00 ; num 5 - stack: [ 5 ] 03 03 00 00 00 ; num 3 - stack: [ 5 3 ] -1C ; > - stack: [ 0 ] (false, 5 is not > 3) +1C ; > - stack: [ -1 ] (true, 5 > 3) #+END_SRC In Fifth source code: @@ -821,7 +816,7 @@ value max > abort" Value too large" *** Sorting Logic #+BEGIN_SRC fifth -a b > if swap then \ ensure smaller value is on top +a b 2dup > if swap then \ ensure smaller value is on top #+END_SRC ** Edge Cases @@ -920,7 +915,7 @@ value min < abort" Value too small" *** Sorting Logic #+BEGIN_SRC fifth -a b < if swap then \ ensure larger value is on top +a b 2dup < if swap then \ ensure larger value is on top #+END_SRC *** Equality Check @@ -942,12 +937,14 @@ then - Equal values return 0 (false) - Negative numbers compared correctly - Works with full 32-bit signed range -- Relationship to other comparisons: =a b <= is equivalent to= =b a > not= +- Relationship to other comparisons: =a b <= is equivalent to= + =a b > not= ** Relationship to Other Comparisons The less than operation is complementary to greater than: -- =a b < is equivalent to= =b a >= -- =a b <= (less than or equal) can be implemented as=: =over over < > or= -- =a b <> (not equal) can be implemented as=: =over over <> = drop= \ No newline at end of file +- =a b < is equivalent to= =b a > +- =a b <= (less than or equal) can be implemented as=: => not= + (=a b > not=) +- =a b <> (not equal) can be implemented as=: =2dup < rot rot > or= diff --git a/Documentation/opcodes_30_39.org b/Documentation/opcodes_30_39.org index 3d87fda..55242f9 100644 --- a/Documentation/opcodes_30_39.org +++ b/Documentation/opcodes_30_39.org @@ -68,7 +68,9 @@ - *Stack Effect:* =port -- byte= - *Description:* Reads a byte from the specified hardware I/O port. - *Notes:* Uses IN instruction to read from the port. Replaces the port - address with the read byte. + address with the read byte. The port number is a 16-bit I/O address + (only the low 16 bits are used); the read byte is zero-extended to a + full 32-bit cell. - *Example:* #+begin_example 0x60 ; Keyboard port @@ -107,7 +109,8 @@ push push i2 #+end_example - After execution, data stack contains 1. + After execution, data stack contains 2 (the return stack top is 1, + its second item is 2). * 35: i3 :PROPERTIES: @@ -125,7 +128,8 @@ push push push i3 #+end_example - After execution, data stack contains 1. + After execution, data stack contains 3 (the return stack top is 1, + its third item is 3). * 36: shl :PROPERTIES: @@ -166,7 +170,6 @@ shl #+end_example Result: FF0 (0x00000FF0 = 4080 decimal). The low 4 bits (F) were shifted up, and the upper bits remain zero. -#+begin_example * 37: shr :PROPERTIES: @@ -192,7 +195,8 @@ shifted up, and the upper bits remain zero. =count mod 32=. For example, shift by 32 results in no change, shift by 33 shifts by 1. - =value = 0=: Result is always 0 regardless of count. -- Shifting by 31 always results in 0 (all bits shifted out). +- Shifting by 31 leaves only the original bit 31 as bit 0 of the result + (e.g. =80000000 >> 31 = 1=); all other bits are shifted out. *Examples:* #+begin_example @@ -216,7 +220,7 @@ Result: 0. The single bit was shifted out and discarded. :END: - *Stack Effect:* =n1 n2 -- n1|n2= - *Description:* Performs a bitwise OR operation on the top two values. -- *Notes:* The top value (n1) is ORed with the second value (n2). +- *Notes:* The top value (n2) is ORed with the second value (n1). - *Example:* #+begin_example 1 2 @@ -231,7 +235,7 @@ Result: 0. The single bit was shifted out and discarded. :END: - *Stack Effect:* =n1 n2 -- n1^n2= - *Description:* Performs a bitwise XOR operation on the top two values. -- *Notes:* The top value (n1) is XORed with the second value (n2). +- *Notes:* The top value (n2) is XORed with the second value (n1). - *Example:* #+begin_example 1 2 diff --git a/Documentation/opcodes_40_49.org b/Documentation/opcodes_40_49.org index 1186994..98a87a7 100644 --- a/Documentation/opcodes_40_49.org +++ b/Documentation/opcodes_40_49.org @@ -1,5 +1,5 @@ #+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme -#+TITLE: Fifth - virtual machine opcodes 40 -- 49 +#+TITLE: Fifth - virtual machine opcodes 40 -- 47 #+AUTHOR: Svjatoslav Agejenko #+LANGUAGE: en @@ -50,14 +50,15 @@ Parameters: graphics mode). - Initialize VESA-related variables (e.g., =gra= for page number). 2. *VESA Page Mapping*: - - For each VESA page (up to 19 pages for 640x480x8 mode): + - For each of the 19 VESA pages (hardcoded for 640x480x8 mode): - Set VESA page using interrupt =0x10= with =AX = 0x4F05=. - The page number is stored in =gra=. 3. *Memory Copying*: - For each page: - - Copy 4096 bytes (4KB) from source address to video memory. + - Copy 16384 bytes (16 KB, 4096 dwords) from source address to + video memory. - Use =STOSD= to write 32-bit words for efficiency. - - The source address is incremented by 4KB after each page. + - The source address is incremented by 16 KB after each page. 4. *Final Cleanup*: - Restore original segments and clean up stack. @@ -72,11 +73,12 @@ vidmap ** Notes - The operation assumes the video mode is set to VESA 640x480x8 (mode - =0x101=). -- The exact number of pages copied depends on the video mode (19 pages - for 640x480x8). -- Requires VESA BIOS support and proper initialization via - =set_vesa_mode=. + =0x101=), which the emulator sets once at startup (there is no + separate =set_vesa_mode= routine). +- The page count is hardcoded to 19. Total transfer is + 19 x 16384 = 311,296 bytes — that is 4,096 bytes PAST the end of a + 307,200-byte 640x480x8 image, so the source buffer must be padded + accordingly. * 41: mouse@ :PROPERTIES: @@ -84,31 +86,37 @@ vidmap :ID: b4bce653-d38f-45ed-82ec-13ca78c9860b :END: Stack Footprint: -: -- x y buttons +: -- dx dy buttons *Description:* -Reads the current mouse position and button states from the mouse -driver. Returns three values on the stack: -- X-coordinate, -- Y-coordinate, +Reads mouse motion and button states from the DOS mouse driver +(=int 33h=). Returns three values on the stack: +- horizontal motion delta, +- vertical motion delta, - button states. - *Returns*: - - *x* (32-bit): Current X-coordinate of the mouse cursor (0-based). - - *y* (32-bit): Current Y-coordinate of the mouse cursor (0-based). + - *dx* (32-bit): Horizontal motion since the last call, in mickeys + (signed; negative = moved left). + - *dy* (32-bit): Vertical motion since the last call, in mickeys + (signed; negative = moved up). - *buttons* (32-bit): Bitmask of button states: - Bit 0: Left button (1 = pressed) - Bit 1: Right button (1 = pressed) - Bit 2: Middle button (1 = pressed) +Note: the values are motion DELTAS (read via mouse function =0Bh=), not +absolute cursor coordinates. Accumulate them to track a cursor +position. + *Example:* #+begin_example mouse@ #+end_example -After execution, stack contains buttons (top), y, x (bottom). +After execution, stack contains buttons (top), dy, dx (bottom). * 42: vidput - put image1 into image2, at location x, y :PROPERTIES: @@ -116,7 +124,7 @@ After execution, stack contains buttons (top), y, x (bottom). :ID: 238e8b03-57b6-424d-bfee-b6bb652cefbc :END: Stack Footprint: -: addr1 addr2 x y --= +: addr1 addr2 x y -- Copies a portion of image1 to image2 at the specified (x, y) coordinates with clipping. Unlike =tvidput=, there is no transparency @@ -125,9 +133,9 @@ support - every pixel is copied regardless of value. *Parameters:* - *addr1* (32-bit): Address of the source image structure (width, - height, data address). + height, inline pixel data). - *addr2* (32-bit): Address of the destination image structure (width, - height, data address). + height, inline pixel data). - *x* (32-bit): X-coordinate in the destination image where the copy starts. - *y* (32-bit): Y-coordinate in the destination image where the copy @@ -138,7 +146,9 @@ support - every pixel is copied regardless of value. Identical to =tvidput=: - *Offset 0*: Image width (32-bit) - *Offset 4*: Image height (32-bit) -- *Offset 8*: Base address of image data (32-bit pointer) +- *Offset 8*: Pixel data, stored INLINE — one byte per pixel, + row-major, starting immediately after the 8-byte header (this is NOT + a pointer) *Example:* #+begin_example @@ -161,13 +171,14 @@ vidput Copies =len= bytes from =addr1= to =addr2=. Handles overlapping memory regions correctly. -- *Notes:* If =addr1 > addr2=, copies backwards to avoid data - corruption; otherwise copies forwards. +- *Notes:* Copy direction is chosen for overlap safety: if =addr1 > + addr2= (source above destination) it copies FORWARDS; otherwise it + copies BACKWARDS. - *Example:* #+begin_example - 100 ; Length - 0x2000 ; Destination address 0x1000 ; Source address + 0x2000 ; Destination address + 100 ; Length cmove #+end_example @@ -205,9 +216,9 @@ destination. *Parameters:* - *addr1* (32-bit): Address of the source image structure (contains - width, height, and data address). -- *addr2* (32-bit): Address of the destination image structure (contains - width, height, and data address). + width, height, and inline pixel data). +- *addr2* (32-bit): Address of the destination image structure + (contains width, height, and inline pixel data). - *x* (32-bit): X-coordinate in the destination image where the copy starts. - *y* (32-bit): Y-coordinate in the destination image where the copy @@ -218,7 +229,9 @@ destination. Each image structure is stored in memory as follows: - *Offset 0*: Image width (32-bit integer) - *Offset 4*: Image height (32-bit integer) -- *Offset 8*: Base address of image data (32-bit pointer) +- *Offset 8*: Pixel data, stored INLINE — one byte per pixel, + row-major, starting immediately after the 8-byte header (this is NOT + a pointer) *Example:* #+begin_example @@ -267,7 +280,8 @@ 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. +color for that pixel. Bit 7 (the MSB) of each byte is the leftmost +pixel of the row; glyphs are fixed at 8x8 pixels. *Parameters:* @@ -276,8 +290,10 @@ color for that pixel. - *colorbg* (1 byte): Background color value (0-255). - *addrsrc* (32-bit): Memory address pointing to 8 bytes of character data. Each byte represents one row of the character (8 bits per row). -- *addrdest* (32-bit): Base address of the destination image buffer in - memory. +- *addrdest* (32-bit): Address of the destination image STRUCTURE — + i.e. width/height header followed by inline pixel data, as used by + =vidput=. NOT a raw framebuffer address: the width field is read as + the row stride. - *x* (32-bit): X-coordinate (0-based) where the character's left edge starts. - *y* (32-bit): Y-coordinate (0-based) where the character's top edge @@ -285,16 +301,13 @@ color for that pixel. *Example:* -TODO: Create dedicated page that explains this instruction in -depth. Link to this new page from current one. - #+begin_example 0xFF ; White foreground 0x00 ; Black background 0x3000 ; Character data address -0x5000 ; Video memory address -20 ; y +0x5000 ; Destination image structure 10 ; x +20 ; y charput #+end_example diff --git a/Documentation/virtual machine.org b/Documentation/virtual machine.org index fddabf3..05eda38 100644 --- a/Documentation/virtual machine.org +++ b/Documentation/virtual machine.org @@ -36,8 +36,9 @@ virtual CPU. The virtual CPU emulator runs as a COM executable (emulator.com) under MS-DOS. The virtual machine is designed to be simple yet powerful, featuring -two stacks (data stack and return stack), approximately 50 -instructions (opcodes), and a theoretical 4GB flat address space. It +two stacks (data stack and return stack), 48 opcode slots (opcodes +0-47; opcode 16 is currently unused), and a theoretical 4GB flat +address space. It serves as the foundation for the Fifth language — a Forth-inspired, interactive programming environment where users can define, extend, and modify system behavior at runtime. @@ -153,7 +154,7 @@ in FIFTH programming language): | 38 | [[file:opcodes_30_39.org::#opcode-or][or]] | n1 n2 -- result | Bitwise OR of top two items on data stack | | | 39 | [[file:opcodes_30_39.org::#opcode-xor][xor]] | n1 n2 -- result | Bitwise XOR of top two items on data stack | | | 40 | [[file:opcodes_40_49.org::#opcode-vidmap][vidmap]] | addr -- | Copy memory to video memory | | -| 41 | [[file:opcodes_40_49.org::#opcode-mouse][mouse@]] | -- x y buttons | Read mouse coordinates and button states | | +| 41 | [[file:opcodes_40_49.org::#opcode-mouse][mouse@]] | -- dx dy buttons | Read mouse motion deltas (mickeys) and button states | | | 42 | [[file:opcodes_40_49.org::#opcode-vidput][vidput]] | addr1 addr2 x y -- | Blit image1 to image2 at (x,y) without transparency | | | 43 | [[file:opcodes_40_49.org::#opcode-cmove][cmove]] | addr1 addr2 len -- | Copy memory block from addr1 to addr2 | | | 44 | [[file:opcodes_40_49.org::#opcode-cfill][cfill]] | byte addr len -- | Fill memory with specified byte value | | diff --git a/emulator/opcodes_00_09.asm b/emulator/opcodes_00_09.asm index bb59fb4..9f53b5e 100644 --- a/emulator/opcodes_00_09.asm +++ b/emulator/opcodes_00_09.asm @@ -43,14 +43,14 @@ ; - 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: +; Returns (in the LOW BYTE of the stack cell only; upper bytes are stale): ; 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 (as 32-bit value, zero-extended) + mov [es:edi], dl ; push scancode byte onto data stack (upper 3 bytes of the cell are NOT cleared) ; Drain the DOS keyboard buffer to prevent key bleeding into the emulator. ; Without this, keys pressed during emulation would appear in the DOS diff --git a/emulator/opcodes_20_29.asm b/emulator/opcodes_20_29.asm index b92a840..91b6d06 100644 --- a/emulator/opcodes_20_29.asm +++ b/emulator/opcodes_20_29.asm @@ -27,7 +27,7 @@ op_20_x@: ; Fetch 32-bit: ( addr -- value ) ; - Replaces address with value on stack ; ; Notes: -; - This is the 32-bit equivalent of c@ (opcode 16) +; - This is the 32-bit equivalent of c@ (opcode 12) ; - Virtual address translation allows memory management ; - Reads full 32-bit words, not bytes mov eax, [es:edi] ; eax = virtual address from top of data stack @@ -52,8 +52,8 @@ op_21_x!: ; Store 32-bit: ( value addr -- ) ; - Removes both arguments from stack ; ; Notes: -; - This is the 32-bit equivalent of c! (opcode 17) -; - Virtual address translation provides memory protection +; - This is the 32-bit equivalent of c! (opcode 13) +; - Virtual address translation relocates VM addresses into the XMS block ; - Order is value then addr (Forth standard: value to address) ; - Both arguments are consumed (stack cleaned up) mov eax, [es:edi] ; eax = virtual address (top of stack) @@ -184,7 +184,8 @@ op_26_xmul: ; Multiply (signed): ( n1 n2 -- n1*n2 ) ; Operation: ; - Reads top item (n2) into eax ; - Pops n2 off stack (n1 becomes new TOS) -; - Clears edx (required for signed multiply) +; - Clears edx (not strictly required: one-operand IMUL overwrites +; edx:eax anyway) ; - Performs signed multiply: edx:eax = eax * n1 ; - Stores low 32 bits (eax) as result ; @@ -277,8 +278,8 @@ op_28_xgreat: ; Greater Than: ( a b -- flag ) ; - 0 = false (zero) ; ; Common usage: -; 5 3 > -> 0 (false, 5 is not greater than 3) -; 3 5 > -> -1 (true, 3 is greater than 5) +; 5 3 > -> -1 (true, 5 is greater than 3) +; 3 5 > -> 0 (false, 3 is not greater than 5) ; ; Edge cases: ; - Equal values return 0 (false) @@ -330,7 +331,7 @@ op_29_xless: ; Less Than: ( a b -- flag ) ; ; Relationship to other comparisons: ; a b < is equivalent to b a > -; a b <> (not equal) can be implemented as: over over <> = drop +; a b <> (not equal) can be implemented as: 2dup < rot rot > or 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)