.aider*
*.html
+
+# Incremental org export cache (Tools/Update web site)
+.org-export-state
# Documentation
-- `doc/index.org` — Project overview, installation, architecture
-- `doc/language.org` — Complete Fifth language reference
-- `doc/virtual machine.org` — Virtual CPU architecture
-- `doc/opcodes_00_09.org` through `doc/opcodes_40_49.org` — Opcode details
+- `Documentation/index.org` — Project overview, installation, architecture
+- `Documentation/language.org` — Complete Fifth language reference
+- `Documentation/virtual machine.org` — Virtual CPU architecture
+- `Documentation/opcodes_00_09.org` through `Documentation/opcodes_40_49.org` — Opcode details
# File Organization
kernel/ # Virtual CPU assembly
imageFile/f/ # Fifth source files (FSCII encoded)
imageFile/f/lib/ # Fifth libraries
-doc/ # Org-mode documentation (edit .org, not .html)
+Documentation/ # Org-mode documentation (edit .org, not .html)
Tools/ # QuickBASIC utilities for file conversion
floppy image/ # QEMU scripts, disk images
```
1. Implement in `emulator/opcodes_XX_XX.asm`
2. Add entry to dispatch table in `emulator/emulator.asm`
3. Add macro in `kernel/define.asm`
-4. Document in `doc/opcodes_XX_XX.org`
+4. Document in `Documentation/opcodes_XX_XX.org`
5. Add high-level word in `imageFile/f/5th_boot` if needed
## Adding a New Fifth Word
--- /dev/null
+.( loading editor ) update\r
+.( done )\r
+.( ETh - for text editor help )\r
+\r
+var handletxt\r
+var handlebuf\r
+Dstralloc const editfile\r
+var shiftx var shifty\r
+var curlocx var curlocy\r
+Dstr \listF\TXT_ET" tmphelpfile\r
+\r
+: disppage ( -- )\r
+handletxt @\r
+dynp shifty @ 50 * +\r
+0 1 at!\r
+1270 type ;\r
+\r
+: fclose ( close opened file )\r
+handletxt @ dup if dynde handletxt off else drop then ;\r
+\r
+: ETnewpage fclose 27100 dynal dup handletxt ! dynp\r
+FF swap 27100 cfill ;\r
+\r
+var tmp1\r
+: fopen ( strhand -- )\r
+fclose\r
+dup editfile Dstr2Dstr fsDloadnew dup push dynp ( addrinp R: handinp )\r
+27100 dynal dup handletxt ! dynp ( addrinp addrout R: handinp )\r
+FF over 27100 cfill\r
+tmp1 off\r
+i dyns do ( addrinp addrout )\r
+over c@ ( addrinp addrout c )\r
+dup FE = if\r
+ drop\r
+ 50 tmp1 @ - + tmp1 off\r
+else\r
+ dup FD = if\r
+ drop tmp1 @ tab ( addrinp addrout numspaces )\r
+ tmp1 @ over + tmp1 ! + 0\r
+ else\r
+ over c! 1+ 0\r
+ tmp1 @ 1+ tmp1 !\r
+ then\r
+ drop\r
+then\r
+swap 1+ swap\r
+loop 2drop\r
+pop dynde ; rh tmp1\r
+\r
+: dispchar ( x y -- )\r
+2dup\r
+swap shiftx @ -\r
+swap shifty @ - 1+\r
+at!\r
+50 * + handletxt @ dynp + c@ emit ;\r
+\r
+: dispcur curlocx @ curlocy @ dispchar ;\r
+\r
+: displine ( line )\r
+0 over shifty @ - 1+ at!\r
+50 * handletxt @ dynp + shiftx @ + 50 type ;\r
+\r
+: dispstatbar\r
+colneg\r
+0 0 at! 50 do space loop\r
+0 0 at! ." col:" curlocx @ .\r
+7 0 at! ." line:" curlocy @ .\r
+11 0 at! ." ascii:" curlocy @ 50 * curlocx @ + handletxt @ dynp + c@ .\r
+1B 0 at! ." file:" editfile Dstr.\r
+49 0 at! ." F1-help"\r
+colnorm ;\r
+\r
+: charins ( char x y )\r
+50 * handletxt @ dynp + ( char x addr )\r
+over push\r
++ dup dup 1+ 4F pop - cmove ( char addr )\r
+c! ;\r
+\r
+: charcut ( x y -- char )\r
+over push\r
+50 * handletxt @ dynp + +\r
+dup c@ swap ( char addr )\r
+dup dup 1+ swap ( char addr addr+1 addr )\r
+4F pop - dup push cmove ( char addr )\r
+pop + FF swap c! ;\r
+\r
+: lineins ( line )\r
+50 * dup push handletxt @ dynp + ( addr )\r
+dup dup 50 + handletxt @ dyns 50 - pop - cmove ( addr )\r
+FF swap 50 cfill ;\r
+\r
+: linecut ( line )\r
+50 * dup push handletxt @ dynp + ( addr )\r
+dup 50 + swap handletxt @ dyns 50 - pop - cmove ;\r
+\r
+: screenaim\r
+0 curlocy @ 1E - 794 bound shifty !\r
+disppage ;\r
+\r
+var tmp1\r
+: length ( line -- length )\r
+50 * 4F + handletxt @ dynp + ( addr )\r
+51 do\r
+ i tmp1 !\r
+ dup c@ FF - if pop drop 0 push then\r
+ 1-\r
+loop\r
+drop\r
+tmp1 @ ; rh tmp1\r
+\r
+var tmp2 var tmp3\r
+: fsavechar ( char ) tmp2 @ swap over c! 1+ tmp2 ! ;\r
+: fsave ( -- )\r
+27200 dynal dup dynp tmp2 !\r
+0\r
+7CF do\r
+ i length if drop pop 1+ 0 push then\r
+loop\r
+0 swap for ( desthand )\r
+ tmp3 off\r
+ i 50 * handletxt @ dynp +\r
+ 0 i length for ( desthand srcaddr )\r
+ dup c@\r
+ i tab 8 = if tmp3 @ if FD fsavechar tmp3 off then then\r
+ dup FF = if\r
+ drop tmp3 @ 1+ tmp3 !\r
+ else\r
+ tmp3 @ do FF fsavechar loop tmp3 off\r
+ fsavechar\r
+ then\r
+ 1+\r
+ loop drop\r
+ FE fsavechar\r
+loop\r
+tmp2 @ over dynp - over dynresize\r
+dup editfile fsDsave\r
+dynde ; rh tmp2 rh tmp3\r
+\r
+str TXT_default" tmpdefault\r
+\r
+: ET\r
+handletxt @ if\r
+disppage\r
+until\r
+ colneg dispcur dispstatbar KBD_FW@ colnorm dispcur\r
+\r
+ dup 415 = if ( DEL )\r
+ curlocx @ curlocy @ charcut drop\r
+ curlocy @ displine\r
+ then\r
+\r
+ dup FC = if ( BACKSPACE )\r
+ curlocx @ if\r
+ curlocx @ 1- curlocy @ charcut drop\r
+ curlocy @ displine\r
+ drop 413\r
+ else\r
+ curlocy @ if\r
+ curlocy @ 1- dup length dup curlocx ! ( y-1 strlen )\r
+ swap 50 * handletxt @ dynp + ( strlen y1addr )\r
+ 2dup + swap 50 + ( strlen dest src )\r
+ swap rot 50 swap - cmove\r
+ curlocy @ linecut\r
+ drop 410\r
+ disppage\r
+ then\r
+ then\r
+ then\r
+\r
+ dup FD = if ( TAB )\r
+ curlocx @ tab\r
+ dup do FF curlocx @ curlocy @ charins loop\r
+ curlocx @ + curlocx !\r
+ curlocy @ displine\r
+ then\r
+\r
+ dup FE = if ( ENTER )\r
+ curlocy @ 7CF < if\r
+ curlocy @ 1+ lineins\r
+ curlocx @ 0\r
+ 50 curlocx @ - do\r
+ over curlocy @ charcut\r
+ over curlocy @ 1+ charins\r
+ 1+\r
+ loop 2drop\r
+ curlocx off\r
+ drop 412\r
+ disppage\r
+ then\r
+ then\r
+\r
+ dup 418 = if curlocy @ 3A - curlocy ! then ( PG-UP )\r
+ dup 419 = if curlocy @ 3A + curlocy ! then ( PG-DOWN )\r
+ dup 416 = if curlocx off then ( HOME )\r
+ dup 417 = if curlocy @ length curlocx ! then ( END )\r
+ dup 401 = if tmphelpfile fsdisp disppage then ( F1 )\r
+ dup 402 = if fsave then ( F2 )\r
+ dup 403 = if fclose drop 1B then ( F3 )\r
+ dup 400 = if done 0 0 at! screen cls drop -1 then ( ESC )\r
+\r
+ 0 over F0 bound? if -1 else 0 then ( NORMAL KEY )\r
+ over FF = if drop -1 then\r
+ if\r
+ curlocx @ curlocy @ charins\r
+ curlocy @ displine\r
+ 411\r
+ then\r
+\r
+ dup 411 = if curlocx @ 1+ curlocx ! then ( RIGHT )\r
+ dup 413 = if curlocx @ 1- curlocx ! then ( LEFT )\r
+ dup 412 = if curlocy @ 1+ curlocy ! then ( DOWN )\r
+ dup 410 = if curlocy @ 1- curlocy ! then ( UP )\r
+ drop\r
+\r
+ 0 curlocx @ 4F bound curlocx !\r
+ 0 curlocy @ 7CF bound curlocy !\r
+\r
+ curlocy @ shifty @ - 3B > if screenaim then\r
+\r
+ curlocy @ shifty @ - 3B - 0 = if\r
+ 0 0 8 screen scrollf\r
+ shifty @ 1+ shifty !\r
+ curlocy @ displine\r
+ then\r
+\r
+ curlocy @ shifty @ - -1 < if screenaim then\r
+\r
+ curlocy @ shifty @ - 1+ 0 = if\r
+ 0 0 -8 screen scrollf\r
+ shifty @ 1- shifty !\r
+ curlocy @ displine\r
+ then\r
+\r
+loop\r
+else\r
+ tmpdefault editfile str2Dstr\r
+ ETnewpage\r
+ ET\r
+then ;\r
+\r
+: ETl\r
+fopen\r
+curlocx off\r
+curlocy off\r
+shiftx off\r
+shifty off\r
+ET ;\r
+\r
+: ETs\r
+editfile Dstr2Dstr\r
+fsave ." saved " ;\r
+\r
+: ETh tmphelpfile fs. ;\r
+\r
+rh handletxt\r
+rh handlebuf\r
+rh editfile\r
+rh shiftx rh shifty\r
+rh curlocx rh curlocy\r
+rh tmphelpfile\r
+rh disppage\r
+rh fclose rh fopen\r
+rh dispchar rh dispcur\r
+rh displine rh dispstatbar\r
+rh charins rh charcut\r
+rh lineins rh linecut\r
+rh screenaim rh length\r
+rh fsavechar rh fsave\r
+rh ETnewpage rh tmpdefault\r
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine, operating system, programming language
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+
+* General
+- This program is free software: released under Creative Commons Zero
+ (CC0) license
+
+- Program author:
+ - Svjatoslav Agejenko
+ - Homepage: https://svjatoslav.eu
+ - Email: mailto://svjatoslav@svjatoslav.eu
+
+- [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]]
+
+** Source code
+- [[https://www2.svjatoslav.eu/gitweb/?p=fifth.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]]
+
+- [[https://www2.svjatoslav.eu/gitweb/?p=fifth.git;a=summary][Browse Git repository online]]
+
+- Clone Git repository using command:
+ : git clone https://www3.svjatoslav.eu/git/fifth.git
+
+* Introduction
+
+Fifth is self-hosting hobby computing environment that integrates a
+[[id:9b251eb9-aff6-4025-94bf-25e89e26d54a][custom virtual machine]], a [[id:da7fff9b-0b67-4843-828a-52a404d7f401][Forth-inspired programming language]], and
+operating system.
+
+At its core lies a stack-based virtual CPU with two stacks (data and
+return), approximately 50 instructions, and a 4GB flat address
+space. This virtual machine executes the Fifth language — a concise,
+interactive, and extensible environment where every operation is a
+command that can be defined, redefined, or extended by the user. The
+language uses a custom character encoding (FSCII) with unique
+representations for control characters (e.g., space as FF instead of
+20 in ASCII) and features an innovative hexadecimal number system
+visualized as squares split into four triangles, each representing a
+single bit—similar to Bibi-binary.
+
+It includes a disk-based filesystem, a built-in text editor, 2D
+graphics capabilities, mouse and keyboard drivers, and tools for file
+management and memory allocation.
+
+Released under the CC0 public domain license, Fifth is free for anyone
+to use, study, and modify. Whether you're a retro computing
+enthusiast, a systems programming student, or a curious developer.
+
+** Screenshots
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:screenshots/start.png]]
+
+Startup screen diplaying Fifth logo and full file list.
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:screenshots/dictionary.png]]
+
+Sample words defined. Most of the words are commands that can be
+executed interactively from the command line or from a file. When
+executed, they can be selectively compiled or interpreted.
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:screenshots/text editor.png]]
+
+Built in text editor.
+
+* Installation
+Just unpack all files, witout altering original directory structure,
+somewhere in your hard disk. For example:
+
+: C:\MISC\FIFTH\
+
+To run fifth you need minimally just 2 files:
+- EMULATOR.COM :: Virtual CPU emulator
+- DISK.RAW :: Virtual disk file
+
+For more information, please refer to [[id:0759f3e0-28bb-4901-9e4f-09ef41732173][Fifth distribution directory
+tree description]].
+
+* Fifth distribution directory tree description
+:PROPERTIES:
+:CUSTOM_ID: ID-0759f3e0-28bb-4901-9e4f-09ef41732173
+:ID: 0759f3e0-28bb-4901-9e4f-09ef41732173
+:END:
+After downloading and unpacking the ZIP file you shoud get directory
+tree similar to this:
+
+#+BEGIN_VERSE
+*DOC* - Fifth documentation
+ *commands* - documentation on Fifth built-in commands
+ *modules* - documentation on additional commands, realized as loadable modules
+ *shots* - Fifth screenshots
+
+*imageFile* - files contained within 'disk.raw', just an extracted form.
+
+*source* - source files
+ *emulator* - emulator source
+ *util* - utilites
+
+*disk.raw* - Virtual disk file, has filesystem inside.
+*emulator.com* - main executable.
+#+END_VERSE
+
+* Requirements
+** Software
+- MS-DOS 6.22 with HIMEM.SYS loaded.
+- Mouse driver (optional, if you have a mouse).
+- CPU is initialized into [[https://en.wikipedia.org/wiki/Unreal_mode][Unreal Mode]] during operation.
+- To recompile ASM sources, you can use FASM (Flat Assembler).
+- To run Quick Basic utilities, use Microsoft Quick Basic 4.5.
+- VESA support through BIOS or external driver (UNIVBE).
+
+** Hardware
+- A minimum of a i386 CPU.
+- 64 KB of free RAM below 640KB.
+- 2 MB of free [[https://en.wikipedia.org/wiki/Extended_memory][extended memory]].
+- A VESA-compatible video card.
+
+* Numbers representation within Fifth
+
+Because we are in full experimentation mode here (no regard for
+compatibility whatsoever), why not to try also alternative number
+representation ?
+
+Here alternative hexadecimal number representation format is devised:
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:numbers.png][file:numbers.png]]
+
+Essentially square is split into 4 triangles. Each triangle represents
+one bit.
+
+Fifth uses this hexadecimal format as primary throughout entire
+system.
+
+See also: [[https://en.wikipedia.org/wiki/Bibi-binary][Bibi-binary]].
+
+* Disk file map and its data structures
+
+The `disk.raw` file is a raw disk image that contains both the boot
+code (loaded before filesystem initialization) and the filesystem
+structure. The disk layout is as follows:
+
+** Disk Allocation Table
+| Offset | Length | Description |
+|---------+---------+----------------------------------------------------------------------|
+| 0 | ~4 KiB | Operating system kernel in virtual CPU byte-code |
+| 4 KiB | ~32 KiB | Human readable, early system initialization code |
+| 37 KiB | ~65 KiB | FAT (File Allocation Table) |
+| 101 KiB | ~16 MiB | Filesystem data area (stores file contents and directory structures) |
+
+** FAT Entry Format
+
+The FAT tracks which disk sectors belong to each file.
+
+| Code | Meaning |
+|----------+----------------------------------------------|
+| -2 | Last sector of a file chain |
+| -1 | Unused (empty) sector |
+| 0 -- ... | Pointer to the next sector in the file chain |
+
+** File Directory Entry Format
+| Offset | Length | Description |
+|--------+--------+-----------------------------------------------------|
+| 0 | 4 | File extension (4-byte FSCII string, e.g., "TXT ") |
+| 4 | 16 | Filename (16-byte FSCII string, padded with spaces) |
+| 20 | 4 | First block address |
+| 24 | 4 | File size in bytes |
+| 28 | 4 | Last modification timestamp (seconds since epoch) |
+
+The directory entries are stored within the filesystem data
+area. Directory is just a file that list other files.
+
+* Core architecture
+
+Fifth core is simply some amount of already compiled into machine code
+and linked together modules (entries in other words). In compilation
+process modules is compiled one by one and simply stored on top of
+already existing and growing core. Separately from core is kept
+dictionary, this is special list that contain names of compiled
+modules, variables etc. and they locations in core. Constants use
+dictionary space only. Random word can be removed from dictionary at
+any time. Currently dictionary can contain at most 1000 entries.
+
+** Dictionary entry format
+| offset | length | description |
+|--------+--------+-----------------------|
+| 0 | 4 | 0 < previous entry |
+| | | 0 = last |
+| | | -1 = empty |
+|--------+--------+-----------------------|
+| 4 | 15 | module name string |
+|--------+--------+-----------------------|
+| 19 | 1 | entry type |
+|--------+--------+-----------------------|
+| 20 | 4 | entry data |
+
+Core headers as linked list of module names make up something like
+dictionary. When some entry address is needed compiler can quickly
+run through headers backwards and find needed entry.
+
+** Possible module types
+
+| type | description | "execute" action |
+|------+----------------+----------------------------|
+| 0 | data | compile "num" instruction |
+| | | with address to module |
+|------+----------------+----------------------------|
+| 1 | submodule | compile "call" instruction |
+| | | with address to module |
+|------+----------------+----------------------------|
+| 2 | imm. submodule | immediately call to module |
+
+** Memory map
+
+| location | size | description |
+|----------+--------+-----------------------------|
+| 0 | ~4096 | core |
+| 1500000 | ~32000 | highlevel Fifth boot code |
+| 200000h | | core startup messages area |
+| 5200000 | | end of dynamic memory space |
+
+* Dynamically loadable modules
+** Keyboard driver
+#+BEGIN_VERSE
+
+KBD_@ ( -- code ) get scancodes for pressed keys from keyboard.
+KBD_down? ( key -- result ) check is key with specified scancode
+ currently pressed down.
+KBD_SC2FSCII ( code -- FSCII ) convert key scancode into FSCII code,
+ or in FSK (Fifth standard keycode).
+KBD_F@ ( -- FSCII ) read pressed key FSCII or FSK, returns -1 if no
+ keys are pressed.
+KBD_FW@ ( -- FSCII ) read pressed key FSCII or FSK, if no keys is
+ are pressed then waits until there is.
+
+ FSK
+ ---
+In HEX.
+
+FC backspace
+FD TAB
+FE enter
+FF space
+
+400 ESC
+401 ... F1 ...
+410 up
+411 right
+412 down
+413 left
+414 INS
+415 DEL
+416 home
+417 end
+418 PG/UP
+419 PG/DN
+#+END_VERSE
+** Mouse driver
+#+BEGIN_VERSE
+mousex var Mouse x coordinate.
+mousey var Mouse y coordinate.
+mousekeyl var Mouse left key.
+mousekeym var Mouse middle key.
+mousekeyr var Mouse right key.
+mousec var Display current mouse coordinates in top left part of screen,
+ if true. (good for debugging)
+mousepointer var Image buffer, holding current mouse pointer.
+mouseadd ( ModuleAddr x1 x2 y1 y2 -- ) Add specified area on screen,
+ into mause click buffer. If any mouse button is clicked on
+ that area, module at "ModuleAddr" will be executed.
+mousebe var Amount of buffer elements.
+mousedo ( -- ) Updates mouse coordinates and keys. Parse mouse
+ click buffer, and draw mouse cursor to "screen".
+#+END_VERSE
+** 2D graphic library
+
++ lineh ( color len x y imgbuf -- ) :: draws horisontal line from X,Y
+ coordinates to right, with specified length.
+
++ linev ( color len x y imgbuf -- ) :: draws vertical line down, from
+ coordinates X,Y, with specified length.
+
++ box ( color x2 x1 y2 y1 imgbuf -- ) :: draws rectangular box. x2
+ bust be >= x1, y2 must be >= y1.
+
+ #+begin_example
+ x1,y1-----------+
+ | |
+ | |
+ +-----------x2,y2
+ #+end_example
+
++ flipv ( imgbuf -- ) :: flip image vertically.
+
++ imgcoltrans ( ImgBuf Color ToColor -- ) :: Translate all pixels in
+ specified image with "Color" into "ToColor".
+
++ imgfill ( color x y imgbuf -- ) :: Fill image region starting at
+ location X & Y with specified color.
+
+** Trigonometry functions
+
+
+*** sin ( a -- result )
+:PROPERTIES:
+:CUSTOM_ID: ID-9a66ca9c-eb5f-45aa-8116-71763081f2fb
+:ID: 9a66ca9c-eb5f-45aa-8116-71763081f2fb
+:END:
+
+Return sine of angle "a", where the:
+
+- Input is scaled such that: 360 degrees = 2000 units (i.e., 1 unit =
+ 0.18 degrees).
+
+- The output is scaled to a 16-bit integer range of -10000 to 10000,
+ where ±10000 represents ±1.0 in floating-point terms.
+
+This fixed-point representation allows efficient computation using
+precomputed lookup tables, avoiding floating-point operations which
+are not supported by the virtual CPU's integer-only instruction
+set. The lookup table provides fast, sine values within the system's
+precision.
+
+*** cos ( a -- result )
+
+Return cosine of angle "a", using the same scaling and fixed-point
+representation as the [[id:9a66ca9c-eb5f-45aa-8116-71763081f2fb][sin]] function. Input angles are scaled (360° =
+2000 units), and outputs range from -10000 to 10000. Precomputed
+values in a lookup table ensure high performance for trigonometric
+calculations.
--- /dev/null
+:PROPERTIES:
+:ID: da7fff9b-0b67-4843-828a-52a404d7f401
+:END:
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - language
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+
+- [[file:5TH_ET.txt][Example Fifth source file - text editor]]
+
+* Fifth source format
+
+Fifth uses a different character table and codes than ASCII (still
+almost similar). I call it FSCII (Fifth Standard Code for Information
+Interchange) for example space character is not 32 but 255 instead. I
+plan to use mainly HEX numbers, and create new characters to represent
+numeric values. So typical nemric characters "0123..." is treated
+like ordinary letters.
+
+** FSCII
+
+| DEC | HEX | function |
+|--------+-------+----------------------------------------|
+| 0 - 15 | 0 - F | HEX numbers |
+| 252 | FC | backspace |
+| 253 | FD | tabulator (TAB) |
+| 254 | FE | carriage return (CR) |
+| 255 | FF | space |
+| else | | ordinary characters, same as in ASCII. |
+
+* Fifth commands
+** Compilation & miscellaneous
+*** init ( -- )
+
+- Description :: First module, control is passed to on startup. Contains
+ initialization routines. Also it is the last core module. All new
+ modules on top of it comes as result of executing external source
+ files.
+
+*** head <name> ( -- )
+
+- Description :: Compiles new dictionary entry without specifying new
+ module type.
+- Example ::
+ : head myentry
+
+*** : <name> ( -- )
+
+- Description :: Creates new code module.
+
+*** ; ( -- )
+
+- Description :: Ends module (immideate).
+- Example ::
+ : : hello ." hi there" ;
+
+*** const <name> ( n -- )
+
+- Description :: Defines new constant.
+- Example ::
+ : 2147483647 const max
+
+*** :i <name> ( -- )
+
+- Description :: Same as =:= but this module will be executed
+ immideately even in compile mode.
+- Example ::
+ : :i ( 41 scan ;
+
+*** create <name> ( -- )
+
+- Description :: Same as =head=, but specify module type as data.
+- Example ::
+ : create LotoResults 5 , 13 , 52 , 12 , 11 , 3 ,
+
+*** allot ( n -- )
+
+- Description :: Allocate *n* bytes in dictionary.
+- Example ::
+ : create MyArray 100 allot
+
+*** " <string>" ( -- )
+
+- Description :: Compile string and its size into core.
+- Example ::
+ : create Mystring " This is it's contects"
+
+*** str <name> <string>" ( -- )
+
+- Description :: Just shorter way for defining strings.
+- Example ::
+ : str Mystring This is it's contenc"
+
+*** var <name> ( -- )
+
+- Description :: Define new 32 bit variable.
+- Example ::
+ : var result
+
+*** ' <module> ( -- n )
+
+- Description :: Return memory address of given entry.
+- Example ::
+ : ' init
+
+*** forget <name> ( -- )
+
+- Description :: Erases from RAM given entry and all entries what was
+ defined after it.
+- Example ::
+ : forget myprog
+
+*** [ ( -- )
+
+- Description :: Set interpret mode (immideate).
+
+*** ] ( n -- )
+
+- Description :: Set compile mode and compile top stack element in as
+ literal. Together =[ .... ]= combination provides good way to
+ compute some values only once, at compile time, rather than every
+ time while program is running.
+- Example ::
+ : : calculate - [ 4 MyConst1 + MyConst2 * ] ;
+
+*** defer <name> ( -- )
+
+- Description :: Creates new module, with jump instruction. Later
+ address where to jump can be modified by =is= command. This
+ provides method of forward referencing. So you can use modules what
+ not yet exist.
+
+*** is ( address1 address2 -- )
+
+- Description :: *address1* - where to jump, *address2* - address of
+ module created by =defer= command.
+- Example ::
+ : defer dispver
+ : : run dispver ." running ..." ;
+ : ... whatever ...
+ : : (dispver ." Version 9.99 " ;
+ : ' (dispver ' dispver is
+
+ Now if I type "run" on the screen appears:
+ : Version 9.99 running ...
+
+*** asc <char> ( -- )
+
+- Description :: Reads char ascii code and treats it as literal
+ (immideate).
+- Example ::
+ : : BreakLine 30 do asc - emit loop ;
+
+ same as:
+ : : BreakLine 30 do 45 emit loop ;
+
+*** dyninc ( handle -- )
+
+- Description :: Execute code in dynamic memory handle. Automatically
+ deallocates it when done.
+
+*** include ( filenumber -- )
+
+- Description :: Execute code in specified file.
+
+*** words ( -- )
+
+- Description :: Display existing blocks in core.
+
+*** bye ( -- )
+
+- Description :: Exit from Fifth.
+
+*** fkey ( -- c )
+
+- Description :: Read one byte from input stream.
+
+*** sadd ( c addr -- )
+
+- Description :: Add one byte *c* to string located at *addr* and
+ updates string length.
+
+*** scan ( c -- )
+
+- Description :: Read input stream and store it to =pad= until it
+ finds *c*. It ignores all *c* bytes until it finds any non *c*
+ byte. Is useful for breaking text lines into words.
+- Example ::
+ : c is: "
+ : input stream: """"This is test !"aoeu idh
+ : result: This is test !
+
+*** skey ( -- c )
+
+- Description :: So called safe =fkey=. Reads data from input stream
+ but converts characters with ASCII codes: 9 13 10 to spaces.
+
+*** str=str? ( adr1 adr2 -- result )
+
+- Description :: Compares string at *adr1* with string at *adr2*,
+ returns true flag if they are equal or false if they are not.
+ true = -1, false = 0.
+
+*** find ( -- addr )
+
+- Description :: Searches whole dictionary for word in =pad=. If
+ found, returns its address, if not, returns 0.
+
+*** execute ( -- )
+
+- Description :: Execute word located in =pad=. Depending on =mode=.
+
+*** dta ( addr -- DataAddr )
+
+- Description :: Calculates address of dictionary entry data area,
+ from entry point.
+
+*** 2num ( -- num result )
+
+- Description :: Attempt to convert string located in =pad= into
+ numeric value. If succeed returns number and true as result. If
+ not, returns whatever and false as result.
+
+*** dadd ( addr length -- )
+
+- Description :: Add to dictionary data located at *addr*, with
+ specified length.
+
+*** lit ( n -- )
+
+- Description :: Act with number depending on =mode=. When
+ interpreting, leaves it in stack.
+
+*** incmod ( addr -- )
+
+- Description :: Add to dictionary data located at *addr*+1, length
+ is taken from *addr*.
+
+*** here ( -- n )
+
+- Description :: Return =h= contents.
+
+*** mode ( var, 8 bit )
+
+- Description :: Holds input stream parser operation mode. 0 =
+ interpreting, 1 = compiling.
+
+*** pad ( var, 128 bytes )
+
+- Description :: Holds temporary strings.
+
+*** h ( var, 32 bit )
+
+- Description :: Pointer to free byte in memory, always at the end of
+ the dictionary. Each time when something is stored by =c,= command,
+ pointer is increased.
+
+*** lp ( var, 32 bit )
+
+- Description :: Pointer to last dictionary word. Each time when new
+ word is compiled or erased by =forget=, this pointer is updated.
+
+*** modulechk ( Dstr<filename> -- )
+
+- Description :: Check if module is loaded, if not immideately load it.
+
+*** ne ( entrydata entrytype -- )
+
+- Description :: Compile new dictionary entry. Its name must be in
+ =pad=.
+
+** Conditionals & control flow
+*** if ( flag -- )
+
+- Description :: =if 1.. else 2.. then= or =if 1.. then=
+ construction. Conditional execution (immideate). Performs "1.." if
+ *flag* was true, elseway performs "2.." if exist. Execution
+ continues after word =then=.
+- Example ::
+ : 1 if ." nonzero" else ." zero" then
+
+*** >= ( n1 n2 -- result )
+
+- Description :: True if (n1 = n2) or (n1 > n2).
+- Example ::
+ : 5 3 >= if ." first number is greater or equal" then
+
+*** <= ( n1 n2 -- result )
+
+- Description :: True if (n1 = n2) or (n1 < n2).
+
+*** = ( n1 n2 -- result )
+
+- Description :: True if n1 = n2.
+
+*** do ( count -- )
+
+- Description :: =do .. loop= construction (immideate). Performs ".."
+ *count* times. In every step *count* is decreased until it is 0.
+- Example ::
+ : : test 5 do i .d loop ;
+ result: 4 3 2 1 0
+
+*** doexit ( -- )
+
+- Description :: Exit from =do .. loop=.
+
+*** for ( count top -- )
+
+- Description :: =for .. loop= construction (immideate). Performs ".."
+ (top - count) times. In every step *count* is increased until it
+ reaches *top*.
+- Example ::
+ : : test 4 10 for i .d loop ;
+ result: 4 5 6 7 8 9
+
+*** forexit ( -- )
+
+- Description :: Exit from =for .. loop=.
+
+*** until ( -- )
+
+- Description :: =until .. loop= construction (immideate). Performs
+ ".." until flag become true. False by default. Top of return stack
+ holds flag.
+
+*** done ( -- )
+
+- Description :: Exit from =until .. loop=.
+
+** Disk & file access
+*** diskload ( FromDisk ToMem amount -- )
+
+- Description :: Load specified amount of bytes from disk into memory.
+
+*** disksave ( FromMem ToDisk amount -- )
+
+- Description :: Save specified amount of bytes from memory into disk.
+
+*** format ( -- )
+
+- Description :: Erase all files.
+
+*** fsDfilesize@ ( handle -- size )
+
+- Description :: Return size of opened file.
+
+*** fsDcurloc@ ( handle -- location )
+
+- Description :: Return current location in file.
+
+*** fsDupdated@ ( handle -- updated? )
+
+- Description :: Return true if file was updated, ie. write operations
+ occurred.
+
+*** fssave ( FromMem DestFileHandle amount -- )
+
+- Description :: Save data to file.
+
+*** fsload ( SrcFileHandle ToMem amount -- )
+
+- Description :: Load data from file.
+
+*** fseof ( handle -- bytesLeft )
+
+- Description :: Return amount of bytes left till end of file. Useful
+ before read operation.
+
+*** fsls ( -- )
+
+- Description :: List all files and lists (directories, folders) in
+ current path.
+
+*** fslsr ( -- )
+
+- Description :: Same as =fsls= but recursively scans also sub lists.
+
+*** fscl ( DynStrHand -- )
+
+- Description :: Change list (path).
+
+*** fscreate ( DynStrHand -- DescPnt )
+
+- Description :: Create new file or list. Can create multiple lists at
+ once.
+- Example ::
+ When creating:
+ : "\listGAMES\listSTRATEGY\listSIMWORLD\5th-runme"
+ and only =\listGAMES\= already exist, then =listSTRATEGY= and
+ =listSIMWORLD= lists will be created, and empty file =5th-runme=
+ placed in there.
+
+*** fsDsave ( DynHand<data> DynStrHand<filename> -- )
+
+- Description :: Create new file and save all data from dynamic memory
+ block to it.
+
+*** fsDload ( DynStr<SrcFileName> DynHand<DataDest> -- )
+
+- Description :: Load whole file into dynamic memory block.
+
+*** fsDloadnew ( DynStr<SrcFileName> -- DynHand<DataDest> )
+
+- Description :: Load whole file into new dynamic memory block.
+
+** Dynamic memory
+*** dynal ( size -- handle )
+
+- Description :: Allocate dynamic memory block and return its handle.
+
+*** dynde ( handle -- )
+
+- Description :: Deallocate dynamic memory block.
+
+*** dynp ( handle -- addr )
+
+- Description :: Returns pointer to memory where dynamic block data
+ begins.
+
+*** dyns ( handle -- size )
+
+- Description :: Returns size of dynamic block.
+
+*** dynresize ( NewSize handle -- )
+
+- Description :: Nondestructively resize dynamic block.
+
+*** dync@ ( addr handle )
+
+- Description :: Read one byte from dynamic block.
+
+*** dync! ( byte addr dynhandle )
+
+- Description :: Write one byte to dynamic block.
+
+*** dyn@ ( addr handle )
+
+- Description :: Read 32 bit number from dynamic block. Address will
+ specify which number, not byte.
+
+*** dyn! ( 32BitNum addr dynhandle )
+
+- Description :: Write 32 bit number to dynamic block. Address will
+ specify which number, not byte.
+
+*** dyncon ( size "name" -- )
+
+- Description :: Allocate dynamic block with specified size, and
+ create constant holding its handle.
+- Example ::
+ : 100 dyncon MyNewBlock
+
+*** dyn. ( handle -- )
+
+- Description :: Write content of dynamic memory block to screen.
+
+** Graphics and text
+*** . ( n -- )
+
+- Description :: Print number on screen.
+
+*** d. ( n -- )
+
+- Description :: Print number on screen in decimal.
+
+*** ? ( addr -- )
+
+- Description :: Print 32 bit value located at *addr*.
+
+*** ." <string>" ( -- )
+
+- Description :: Print string into screen. Immideately compiles.
+- Example ::
+ : : greeting ." Hello, World" ;
+
+*** tab. ( -- )
+
+- Description :: Print tabulator.
+
+*** calccol ( b g r -- c )
+
+- Description :: Calculate color what best matches given Blue Green &
+ Red values. Values must be in range 0 - 255.
+
+*** imgalloc ( xsize ysize -- imgbuf )
+
+- Description :: Allocate image buffer for specified size.
+
+*** imgsize ( imgbuf -- )
+
+- Description :: Print on the screen X & Y size of image buffer.
+
+*** point ( x y imgbuf -- addr )
+
+- Description :: Returns memory address for specified pixel.
+
+*** pset ( color x y imgbuf -- )
+
+- Description :: Set graphic point.
+
+*** boxf ( x1 x2 y1 y2 imgbuf color -- )
+
+- Description :: Draw filled box.
+
+*** cls ( imgbuf -- )
+
+- Description :: Clear image buffer.
+
+*** setpal ( b g r color -- )
+
+- Description :: Set palette value for specified color. Values must be
+ in range 0 - 63.
+
+*** putchar ( char color x y imgbuf -- )
+
+- Description :: Put graphic character in imagebuffer to specified (x
+ & y) location.
+
+*** scroll ( x y imgbuf -- )
+
+- Description :: Scroll in imgbuf.
+
+*** scrollf ( color x y screen -- )
+
+- Description :: Scroll and fill empty space with given color.
+
+*** at! ( x y -- )
+
+- Description :: Set cursor location.
+
+*** curc! ( color -- )
+
+- Description :: Set text color.
+
+*** curb! ( color -- )
+
+- Description :: Set background color.
+
+*** colnorm ( -- )
+
+- Description :: Set text color to normal.
+
+*** colneg ( -- )
+
+- Description :: Set text color to negative (selected).
+
+*** dyntype ( dynhandle -- )
+
+- Description :: Display content of dynamic memory on screen.
+
+*** fsdisp ( file -- )
+
+- Description :: Clear screen, display file, and wait for key.
+
+*** type ( addr length -- )
+
+- Description :: Types on the screen string, from memory at *addr*
+ and specified length.
+
+*** write ( addr -- )
+
+- Description :: Types on the screen string, from memory at *addr*+1,
+ length is taken from *addr*.
+
+*** screen ( const, 32 bit )
+
+- Description :: Holds handle of screen buffer.
+
+*** copyscreen ( SrcImgHandle DestImgHandle -- )
+
+- Description :: Copy content of source image to destination image.
+ Source and destination images must have same size.
+
+** Math, memory & stack manipulation
+*** off ( n -- )
+
+- Description :: Writes 0 to given address, good for zeroing variable.
+- Example ::
+ : MyVariable off
+
+*** on ( n -- )
+
+- Description :: Writes -1 (true flag) to given address.
+- Example ::
+ : MyVariable on
+
+*** 2dup ( n1 n2 -- n1 n2 n1 n2 )
+*** 2drop ( n1 n2 -- )
+*** nip ( n1 n2 -- n2 )
+*** neg ( n1 -- -n1 )
+:PROPERTIES:
+:CUSTOM_ID: ID-a7cf0a47-40f1-49bc-afde-f6e86ac2c6d8
+:ID: a7cf0a47-40f1-49bc-afde-f6e86ac2c6d8
+:END:
++ See also: [[id:861f65e6-7ecb-43f0-9927-396855fb993f][abs ( n -- |n| )]]
+
+- Description :: Invert sign (negate) of a numerical value.
+
+*** bit@ ( n bit -- result )
+
+- Description :: Return specified bit from n.
+- Example ::
+ : 38 2 bit@
+ result will be 1
+
+*** to32bit ( n1 n2 n3 n4 -- n32 )
+
+- Description :: Treat 4 last stack elements as bytes and unite them
+ into 32 bit double word. Most significant byte on top.
+- Example ::
+ : 12 76 23 11 to32bit
+ result: 186076172
+
+*** to8bit ( n32 -- n1 n2 n3 n4 )
+
+- Description :: Break 32 bit number into 4 bytes. Useful if you need
+ to send 32 bit numbers though 8 bit COM port.
+- Example ::
+ : 186076172 to8bit
+ result: 12 76 23 11
+
+*** mod ( n1 n2 -- reminder )
+
+- Description :: Divide *n1* by *n2* and returns reminder.
+- Example ::
+ : 12 5 mod
+ result: 2
+
+*** bound ( low n high -- n )
+
+- Description :: Check if *n* is in given bounds (upper and lower
+ bounds are both inclusive). If *n* if outside of bounds, increase or
+ decrease its value accordingly to stay within bounds.
+- Examples ::
+ : 5 80 15 bound
+ result: 15
+
+ : 5 10 15 bound
+ result: 10
+
+ : 5 -10 15 bound
+ result: 5
+
+*** bound? ( low n high -- result )
+
+- Description :: Returns true if *n* is in the given bounds. Upper and
+ lower bounds are both inclusive.
+
+*** tab ( col -- spaces )
+
+- Description :: Calculate amount of spaces to add to reach next
+ tabulation from given column.
+
+*** count ( addr -- addr+1 n )
+
+- Description :: Useful for returning bytes from constantly
+ incareasing address. Module =type= is nice example.
+
+*** c, ( n -- )
+
+- Description :: Store one byte at memory specified by =h=. And
+ incarease =h= by 1.
+
+*** , ( n -- )
+
+- Description :: Store 32 bit number at memory specified by =h=. And
+ incarease =h= by 4.
+
+*** cmove ( addr1 addr2 n -- )
+
+- Description :: Copy *n* amount of bytes from memory at *addr1* to
+ memory at *addr2*.
+
+*** rnd ( limit -- result )
+
+- Description :: Generates random number in range 0 to =limit= - 1.
+
+*** abs ( n -- |n| )
+:PROPERTIES:
+:CUSTOM_ID: ID-861f65e6-7ecb-43f0-9927-396855fb993f
+:ID: 861f65e6-7ecb-43f0-9927-396855fb993f
+:END:
++ See also: [[id:a7cf0a47-40f1-49bc-afde-f6e86ac2c6d8][neg ( n1 -- -n1 )]]
+
+- Description :: Returns absolute value of *n*.
+
+** Dynamic & static strings
+
+Fifth supports both static and dynamic strings. Static strings must
+have predefined space reserved, and string mustn't exceed this
+length. They manipulation is faster. But they use more memory. Static
+string memory address is used to refer to the string.
+
+Dynamic strings can have at any time length form 0 to 0FFh, They take
+up only memory they currently need. They are held in dynamic memory
+blocks, so dynamic block handle is used to refer to this string.
+
+Both types of strings are stored in the way, where first (0th) byte
+holds current string length, following bytes are string itself.
+
+*** Dstral ( -- handle )
+
+- Description :: Allocate new dynamic string.
+
+*** Dstrlen ( handle -- length )
+
+- Description :: Return string length.
+
+*** c+Dstr ( chr handle -- )
+
+- Description :: Add one byte to end of the string.
+
+*** c+lDstr ( chr handle -- )
+
+- Description :: Add one byte to left side (beginning) of the string.
+
+*** Dstr. ( handle -- )
+
+- Description :: Write content of string into screen.
+
+*** Dstrsure ( size Dstr -- )
+
+- Description :: Makes sure that at least requested *size* (amount of
+ characters) is allocated for given dynamic string.
+
+*** Dstr2str ( handle address -- )
+
+- Description :: Copy dynamic string into static memory space.
+
+*** str2Dstr ( address handle -- )
+
+- Description :: Copy static string into dynamic string.
+
+*** Dstr+str ( Dstr addr -- )
+
+- Description :: Add content of dynamic string to static string.
+
+*** D" any string" ( -- Dstr )
+
+- Description :: Moves specified string into dynamic string called
+ =defDstr=.
+
+*** D> any_string ( -- Dstr )
+
+- Description :: Moves specified string into dynamic string called
+ =defDstr=. Space marks end of string!
+
+*** D>2 any_string ( -- Dstr )
+
+- Description :: Moves specified string into dynamic string called
+ =defDstr2=. Space marks end of string!
+
+*** Dstr+Dstr ( Dstr1 Dstr2 -- )
+
+- Description :: Adds *Dstr1* to *Dstr2* and places result into
+ *Dstr2*.
+
+*** Dstrclear ( Dstr -- )
+
+- Description :: Clears content of dynamic string.
+
+*** Dstr2Dstr ( Dstr1 Dstr2 -- )
+
+- Description :: Moves *Dstr1* to *Dstr2*.
+
+*** Dstr ( data" name -- )
+
+- Description :: Creates new dynamic string and moves specified data
+ into it. Then creates new constant with given *name* holding
+ created dynamic string handle.
+- Example ::
+ : Dstr Hello, my name is Sven!" message \ creates it
+ : message Dstr. \ tests it
+
+*** Dstrlscan ( char Dstr -- loc )
+
+- Description :: Searches dynamic string for *char*, from left to
+ right, returns first found *char* location in string, or 0, if not
+ found.
+
+*** Dstrrscan ( char Dstr -- loc )
+
+- Description :: Searches dynamic string for *char*, from right to
+ left, returns first found *char* location in string, or 0, if not
+ found.
+
+*** Dstrlscane ( char Dstr -- loc )
+
+- Description :: Same as =Dstrlscan= but returns string length+1 as
+ location.
+
+*** Dstrleft ( amo Dstr -- )
+
+- Description :: Only specified amount of characters from left remains
+ in dynamic string. ie. cut right part out.
+
+*** Dstrright ( amo Dstr -- )
+
+- Description :: Only specified amount of characters from right
+ remains in dynamic string. ie. cut left part out.
+
+*** Dstrcutl ( amo Dstr -- )
+
+- Description :: Cut specified amount of characters from left of
+ dynamic string out.
+
+*** Dstrsp ( char Dstr1 Dstr2 -- )
+
+- Description :: Separate dynamic string in *Dstr1* into two parts,
+ using *char* as separator. First part will be stored in *Dstr2*,
+ second part in *Dstr1*.
+- Example ::
+ : asc \ \ ..separator
+ : D> listF\listLIB\5TH_DRVMOUSE \ ..separate from
+ : defDstr2 \ ..place result in
+ : Dstrsp \ separation command
+ : defDstr Dstr. \ will be: listLIB\5TH_DRVMOUSE
+ : defDstr2 Dstr. \ will be: listF
+
+*** Dv ( addr -- )
+
+- Description :: Allocates empty dynamic string, and places its
+ handle into given address.
+
+*** Df ( addr -- )
+
+- Description :: Reads dynamic string handle from given address and
+ deallocates (frees) it.
+- Example ::
+ : var mystring1
+ : : testmodule
+ : mystring1 Dv \ allocates string
+ :
+ : <whatever>
+ :
+ : mystring1 Df ; \ deallocates it again when no longer needed.
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine opcodes 0 -- 9
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+* Overview
+
+This document describes virtual machine opcodes 0 through 9. These are
+the fundamental instructions for program flow control, stack
+manipulation, and basic I/O operations.
+
+*Quick Reference:*
+
+| Opcode | Name | Stack Effect | Bytecode Size | Purpose |
+|--------+------+------------------+---------------+---------------------------------|
+| 0 | nop | -- | 1 byte | Do nothing (placeholder) |
+| 1 | halt | -- | 1 byte | Exit emulator |
+| 2 | kbd@ | -- scancode | 1 byte | Read keyboard input |
+| 3 | num | -- n | 5 bytes | Push literal 32-bit number |
+| 4 | jmp | -- | 5 bytes | Unconditional jump |
+| 5 | call | -- (R: -- ret) | 5 bytes | Call subroutine |
+| 6 | 1+ | n -- n+1 | 1 byte | Increment by 1 |
+| 7 | 1- | n -- n-1 | 1 byte | Decrement by 1 |
+| 8 | dup | n -- n n | 1 byte | Duplicate top of stack |
+| 9 | drop | n -- | 1 byte | Discard top of stack |
+
+* 0: nop (No Operation)
+:PROPERTIES:
+:CUSTOM_ID: opcode-nop
+:ID: 4f0dee8a-647e-4d91-a252-aaf4782b5edc
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+-------------------|
+| Opcode | 0 |
+| Name | nop |
+| Stack | ( -- ) |
+| Bytecode | Single byte: =00= |
+
+*Description*
+
+Does nothing. Execution continues immediately to the next instruction.
+This opcode is handled as the entry point to the emulator's instruction
+fetch loop, meaning it effectively "falls through" to the next opcode.
+
+*When to Use:*
+
+- Padding bytecode for alignment purposes
+- Placeholder during development or debugging
+- Safe target for jump instructions when you need a "do nothing" branch
+- Filling unused entries in jump tables
+
+*Example:*
+
+#+BEGIN_SRC
+00 ; nop - do nothing
+03 05 00 00 00 ; num 5 - push 5 onto stack
+#+END_SRC
+
+No observable effect. The instruction pointer simply advances to the
+next opcode.
+
+* 1: halt (Stop Execution)
+:PROPERTIES:
+:CUSTOM_ID: opcode-halt
+:ID: 7c8841d3-2335-4ee9-b6b9-518d05354841
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+-------------------|
+| Opcode | 1 |
+| Name | halt |
+| Stack | ( -- ) |
+| Bytecode | Single byte: =01= |
+
+** Description
+
+Terminates the emulator process cleanly. This is the only proper way
+to exit the Fifth environment. When executed, the emulator:
+
+1. Restores text video mode (mode 3)
+2. Closes the virtual disk file handle
+3. Restores the original keyboard interrupt handler
+4. Returns control to DOS
+
+** When to Use
+
+- Terminating the program cleanly and exiting the Fifth environment
+- Implementing a "quit" or "bye" command in REPL
+- Shutting down after fatal errors
+
+** Example
+
+#+BEGIN_SRC
+01 ; halt - exit emulator immediately
+#+END_SRC
+
+In Fifth source code, this is typically invoked via the =bye= word,
+which compiles to the halt opcode.
+
+* 2: kbd@ (Keyboard Input)
+:PROPERTIES:
+:CUSTOM_ID: opcode-kbd-read
+:ID: 820b1b90-4f4c-4ab1-b49f-9b4a52ea2528
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+-------------------|
+| Opcode | 2 |
+| Name | kbd@ |
+| Stack | ( -- scancode ) |
+| Bytecode | Single byte: =02= |
+
+** Description
+
+Reads a keyboard scancode from the internal ring buffer and pushes it
+onto the data stack. This is a non-blocking operation: if no key is
+pending, it pushes 0 immediately.
+
+** Scan Code Behavior
+
+The keyboard hardware generates scan codes for every key event:
+
+- *Make code*: Generated when a key is pressed (values 1-127)
+- *Break code*: Generated when a key is released (make code + 128)
+
+For example:
+- Pressing 'A' produces scan code =1E=
+- Releasing 'A' produces scan code =9E= (1E + 80 hex)
+
+** Common Scan Codes (US QWERTY)
+
+| Key | Make Code | Break Code |
+|-------------+-----------+------------|
+| Escape | 01 | 81 |
+| 1-9 | 02-0A | 82-8A |
+| 0 | 0B | 8B |
+| Enter | 1C | 9C |
+| Space | 39 | B9 |
+| Left Arrow | 4B | CB |
+| Right Arrow | 4D | CD |
+| Up Arrow | 48 | C8 |
+| Down Arrow | 50 | D0 |
+| A | 1E | 9E |
+| Z | 2C | AC |
+
+** When to Use
+
+- Building interactive programs (games, text editors)
+- Reading raw keyboard input without translation to characters
+- Detecting key press/release events separately
+- Implementing custom keyboard drivers or input handlers
+
+** Example
+
+Basic key reading:
+
+#+BEGIN_SRC
+02 ; kbd@ - read scancode
+ ; data stack now contains: scancode (or 0 if no key)
+#+END_SRC
+
+** Return Values
+
+- Returns =0= if no key is available in the buffer
+- Returns scancode (1-255) when a key event is pending
+- Each call consumes one scancode from the buffer
+
+** Important Notes
+
+- This returns raw scan codes, not ASCII characters
+- Use a keyboard layout file (like =5TH_KBD_US=) to translate scan
+ codes to FSCII characters
+- The buffer holds 128 scan codes; older codes are overwritten if full
+
+* 3: num (Push Literal Number)
+:PROPERTIES:
+:CUSTOM_ID: opcode-num
+:ID: 8721dc8c-b237-4e9a-a509-0ded1c02329b
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+------------------------------------------|
+| Opcode | 3 |
+| Name | num |
+| Stack | ( -- n ) |
+| Bytecode | =03= followed by 4 bytes (little-endian) |
+
+** Description
+
+Pushes a 32-bit literal value onto the data stack. The value is encoded
+directly in the instruction stream immediately after the opcode byte.
+After execution, the instruction pointer advances by 4 bytes.
+
+** Bytecode Format
+
+#+BEGIN_SRC
+03 xx xx xx xx
+│ └──────────┘ 32-bit value (little-endian)
+└ opcode
+#+END_SRC
+
+** When to Use
+
+- Loading constant values for calculations
+- Pushing memory addresses onto the stack
+- Setting up parameters for function calls
+- Initializing counters, pointers, and loop variables
+
+** Example
+
+Push the value =0x12345678=:
+
+#+BEGIN_SRC
+03 ; num opcode
+78 56 34 12 ; 32-bit value 0x12345678 (little-endian)
+ ; stack now contains: 0x12345678
+#+END_SRC
+
+In Fifth source code, simply write a number:
+
+#+BEGIN_SRC fifth
+12345678 ; Compiles to: 03 78 56 34 12
+FF ; Compiles to: 03 FF 00 00 00
+1000 ; Compiles to: 03 00 10 00 00
+#+END_SRC
+
+** Important Notes
+
+- Numbers in Fifth source code are **hexadecimal by default**
+- Use =d.= to display numbers in decimal for debugging
+- All 32 bits are pushed; high bytes are zero-filled for small values
+- The value is stored in little-endian byte order in bytecode
+
+* 4: jmp (Unconditional Jump)
+:PROPERTIES:
+:CUSTOM_ID: opcode-jmp
+:ID: a5c53b60-ffe9-4ba4-ab87-30c2ccc51a45
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+------------------------------------------|
+| Opcode | 4 |
+| Name | jmp |
+| Stack | ( -- ) |
+| Bytecode | =04= followed by 4-byte target address |
+
+** Description
+
+Transfers execution unconditionally to the specified address. The
+target address is embedded in the instruction stream immediately after
+the opcode. No return address is saved.
+
+** Bytecode Format
+
+#+BEGIN_SRC
+04 aa aa aa aa
+│ └──────────┘ 32-bit target address (little-endian)
+└ opcode
+#+END_SRC
+
+** When to Use
+
+- Creating infinite loops (combined with conditional exits)
+- Skipping over code blocks
+- Implementing forward jumps in control structures
+- Jump tables and dispatch mechanisms
+
+** Example
+
+Jump to address =0x1000=:
+
+#+BEGIN_SRC
+04 ; jmp opcode
+00 10 00 00 ; target address 0x1000 (little-endian)
+ ; execution continues at 0x1000
+#+END_SRC
+
+Create an infinite loop:
+
+#+BEGIN_SRC
+; At address 0x100:
+04 00 01 00 00 ; jmp 0x100 - infinite loop
+#+END_SRC
+
+Skip over code:
+
+#+BEGIN_SRC
+04 ... ; jmp to skip_target
+... ; code to skip
+; skip_target:
+#+END_SRC
+
+** Important Notes
+
+- The address is a **virtual address** in the VM's address space
+- The emulator adds =xms_addr= to convert to a physical memory address
+- No return address is saved; use =call= for subroutines
+
+* 5: call (Call Subroutine)
+:PROPERTIES:
+:CUSTOM_ID: opcode-call
+:ID: 66efbd35-c2b6-4657-ba0f-ff00a3a2c312
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|--------------+----------------------------------------------|
+| Opcode | 5 |
+| Name | call |
+| Stack | ( -- ) |
+| Return Stack | ( -- ret-addr ) |
+| Bytecode | =05= followed by 4-byte target address |
+
+** Description
+
+Calls a subroutine at the specified address. The return address (the
+address of the instruction immediately following this call) is saved
+on the return stack. Use =ret= (opcode 11) to return to the caller.
+
+** Bytecode Format
+
+#+BEGIN_SRC
+05 aa aa aa aa
+│ └──────────┘ 32-bit target address (little-endian)
+└ opcode
+#+END_SRC
+
+** When to Use
+
+- Calling reusable code blocks (functions, procedures)
+- Implementing Forth word definitions
+- Building modular programs with separate subroutines
+- Recursion (with proper stack management)
+
+** Example
+
+#+BEGIN_SRC
+; Main code at 0x100:
+05 00 20 00 00 ; call 0x2000
+03 01 00 00 00 ; num 1 (this is where we return to)
+
+; Subroutine at 0x2000:
+... ; subroutine code
+0B ; ret - return to caller
+#+END_SRC
+
+** Stack Effects
+
+| Stack | Before | After |
+|--------------+--------+-----------|
+| Data Stack | ... | ... |
+| Return Stack | ... | ret-addr |
+
+The return address is the virtual address immediately after the 4-byte
+argument (i.e., the address of the next instruction).
+
+** Important Notes
+
+- The return stack is **separate** from the data stack
+- The return stack is also used by =push= and =pop= opcodes
+- Always balance =call= with =ret= to avoid return stack corruption
+- Nesting depth is limited by return stack size
+
+* 6: 1+ (Increment)
+:PROPERTIES:
+:CUSTOM_ID: opcode-1plus
+:ID: 76fe1c8f-756d-406e-9e8a-4ecc8f1d7369
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 6 |
+| Name | 1+ |
+| Stack | ( n -- n+1 ) |
+| Bytecode | Single byte: =06= |
+
+** Description
+
+Adds 1 to the value on top of the data stack, modifying it in place.
+This is a highly efficient single-byte operation.
+
+** When to Use
+
+- Incrementing loop counters
+- Advancing pointers through arrays or buffers
+- Computing successor values
+- Any situation where you need to add exactly 1
+
+** Example
+
+#+BEGIN_SRC
+03 05 00 00 00 ; num 5 - stack: [ 5 ]
+06 ; 1+ - stack: [ 6 ]
+06 ; 1+ - stack: [ 7 ]
+#+END_SRC
+
+** Efficiency Comparison
+
+| Operation | Bytecode | Size |
+|-------------+---------------------------+-------|
+| 1+ | =06= | 1 byte|
+| 1 + | =03 01 00 00 00= =18= | 6 bytes|
+
+Use =1+= instead of =1 += for better code density.
+
+** Important Notes
+
+- Modifies the value in place (no additional stack space needed)
+- Works identically on signed and unsigned 32-bit values
+- For adding larger values, use =+= (opcode 24)
+
+* 7: 1- (Decrement)
+:PROPERTIES:
+:CUSTOM_ID: opcode-1minus
+:ID: 523f93a3-359e-4a6d-b296-df25008db403
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 7 |
+| Name | 1- |
+| Stack | ( n -- n-1 ) |
+| Bytecode | Single byte: =07= |
+
+** Description
+
+Subtracts 1 from the value on top of the data stack, modifying it in
+place. This is a highly efficient single-byte operation.
+
+** When to Use
+
+- Decrementing loop counters
+- Moving pointers backward through arrays or buffers
+- Computing predecessor values
+- Counting down to zero
+
+** Example
+
+#+BEGIN_SRC
+03 0A 00 00 00 ; num 10 - stack: [ 10 ]
+07 ; 1- - stack: [ 9 ]
+07 ; 1- - stack: [ 8 ]
+#+END_SRC
+
+** Common Pattern: Countdown Loop
+
+#+BEGIN_SRC
+: loop ( n -- )
+ begin
+ dup . ; display counter
+ 1- dup ; decrement and copy for test
+ 0= until ; loop until zero
+ drop ; clean up
+;
+
+A loop ; prints A, 9, 8, ... 1
+#+END_SRC
+
+** Important Notes
+
+- Modifies the value in place (no additional stack space needed)
+- Works identically on signed and unsigned 32-bit values
+- For subtracting larger values, use =-= (opcode 25)
+
+---------------------------------------------------------------------
+
+* 8: dup (Duplicate)
+:PROPERTIES:
+:CUSTOM_ID: opcode-dup
+:ID: 2a6a449a-fc76-421c-a81c-c2024a15fc78
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 8 |
+| Name | dup |
+| Stack | ( n -- n n ) |
+| Bytecode | Single byte: =08= |
+
+** Description
+
+Duplicates the value on top of the data stack, pushing a copy. The
+original value remains, and an identical copy is placed on top.
+
+** When to Use
+
+- Preserving a value before consuming operations
+- Using the same value multiple times in a calculation
+- Displaying values without losing them
+- Setting up parameters for operations that consume values
+
+** Example
+
+#+BEGIN_SRC
+03 2A 00 00 00 ; num 42 - stack: [ 42 ]
+08 ; dup - stack: [ 42 42 ]
+#+END_SRC
+
+** Common Patterns
+
+*** Read and Keep Address
+
+#+BEGIN_SRC
+addr dup @ ; stack: [ addr value ]
+ ; read from address while keeping the address
+#+END_SRC
+
+*** Display and Keep
+
+#+BEGIN_SRC
+value dup . ; display value while keeping it on stack
+#+END_SRC
+
+*** Test Without Consuming
+
+#+BEGIN_SRC
+value dup 0= ; test if zero, keep original value
+if
+ ... ; handle zero case (value still on stack)
+then
+drop ; now consume it
+#+END_SRC
+
+** Important Notes
+
+- Increases stack depth by 1
+- Often paired with =drop= to test values without consuming them
+- For duplicating the second stack item, use =over= (opcode 22)
+
+---------------------------------------------------------------------
+
+* 9: drop (Discard)
+:PROPERTIES:
+:CUSTOM_ID: opcode-drop
+:ID: 4600dbeb-1833-4e7d-af7e-f6cc6c98d022
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 9 |
+| Name | drop |
+| Stack | ( n -- ) |
+| Bytecode | Single byte: =09= |
+
+** Description
+
+Removes and discards the value from the top of the data stack. The
+value is completely lost.
+
+** When to Use
+
+- Cleaning up the stack after calculations
+- Discarding values that are no longer needed
+- Consuming function results you don't need
+- Balancing stack operations
+
+** Example
+
+#+BEGIN_SRC
+03 2A 00 00 00 ; num 42 - stack: [ 42 ]
+03 64 00 00 00 ; num 100 - stack: [ 42 100 ]
+09 ; drop - stack: [ 42 ]
+09 ; drop - stack: [ ]
+#+END_SRC
+
+** Common Patterns
+
+*** Discard Unused Return Values
+
+#+BEGIN_SRC
+some-operation drop ; call operation, ignore result
+#+END_SRC
+
+*** Execute for Side Effect Only
+
+#+BEGIN_SRC
+addr @ drop ; read from memory, discard value
+ ; useful for prefetching or triggering hardware
+#+END_SRC
+
+*** Clean Up After Conditional
+
+#+BEGIN_SRC
+condition if
+ ... ; condition was true (consumed by if)
+else
+ ... ; condition was false
+then
+#+END_SRC
+
+** Important Notes
+
+- Reduces stack depth by 1
+- The value is completely lost; ensure you don't need it
+- Often paired with =dup= for testing values without consuming them
+- For discarding two values, use =2drop= (defined in high-level Fifth)
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine opcodes 10 -- 19
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+* Overview
+
+This document describes virtual machine opcodes 10 through 19. These
+opcodes provide conditional branching, subroutine return, byte-level
+memory access, stack-to-return-stack operations, stack rotation, and
+disk I/O.
+
+*Quick Reference:*
+
+| Opcode | Name | Stack Effect | Bytecode Size | Purpose |
+|--------+--------+----------------+---------------+-------------------------------------------------|
+| 10 | if | flag -- | 5 bytes | Conditional jump (jump if zero) |
+| 11 | ret | -- | 1 byte | Return from subroutine |
+| 12 | c@ | addr -- byte | 1 byte | Read byte from memory |
+| 13 | c! | byte addr -- | 1 byte | Write byte to memory |
+| 14 | push | n -- | 1 byte | Move data to return stack |
+| 15 | pop | -- n | 1 byte | Move data from return stack |
+| 16 | unused | -- | 1 byte | Reserved for future use. Currently acts as nop. |
+| 17 | rot | a b c -- b c a | 1 byte | Rotate three stack elements |
+| 18 | disk@ | sector addr -- | 1 byte | Read 1KB sector from disk |
+| 19 | disk! | addr sector -- | 1 byte | Write 1KB sector to disk |
+
+* 10: if (Conditional Jump)
+:PROPERTIES:
+:CUSTOM_ID: opcode-if
+:ID: d6f834b6-9a37-4414-91b3-62b3e1d920c1
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+------------------------------------------|
+| Opcode | 10 (0x0A) |
+| Name | if |
+| Stack | ( flag -- ) |
+| Bytecode | =0A= followed by 4-byte target address |
+
+*Description:*
+
+Pops the top value from the data stack. If the value is zero, jumps to
+the address embedded in the instruction stream. If the value is
+non-zero, skips the address and continues with the next instruction.
+
+This "jump if false" semantics is exactly what is needed to jump/skip
+over code because comparison condition was not met:
+
+: #1: <comparison logic, leaves comparison result on the top of data stack>
+: #2: <jmp instruction + pointer to #4 address>
+: #3: <this code will be executed conditionally, if comparison was true>
+: #4: <any other code that follows after IF ... THEN ... block.>
+
+
+*Bytecode Format:*
+
+#+BEGIN_SRC
+0A aa aa aa aa
+│ └──────────┘ 32-bit target address (little-endian)
+└ opcode
+#+END_SRC
+
+*When to Use*
+
+- Implementing conditional branches in control structures
+- Building if-then-else constructs (the "else" branch)
+- Loop exit conditions
+
+*Important Notes:*
+
+- The jump is taken when flag is ZERO (false condition)
+- This is actually "jump if false" semantics
+- High-level Fifth words like =if= =then= =else= compile to this opcode
+- The address is a virtual address in the VM's address space
+
+* 11: ret (Return from Subroutine)
+:PROPERTIES:
+:CUSTOM_ID: opcode-ret
+:ID: 6e683977-a985-4bb8-9d2c-c860d30e1df6
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|----------+-------------------|
+| Opcode | 11 (0x0B) |
+| Name | ret |
+| Stack | ( -- ) |
+| R. Stack | ( ret-addr -- ) |
+| Bytecode | Single byte: =0B= |
+
+*Description:*
+
+Returns from a subroutine by popping the return address from the
+return stack and jumping to it. This is the counterpart to the =call=
+opcode (5).
+
+
+*When to Use:*
+
+- Ending subroutine/word definitions
+- Early exit from subroutines
+
+*Example:*
+
+#+BEGIN_SRC
+; Subroutine at address 0x2000:
+03 2A 00 00 00 ; num 42 - push a value
+0B ; ret - return to caller
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+: myword ( -- n )
+ 2A \ push 42
+; \ compiles to ret
+#+END_SRC
+
+** Important Notes
+
+- Each =ret= must have a matching =call= to maintain return stack balance
+- Unbalanced call/ret pairs will corrupt the return stack
+- The return stack is also used by =push= and =pop= opcodes
+- Return addresses are virtual addresses (without xms_addr offset)
+
+* 12: c@ (Fetch Byte from Memory)
+:PROPERTIES:
+:CUSTOM_ID: opcode-cfetch
+:ID: a2ce44f7-b661-44e0-909b-644ff52aa38e
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 12 (0x0C) |
+| Name | c@ (c-fetch) |
+| Stack | ( addr -- byte ) |
+| Bytecode | Single byte: =0C= |
+
+*Description:*
+
+Reads a single byte from the specified memory address and pushes it
+onto the data stack. The byte value is zero-extended to 32 bits
+(padded with zeros in the upper 24 bits).
+
+*When to Use:*
+
+- Reading character data from strings
+- Parsing binary data structures
+- Reading individual pixels from image buffers
+
+
+*Example:*
+
+Read a byte from address 0x1000:
+
+#+BEGIN_SRC
+03 00 10 00 00 ; num 0x1000 - push address
+0C ; c@ - read byte
+; Stack now contains: byte-value (0-255, zero-extended)
+#+END_SRC
+
+
+*Important Notes:*
+
+- The byte is zero-extended: reading 0xFF gives 0x000000FF on stack
+- The address is a virtual address (xms_addr is added internally)
+
+* 13: c! (Store Byte to Memory)
+:PROPERTIES:
+:CUSTOM_ID: opcode-cstore
+:ID: f129ef87-a31a-40fe-b8d3-984da2db90e2
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 13 (0x0D) |
+| Name | c! (c-store) |
+| Stack | ( byte addr -- ) |
+| Bytecode | Single byte: =0D= |
+
+*Description:*
+
+Stores a single byte to the specified memory address. The byte value
+is taken from the low 8 bits of the value on the stack. The upper 24
+bits are ignored.
+
+*When to Use:*
+
+- Writing character data to strings
+- Building binary data structures
+- Drawing individual pixels to image buffers
+
+*Example:*
+
+Store byte 0x42 at address 0x1000:
+
+#+BEGIN_SRC
+03 42 00 00 00 ; num 0x42 - byte value to store
+03 00 10 00 00 ; num 0x1000 - destination address
+0D ; c! - store byte
+; Memory at 0x1000 now contains 0x42
+#+END_SRC
+
+
+*Important Notes:*
+
+- Upper 24 bits are ignored (no error if value > 255)
+- The address is a virtual address
+
+* 14: push (Move Data to Return Stack)
+:PROPERTIES:
+:CUSTOM_ID: opcode-push
+:ID: e4bcbaf1-7724-4051-b19c-1aa7cd06eae6
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 14 (0x0E) |
+| Name | push |
+| Stack | ( n -- ) |
+| R. Stack | ( -- n ) |
+| Bytecode | Single byte: =0E= |
+
+*Description:*
+
+Moves the top value from the data stack to the return stack. This
+provides temporary storage that doesn't interfere with data stack
+operations.
+
+*When to Use:*
+
+- Temporarily saving values during complex stack manipulations
+
+*Example:*
+
+Save a value for later retrieval:
+
+#+BEGIN_SRC
+03 2A 00 00 00 ; num 42 - push value onto data stack
+0E ; push - move 42 to return stack
+; Data stack is now empty
+; Return stack now contains 42
+
+; ... do other work ...
+
+0F ; pop - retrieve 42 from return stack
+; Data stack now contains 42
+#+END_SRC
+
+
+*Important Notes:*
+
+- Must be balanced with =pop= to avoid return stack corruption
+- Do not mix carelessly with =call= /=ret= pairs
+- The return stack grows downward (like the data stack)
+- Popping too many times can corrupt return addresses
+
+* 15: pop (Move Data from Return Stack)
+:PROPERTIES:
+:CUSTOM_ID: opcode-pop
+:ID: 21871d09-4d58-440f-8c94-231105aa4e3f
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 15 (0x0F) |
+| Name | pop |
+| Stack | ( -- n ) |
+| R. Stack | ( n -- ) |
+| Bytecode | Single byte: =0F= |
+
+*Description:*
+
+Moves the top value from the return stack to the data stack. This is
+the inverse of the =push= opcode.
+
+*When to Use:*
+
+- Retrieving temporarily saved values
+
+*Example:*
+
+Retrieve a saved value:
+
+#+BEGIN_SRC
+; Assuming return stack contains 42 (from previous 'push')
+0F ; pop - move 42 from return stack to data stack
+; Data stack now contains 42
+; Return stack top item removed
+#+END_SRC
+
+Important Notes:
+- Popping from an empty return stack causes undefined behavior
+- Always ensure there's a value on the return stack before calling =pop=
+- Balance every =push= with a corresponding =pop=
+
+** Related Opcode: i
+
+The =i= opcode (31) copies (rather than moves) the top of the return
+stack to the data stack, leaving the return stack unchanged.
+
+| Opcode | Action | R. Stack Effect |
+|--------+------------------------+-----------------|
+| i | Copy top to data stack | <unchanged> |
+| pop | Move top to data stack | n -- |
+
+* 16: (Unused/Reserved)
+:PROPERTIES:
+:CUSTOM_ID: opcode-unused
+:END:
+
+Opcode 16 (0x10) is reserved for future use. Opcode currently behaves as *nop* instruction.
+
+* 17: rot (Rotate Three Stack Elements)
+:PROPERTIES:
+:CUSTOM_ID: opcode-rot
+:ID: 4cee73f7-c105-4b96-9380-ff89bd7fedad
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+----------------------------|
+| Opcode | 17 (0x11) |
+| Name | rot |
+| Stack | ( n1 n2 n3 -- n2 n3 n1 ) |
+| Bytecode | Single byte: =11= |
+
+*Description*
+
+Rotates the top three values on the data stack. The third item from
+the top moves to the top, while the top two items shift down.
+
+*Visual Representation:*
+#+BEGIN_SRC
+Before: [ ... n1 n2 n3 ] (n3 is on top)
+After: [ ... n2 n3 n1 ] (n1 is now on top)
+#+END_SRC
+
+Rotation direction: the deepest of the three (n1) rotates to the top.
+
+*When to Use:*
+- Accessing values buried deeper on the stack
+- Swapping values in multi-item calculations
+
+
+*Example:*
+
+#+BEGIN_SRC
+03 01 00 00 00 ; num 1 - push n1
+03 02 00 00 00 ; num 2 - push n2
+03 03 00 00 00 ; num 3 - push n3
+; Stack: [ 1 2 3 ] with 3 on top
+
+11 ; rot
+; Stack: [ 2 3 1 ] with 1 on top
+#+END_SRC
+
+Important Notes:
+
+- Requires at least three items on the data stack
+- Rotating with fewer items causes undefined behavior
+- This is a rotation, not a swap - all three elements move
+
+* 18: disk@ (Read Disk Sector)
+:PROPERTIES:
+:CUSTOM_ID: opcode-disk-read
+:ID: bed1aa27-66ac-4c73-bbb9-e49ff2aa67c5
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------------|
+| Opcode | 18 (0x12) |
+| Name | disk@ (disk-fetch) |
+| Stack | ( sector addr -- ) |
+| Bytecode | Single byte: =12= |
+
+*Description:*
+
+Reads one sector (1024 bytes) from the virtual disk into memory at the
+specified address.
+
+*Parameters:*
+
+- *sector* (top of stack): Sector number to read from (0-based)
+- *addr* (second): Destination memory address for the data
+
+*When to Use:*
+
+- Loading program code from disk
+- Reading data files
+- Accessing filesystem structures
+
+*Example:*
+
+Read sector 16 into memory at 0x2000:
+
+#+BEGIN_SRC
+03 10 00 00 00 ; num 16 - sector number
+03 00 20 00 00 ; num 0x2000 - destination address
+12 ; disk@ - read sector
+; 1024 bytes from sector 16 now at address 0x2000
+#+END_SRC
+
+** Technical Details
+
+- 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
+
+** 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
+- The disk is accessed through DOS file handles, not BIOS interrupts
+
+* 19: disk! (Write Disk Sector)
+:PROPERTIES:
+:CUSTOM_ID: opcode-disk-write
+:ID: 02eda775-e483-4057-b809-de36d586579b
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------------|
+| Opcode | 19 (0x13) |
+| Name | disk! (disk-store) |
+| Stack | ( addr sector -- ) |
+| Bytecode | Single byte: =13= |
+
+*Description*
+
+Writes one sector (1024 bytes) from memory to the virtual disk at the
+specified sector number.
+
+*Parameters:*
+
+- *addr* (top of stack): Source memory address
+- *sector* (second): Sector number to write to (0-based)
+
+*When to Use:*
+
+- Saving program data to disk
+- Modifying filesystem structures
+- Persisting state across emulator sessions
+
+*Example:*
+
+Write memory at 0x2000 to sector 16:
+
+#+BEGIN_SRC
+03 00 20 00 00 ; num 0x2000 - source address
+03 10 00 00 00 ; num 16 - sector number
+13 ; disk! - write sector
+; 1024 bytes from address 0x2000 written to sector 16
+#+END_SRC
+
+*Technical Details:*
+
+- One sector = 1024 bytes (1 KB)
+- Data is first copied to a temporary buffer in conventional memory
+ before being written to disk (required for XMS memory access)
+- Writing to sector 0 may corrupt the bootloader/kernel
+
+*Stack Order Note:*
+
+The stack order is =( addr sector -- )=, which is reversed from
+=disk@= =( sector addr -- )=. This is because the data source (addr)
+is "produced" first, then the destination (sector) is specified.
+
+*Important Notes*
+
+- The address is a virtual address
+- Writing to the disk image is immediate (no buffering)
+- Be careful not to overwrite critical sectors (kernel, filesystem)
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine opcodes 20 -- 29
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+* Overview
+
+This document describes virtual machine opcodes 20 through 29. These are
+fundamental instructions for memory access, stack manipulation, arithmetic
+operations, and value comparison.
+
+** Quick Reference
+
+| Opcode | Name | Stack Effect | Bytecode Size | Purpose |
+|--------+-------+-------------------+---------------+----------------------------------|
+| 20 | @ | addr -- value | 1 byte | Fetch 32-bit value from memory |
+| 21 | ! | value addr -- | 1 byte | Store 32-bit value to memory |
+| 22 | over | n1 n2 -- n1 n2 n1 | 1 byte | Duplicate second stack item |
+| 23 | swap | n1 n2 -- n2 n1 | 1 byte | Exchange top two stack items |
+| 24 | + | n1 n2 -- n1+n2 | 1 byte | Add two 32-bit values |
+| 25 | - | n1 n2 -- n1-n2 | 1 byte | Subtract two 32-bit values |
+| 26 | * | n1 n2 -- n1*n2 | 1 byte | Multiply two 32-bit values |
+| 27 | / | n1 n2 -- n1/n2 | 1 byte | Divide two 32-bit values |
+| 28 | > | n1 n2 -- flag | 1 byte | Greater than comparison |
+| 29 | < | n1 n2 -- flag | 1 byte | Less than comparison |
+
+* 20: @ (Fetch)
+:PROPERTIES:
+:CUSTOM_ID: opcode-fetch
+:ID: 0a54f24b-e62a-4244-85e1-855a0007c81e
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 20 |
+| Name | @ |
+| Stack | ( addr -- value ) |
+| Bytecode | Single byte: =14= |
+
+** Description
+
+Reads a 32-bit value from the specified memory address and replaces
+the address with the fetched value on the data stack. This is the
+primary operation for reading data from memory.
+
+** Memory Access
+
+The operation performs virtual address translation:
+
+1. Takes virtual address from top of data stack
+2. Converts to physical address by adding =xms_addr=
+3. Reads 32-bit value from physical memory
+4. Replaces address with value on stack
+
+** When to Use
+
+- Reading variables and data structures from memory
+- Accessing array elements
+- Loading values for computation
+- Implementing high-level words that need memory access
+- Reading hardware registers (when mapped to memory)
+
+** Example
+
+Read a value from address =0x1000=:
+
+#+BEGIN_SRC
+03 00 10 00 00 ; num 0x1000 - push address 0x1000
+14 ; @ - read 32-bit value from 0x1000
+ ; stack now contains the value from address 0x1000
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+0x1000 @ \ read 32-bit value from address 0x1000
+#+END_SRC
+
+** Important Notes
+
+- This is the 32-bit equivalent of =c@= (opcode 16)
+- Virtual address translation provides memory management
+- Reads full 32-bit words, not bytes
+- The address is consumed and replaced with the value
+- No bounds checking is performed
+
+** Common Patterns
+
+*** Read and Use
+
+#+BEGIN_SRC fifth
+addr @ . \ read from address and display
+#+END_SRC
+
+*** Array Access
+
+#+BEGIN_SRC fifth
+base index 4 * + @ \ read array[index] (32-bit elements)
+#+END_SRC
+
+---------------------------------------------------------------------
+
+* 21: ! (Store)
+:PROPERTIES:
+:CUSTOM_ID: opcode-store
+:ID: 50e0c841-d678-41b4-be5a-1bab792f367d
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 21 |
+| Name | ! |
+| Stack | ( value addr -- ) |
+| Bytecode | Single byte: =15= |
+
+** Description
+
+Stores a 32-bit value to the specified memory address. Both the value
+and address are consumed from the data stack, leaving a clean stack.
+
+** Memory Access
+
+The operation performs virtual address translation:
+
+1. Takes address from top of stack
+2. Takes value to store from second on stack
+3. Converts virtual address to physical address
+4. Stores 32-bit value to physical memory
+5. Removes both arguments from stack
+
+** When to Use
+
+- Writing variables and data structures to memory
+- Storing array elements
+- Saving computation results
+- Implementing high-level words that modify memory
+- Writing to hardware registers (when mapped to memory)
+
+** Example
+
+Store value =0x12345678= to address =0x1000=:
+
+#+BEGIN_SRC
+03 78 56 34 12 ; num 0x12345678 - push value to store
+03 00 10 00 00 ; num 0x1000 - push destination address
+15 ; ! - store value to address
+ ; stack is now empty
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+0x12345678 0x1000 ! \ store 0x12345678 at address 0x1000
+#+END_SRC
+
+** Important Notes
+
+- This is the 32-bit equivalent of =c!= (opcode 17)
+- Virtual address translation provides memory protection
+- Order is value then addr (Forth standard: value to address)
+- Both arguments are consumed (stack cleaned up)
+- No bounds checking is performed
+- Overwrites existing data at the target address
+
+** Common Patterns
+
+*** Store Constant
+
+#+BEGIN_SRC fifth
+42 addr ! \ store constant 42 at addr
+#+END_SRC
+
+*** Array Assignment
+
+#+BEGIN_SRC fifth
+value base index 4 * + ! \ array[index] = value (32-bit elements)
+#+END_SRC
+
+*** Computed Store
+
+#+BEGIN_SRC fifth
+a b + addr ! \ store (a + b) at addr
+#+END_SRC
+
+---------------------------------------------------------------------
+
+* 22: over (Over)
+:PROPERTIES:
+:CUSTOM_ID: opcode-over
+:ID: 29828b20-b14d-41ff-9705-ca576c352fb4
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 22 |
+| Name | over |
+| Stack | ( n1 n2 -- n1 n2 n1 ) |
+| Bytecode | Single byte: =16= |
+
+** Description
+
+Duplicates the second item on the data stack, placing a copy on top.
+This allows access to the second item without disturbing the top item.
+
+** Stack Behavior
+
+The operation preserves the original order while adding a copy:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ n1 n2 n1 ] (new n1 = TOS)
+#+END_SRC
+
+** When to Use
+
+- Preserving values while using the top item
+- Accessing the second item when you need to keep the top
+- Setting up multiple uses of the same value
+- Implementing arithmetic operations that need the original value
+- Creating stack patterns for complex operations
+
+** Example
+
+Duplicate the second item:
+
+#+BEGIN_SRC
+03 01 00 00 00 ; num 1 - stack: [ 1 ]
+03 02 00 00 00 ; num 2 - stack: [ 1 2 ]
+16 ; over - stack: [ 1 2 1 ]
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+1 2 over \ stack: 1 2 1
+#+END_SRC
+
+** Important Notes
+
+- Essential for preserving values while using the top item
+- More efficient than dup+swap+drop combination
+- Increases stack depth by 1
+- Often paired with arithmetic operations
+- Fundamental stack manipulation word
+
+** 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
+a b over + \ compute a + b, keeping a on stack
+#+END_SRC
+
+*** Save Address for Later Use
+
+#+BEGIN_SRC fifth
+addr over @ \ read from addr, keeping addr for later use
+#+END_SRC
+
+---------------------------------------------------------------------
+
+* 23: swap (Swap)
+:PROPERTIES:
+:CUSTOM_ID: opcode-swap
+:ID: 905894ae-d687-47cd-add0-f1904179a8a0
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 23 |
+| Name | swap |
+| Stack | ( n1 n2 -- n2 n1 ) |
+| Bytecode | Single byte: =17= |
+
+** Description
+
+Exchanges the top two values on the data stack. This is essential for
+reordering arguments when the natural stack order doesn't match the
+required order for operations.
+
+** Stack Behavior
+
+The operation reverses the order of the top two items:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ n2 n1 ] (n1 = TOS, n2 = second)
+#+END_SRC
+
+** When to Use
+
+- Reordering arguments for arithmetic operations
+- Fixing stack order after computations
+- Implementing algorithms that need specific operand order
+- Exchanging values in sorting algorithms
+- Correcting stack order for function calls
+
+** Example
+
+Exchange the top two values:
+
+#+BEGIN_SRC
+03 01 00 00 00 ; num 1 - stack: [ 1 ]
+03 02 00 00 00 ; num 2 - stack: [ 1 2 ]
+17 ; swap - stack: [ 2 1 ]
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+1 2 swap \ stack: 2 1
+#+END_SRC
+
+** Important Notes
+
+- Fundamental stack manipulation operation
+- Used extensively for argument reordering
+- More efficient than multiple push/pop operations
+- Stack depth remains unchanged
+- Essential for correct operand order in arithmetic
+
+** Common Patterns
+
+*** Fix Subtraction Order
+
+#+BEGIN_SRC fifth
+min max swap - \ compute max - min
+#+END_SRC
+
+*** Correct Division Order
+
+#+BEGIN_SRC fifth
+divisor dividend swap / \ compute dividend / divisor
+#+END_SRC
+
+*** Exchange Variables
+
+#+BEGIN_SRC fifth
+addr1 @ addr2 @ swap \ read two values and exchange them
+addr1 ! addr2 ! \ store them back in exchanged order
+#+END_SRC
+
+---------------------------------------------------------------------
+
+* 24: + (Add)
+:PROPERTIES:
+:CUSTOM_ID: opcode-add
+:ID: bf4434bb-91a5-40c1-93de-90a0e05a50ec
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 24 |
+| Name | + |
+| Stack | ( n1 n2 -- n1+n2 ) |
+| Bytecode | Single byte: =18= |
+
+** Description
+
+Adds the top two values on the data stack, replacing them with the sum.
+This is a fundamental arithmetic operation for mathematical computations.
+
+** Operation
+
+The operation performs unsigned 32-bit addition:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ sum ] (sum = n1 + n2)
+#+END_SRC
+
+** When to Use
+
+- Basic arithmetic calculations
+- Computing sums and totals
+- Pointer arithmetic
+- Incrementing values by arbitrary amounts
+- Mathematical expressions and formulas
+
+** Example
+
+Add two numbers:
+
+#+BEGIN_SRC
+03 05 00 00 00 ; num 5 - stack: [ 5 ]
+03 03 00 00 00 ; num 3 - stack: [ 5 3 ]
+18 ; + - stack: [ 8 ]
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+5 3 + \ result: 8
+#+END_SRC
+
+** Important Notes
+
+- Performs unsigned 32-bit addition
+- Overflow wraps around modulo 2^32
+- Operates in place for efficiency (no extra stack slot needed)
+- Reduces stack depth by 1
+- Commutative operation (a + b = b + a)
+
+** Common Patterns
+
+*** Increment by Value
+
+#+BEGIN_SRC fifth
+value increment + \ value = value + increment
+#+END_SRC
+
+*** Pointer Arithmetic
+
+#+BEGIN_SRC fifth
+ptr 4 + \ advance pointer by 4 bytes
+#+END_SRC
+
+*** Sum Multiple Values
+
+#+BEGIN_SRC fifth
+a b + c + d + \ compute a + b + c + d
+#+END_SRC
+
+** Edge Cases
+
+- =0xFFFFFFFF + 1 = 0= (wrap around)
+- =Any number + 0 = same number=
+- Large additions may overflow
+
+---------------------------------------------------------------------
+
+* 25: - (Subtract)
+:PROPERTIES:
+:CUSTOM_ID: opcode-sub
+:ID: 9afc77fc-ecee-4a69-b1d2-f511e64a0d72
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 25 |
+| Name | - |
+| Stack | ( n1 n2 -- n1-n2 ) |
+| Bytecode | Single byte: =19= |
+
+** Description
+
+Subtracts the top value from the second value on the data stack. The
+result replaces both values. This operation is not commutative - order
+matters.
+
+** Operation
+
+The operation performs unsigned 32-bit subtraction:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ diff ] (diff = n1 - n2)
+#+END_SRC
+
+** When to Use
+
+- Computing differences between values
+- Decrementing by arbitrary amounts
+- Distance calculations
+- Mathematical expressions requiring subtraction
+- Reverse arithmetic operations
+
+** Example
+
+Subtract two numbers:
+
+#+BEGIN_SRC
+03 0A 00 00 00 ; num 10 - stack: [ 10 ]
+03 03 00 00 00 ; num 3 - stack: [ 10 3 ]
+19 ; - - stack: [ 7 ]
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+10 3 - \ result: 7 (10 - 3)
+#+END_SRC
+
+** Important Notes
+
+- Performs unsigned 32-bit subtraction
+- Underflow wraps around modulo 2^32
+- Operates in place for efficiency
+- Reduces stack depth by 1
+- NOT commutative (a - b ≠ b - a)
+- Order: second item minus top item
+
+** Common Patterns
+
+*** Decrement by Value
+
+#+BEGIN_SRC fifth
+value decrement - \ value = value - decrement
+#+END_SRC
+
+*** Distance Calculation
+
+#+BEGIN_SRC fifth
+end start - \ compute distance from start to end
+#+END_SRC
+
+*** Reverse Subtraction
+
+#+BEGIN_SRC fifth
+a b swap - \ compute b - a instead of a - b
+#+END_SRC
+
+** Edge Cases
+
+- =0 - 1 = 0xFFFFFFFF= (wrap around)
+- =Any number - 0 = same number=
+- =n - n = 0= for any n
+
+---------------------------------------------------------------------
+
+* 26: * (Multiply)
+:PROPERTIES:
+:CUSTOM_ID: opcode-mul
+:ID: 85833e5c-e3ae-41a7-ad3d-6e9f0ecc9cdb
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 26 |
+| Name | * |
+| Stack | ( n1 n2 -- n1*n2 ) |
+| Bytecode | Single byte: =1A= |
+
+** Description
+
+Multiplies the top two values on the data stack using signed 32-bit
+multiplication. The result is the low 32 bits of the product.
+
+** Operation
+
+The operation performs signed 32-bit multiplication:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ product ] (product = n1 * n2)
+#+END_SRC
+
+** When to Use
+
+- Multiplication operations
+- Area calculations
+- Scaling values
+- Mathematical formulas
+- Computing products and multiples
+
+** Example
+
+Multiply two numbers:
+
+#+BEGIN_SRC
+03 05 00 00 00 ; num 5 - stack: [ 5 ]
+03 03 00 00 00 ; num 3 - stack: [ 5 3 ]
+1A ; * - stack: [ 15 ]
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+5 3 * \ result: 15
+#+END_SRC
+
+** Important Notes
+
+- Uses IMUL instruction (signed multiplication)
+- Result is low 32 bits only (high bits in edx are discarded)
+- Handles both positive and negative numbers correctly
+- Overflow behavior: result wraps around modulo 2^32
+- Reduces stack depth by 1
+- Commutative operation (a * b = b * a)
+
+** Common Patterns
+
+*** Area Calculation
+
+#+BEGIN_SRC fifth
+width height * \ compute area of rectangle
+#+END_SRC
+
+*** Scaling
+
+#+BEGIN_SRC fifth
+value scale * \ scale value by factor
+#+END_SRC
+
+*** Power of Two
+
+#+BEGIN_SRC fifth
+value 2 * \ multiply by 2 (equivalent to shift left by 1)
+#+END_SRC
+
+** Edge Cases
+
+- =0 * anything = 0=
+- =1 * anything = anything=
+- =(-1) * (-1) = 1=
+- Large products may overflow (wrap around)
+
+---------------------------------------------------------------------
+
+* 27: / (Divide)
+:PROPERTIES:
+:CUSTOM_ID: opcode-div
+:ID: e5ff13c7-a9e6-4035-8efd-220d05381bf1
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 27 |
+| Name | / |
+| Stack | ( n1 n2 -- n1/n2 ) |
+| Bytecode | Single byte: =1B= |
+
+** Description
+
+Divides the second value by the top value using signed 32-bit division.
+Returns the quotient only (remainder is discarded).
+
+** Operation
+
+The operation performs signed 32-bit division:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = divisor = TOS, n1 = dividend = second)
+After: [ quotient ] (quotient = n1 / n2)
+#+END_SRC
+
+** When to Use
+
+- Division operations
+- Averaging calculations
+- Ratio computations
+- Mathematical formulas requiring division
+- Splitting quantities into equal parts
+
+** Example
+
+Divide two numbers:
+
+#+BEGIN_SRC
+03 0F 00 00 00 ; num 15 - stack: [ 15 ]
+03 03 00 00 00 ; num 3 - stack: [ 15 3 ]
+1B ; / - stack: [ 5 ]
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+15 3 / \ result: 5 (15 / 3)
+#+END_SRC
+
+** Important Notes
+
+- Uses IDIV instruction (signed division)
+- Returns quotient only (remainder discarded)
+- Handles both positive and negative numbers correctly
+- Division by zero causes CPU exception (not handled)
+- Reduces stack depth by 1
+- NOT commutative (a / b ≠ b / a)
+- Order: second item divided by top item
+
+** Common Patterns
+
+*** Average Calculation
+
+#+BEGIN_SRC fifth
+a b + 2 / \ compute average of a and b
+#+END_SRC
+
+*** Split into Parts
+
+#+BEGIN_SRC fifth
+total parts / \ divide total into equal parts
+#+END_SRC
+
+*** Correct Division Order
+
+#+BEGIN_SRC fifth
+dividend divisor swap / \ compute dividend / divisor
+#+END_SRC
+
+** Edge Cases
+
+- Division by zero: CPU exception (crash)
+- =Anything / 1 = anything=
+- =0 / anything = 0=
+- Negative division follows standard signed rules
+
+** WARNING: Division by zero will crash the emulator!
+
+---------------------------------------------------------------------
+
+* 28: > (Greater Than)
+:PROPERTIES:
+:CUSTOM_ID: opcode-gt
+:ID: abcc14d3-6cbf-46c3-8a93-ee7180f350d4
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 28 |
+| Name | > |
+| Stack | ( n1 n2 -- flag ) |
+| Bytecode | Single byte: =1C= |
+
+** Description
+
+Compares the second value (a) with the top value (b). Returns -1 (true)
+if a > b, otherwise returns 0 (false). This is a fundamental comparison
+operation for conditional logic.
+
+** Operation
+
+The operation performs signed 32-bit comparison:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ flag ] (flag = -1 if n1 > n2, 0 otherwise)
+#+END_SRC
+
+** Truth Value Convention
+
+- =-1 = true= (all bits set, non-zero)
+- =0 = false= (zero)
+
+** When to Use
+
+- Conditional branching and logic
+- Sorting algorithms
+- Range checking
+- Decision making in programs
+- Implementing high-level comparison words
+
+** Example
+
+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)
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+3 5 > \ result: 0 (false)
+5 3 > \ result: -1 (true)
+#+END_SRC
+
+** Important Notes
+
+- Returns -1 for true (all bits set), 0 for false
+- Standard Forth truth value convention
+- Signed comparison (works for negative numbers)
+- Reduces stack depth by 1
+- NOT commutative (a > b ≠ b > a)
+
+** Common Patterns
+
+*** Conditional Execution
+
+#+BEGIN_SRC fifth
+a b > if
+ \ code for when a > b
+then
+#+END_SRC
+
+*** Range Check
+
+#+BEGIN_SRC fifth
+value max > abort" Value too large"
+#+END_SRC
+
+*** Sorting Logic
+
+#+BEGIN_SRC fifth
+a b > if swap then \ ensure smaller value is on top
+#+END_SRC
+
+** Edge Cases
+
+- Equal values return 0 (false)
+- Negative numbers compared correctly
+- Works with full 32-bit signed range
+
+---------------------------------------------------------------------
+
+* 29: < (Less Than)
+:PROPERTIES:
+:CUSTOM_ID: opcode-lt
+:ID: 68362f38-c9e4-43f8-809c-8e15725a58c8
+:END:
+
+#+ATTR_HTML: :border 2 :rules all :frame border
+| Property | Value |
+|------------+--------------------|
+| Opcode | 29 |
+| Name | < |
+| Stack | ( n1 n2 -- flag ) |
+| Bytecode | Single byte: =1D= |
+
+** Description
+
+Compares the second value (a) with the top value (b). Returns -1 (true)
+if a < b, otherwise returns 0 (false). This is the complement of the
+greater than operation and essential for conditional logic.
+
+** Operation
+
+The operation performs signed 32-bit comparison:
+
+#+BEGIN_SRC
+Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+After: [ flag ] (flag = -1 if n1 < n2, 0 otherwise)
+#+END_SRC
+
+** Truth Value Convention
+
+- =-1 = true= (all bits set, non-zero)
+- =0 = false= (zero)
+
+** When to Use
+
+- Conditional branching and logic
+- Sorting algorithms
+- Range checking
+- Decision making in programs
+- Implementing high-level comparison words
+- Complementary logic to greater than
+
+** Example
+
+Compare two numbers:
+
+#+BEGIN_SRC
+03 03 00 00 00 ; num 3 - stack: [ 3 ]
+03 05 00 00 00 ; num 5 - stack: [ 3 5 ]
+1D ; < - stack: [ -1 ] (true, 3 < 5)
+#+END_SRC
+
+In Fifth source code:
+
+#+BEGIN_SRC fifth
+3 5 < \ result: -1 (true)
+5 3 < \ result: 0 (false)
+#+END_SRC
+
+** Important Notes
+
+- Returns -1 for true (all bits set), 0 for false
+- Standard Forth truth value convention
+- Signed comparison (works for negative numbers)
+- Reduces stack depth by 1
+- NOT commutative (a < b ≠ b < a)
+- Complementary to > (greater than) operation
+
+** Common Patterns
+
+*** Conditional Execution
+
+#+BEGIN_SRC fifth
+a b < if
+ \ code for when a < b
+then
+#+END_SRC
+
+*** Range Check
+
+#+BEGIN_SRC fifth
+value min < abort" Value too small"
+#+END_SRC
+
+*** Sorting Logic
+
+#+BEGIN_SRC fifth
+a b < if swap then \ ensure larger value is on top
+#+END_SRC
+
+*** Equality Check
+
+#+BEGIN_SRC fifth
+a b < if
+ \ a < b
+else
+ a b > if
+ \ a > b
+ else
+ \ a = b
+ then
+then
+#+END_SRC
+
+** Edge Cases
+
+- 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
+
+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
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine opcodes 30 -- 39
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+
+* 30: not
+:PROPERTIES:
+:CUSTOM_ID: opcode-not
+:ID: 21994321-a1c6-42f6-9682-d5a3fbabb88a
+:END:
+- *Stack Effect:* =n -- ~n=
+- *Description:* Performs a bitwise NOT operation on the top value of
+ the data stack.
+- *Notes:* Inverts all bits of the dword at the top of the stack.
+- *Example:*
+ #+begin_example
+ 0x0000FFFF
+ not
+ #+end_example
+ After execution, stack contains 0xFFFF0000.
+
+* 31: i
+:PROPERTIES:
+:CUSTOM_ID: opcode-i
+:ID: 61fbdd36-4384-44a6-b162-b4e998c4c191
+:END:
+- *Stack Effect:* =-- r=
+- *Description:* Pushes the top value from the return stack onto the
+ data stack.
+- *Notes:* Commonly used to access loop counters or subroutine
+ parameters.
+- *Example:*
+ #+begin_example
+ 42
+ push
+ i
+ #+end_example
+ After execution, data stack contains 42.
+
+* 32: cprt@
+:PROPERTIES:
+:CUSTOM_ID: opcode-cprt-read
+:ID: 78c1ac50-9c26-4132-8e02-8311331f2a1c
+:END:
+- *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.
+- *Example:*
+ #+begin_example
+ 0x60 ; Keyboard port
+ cprt@
+ #+end_example
+
+* 33: cprt!
+:PROPERTIES:
+:CUSTOM_ID: opcode-cprt-write
+:ID: cfa8550e-bf8b-4d44-a3d1-9ba8ad183147
+:END:
+- *Stack Effect:* =byte port --=
+- *Description:* Writes a byte to the specified hardware I/O port.
+- *Notes:* Uses OUT instruction to write to the port. Consumes both the
+ byte and port address.
+- *Example:*
+ #+begin_example
+ 0x42 ; Byte to write
+ 0x60 ; Keyboard port
+ cprt!
+ #+end_example
+
+* 34: i2
+:PROPERTIES:
+:CUSTOM_ID: opcode-i2
+:ID: c3758859-c82e-40c1-9e92-87e5ef410cd2
+:END:
+- *Stack Effect:* =-- r2=
+- *Description:* Pushes the second value from the return stack onto the
+ data stack.
+- *Notes:* Useful for accessing deeper items in the return stack without
+ popping.
+- *Example:*
+ #+begin_example
+ 1 2
+ push push
+ i2
+ #+end_example
+ After execution, data stack contains 1.
+
+* 35: i3
+:PROPERTIES:
+:CUSTOM_ID: opcode-i3
+:ID: 304cadf7-9725-4e2d-8634-2e67b5ad49f3
+:END:
+- *Stack Effect:* =-- r3=
+- *Description:* Pushes the third value from the return stack onto the
+ data stack.
+- *Notes:* Allows access to the third item in the return stack for
+ deeper subroutine contexts.
+- *Example:*
+ #+begin_example
+ 1 2 3
+ push push push
+ i3
+ #+end_example
+ After execution, data stack contains 1.
+
+* 36: shl
+:PROPERTIES:
+:CUSTOM_ID: opcode-shl
+:ID: 978d1b75-3a1d-476f-8cdb-d3a6ee40b3f5
+:END:
+
+- *Stack Effect:* =value count -- result=
+- *Description:* Shifts =value= left by =count= bit positions. Each bit
+ position shifted left multiplies the value by 2.
+- *Bits Shifted Out:* Bits that are shifted beyond the most
+ significant bit (bit 31) are **discarded**.
+- *Count Masking:* Only the low byte of =count= is used. On x86
+ processors, the actual shift amount is further masked to 5 bits
+ (=count & 0x1F=), meaning the effective shift is always 0-31.
+
+
+*Corner Cases:*
+- =count = 0=: Value unchanged.
+- =count >= 32=: Due to 5-bit masking, the effective shift is
+ =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.
+
+*Examples:*
+
+#+begin_example
+1 \ Value to shift
+2 \ Shift amount (count)
+shl
+#+end_example
+Result: 4 (0b0001 << 2 = 0b0100).
+
+#+begin_example
+FF \ Value: 255 (0x000000FF)
+4 \ Shift by 4
+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:
+:CUSTOM_ID: opcode-shr
+:ID: 9dbfcd07-6c34-41b7-b5dc-a33f0773e7cd
+:END:
+
+- *Stack Effect:* =value count -- result=
+- *Description:* Shifts =value= right by =count= bit positions. Each bit
+ position shifted right divides the value by 2 (integer division).
+- *Bits Shifted Out:* Bits that are shifted beyond the least significant
+ bit (bit 0) are **discarded**.
+- *Zero Fill:* This is a **logical shift right** (unsigned). Zeros are
+ shifted in from the left (MSB side). This differs from an arithmetic
+ shift right which would preserve the sign bit.
+- *Count Masking:* Only the low byte of =count= is used. On x86
+ processors, the actual shift amount is further masked to 5 bits
+ (=count & 0x1F=), meaning the effective shift is always 0-31.
+
+*Corner Cases:*
+- =count = 0=: Value unchanged.
+- =count >= 32=: Due to 5-bit masking, the effective shift is
+ =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).
+
+*Examples:*
+#+begin_example
+8 \ Value to shift
+2 \ Shift amount (count)
+shr
+#+end_example
+Result: 2 (0b1000 >> 2 = 0b0010).
+
+#+begin_example
+1 \ Value: only bit 0 set
+1 \ Shift by 1
+shr
+#+end_example
+Result: 0. The single bit was shifted out and discarded.
+
+* 38: or
+:PROPERTIES:
+:CUSTOM_ID: opcode-or
+:ID: b38b971f-3a5f-4b16-9536-4193d2c5ae6b
+: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).
+- *Example:*
+ #+begin_example
+ 1 2
+ or
+ #+end_example
+ After execution, stack contains 3 (0b01 | 0b10).
+
+* 39: xor
+:PROPERTIES:
+:CUSTOM_ID: opcode-xor
+:ID: e87a0d6d-89b0-4791-9500-654d60d99318
+: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).
+- *Example:*
+ #+begin_example
+ 1 2
+ xor
+ #+end_example
+ After execution, stack contains 3 (0b01 ^ 0b10).
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine opcodes 40 -- 49
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+
+* 40: vidmap
+:PROPERTIES:
+:CUSTOM_ID: opcode-vidmap
+:ID: fe47ff6d-768f-4b05-9f8c-f52352c106f4
+:END:
+Stack Footprint
+: addr --
+
+Copies memory from the specified address to the video memory (VGA or
+VESA mode). Used for displaying images directly on the screen.
+
+Parameters:
+- *addr* (32-bit): Source memory address containing the image data to
+ copy to video memory.
+
+** Detailed Operation
+:PROPERTIES:
+:CUSTOM_ID: detailed-operation-4
+:END:
+1. *Video Memory Setup*:
+ - Set video memory segment to =0xA000= (VGA standard segment for
+ 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):
+ - 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.
+ - Use =STOSD= to write 32-bit words for efficiency.
+ - The source address is incremented by 4KB after each page.
+4. *Final Cleanup*:
+ - Restore original segments and clean up stack.
+
+** Example
+Copy a 640x480x8 image (307,200 bytes) from memory at =0x10000= to video
+memory:
+#+begin_example
+0x10000
+vidmap
+#+end_example
+
+** 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=.
+
+* 41: mouse@
+:PROPERTIES:
+:CUSTOM_ID: opcode-mouse
+:ID: b4bce653-d38f-45ed-82ec-13ca78c9860b
+:END:
+Stack Footprint:
+: -- x y buttons
+
+*Description:*
+
+Reads the current mouse position and button states from the mouse
+driver. Returns three values on the stack:
+- X-coordinate,
+- Y-coordinate,
+- 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).
+ - *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)
+
+*Example:*
+
+#+begin_example
+mouse@
+#+end_example
+After execution, stack contains buttons (top), y, x (bottom).
+
+* 42: vidput - put image1 into image2, at location x, y
+:PROPERTIES:
+:CUSTOM_ID: opcode-vidput
+:ID: 238e8b03-57b6-424d-bfee-b6bb652cefbc
+:END:
+Stack Footprint:
+: 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
+support - every pixel is copied regardless of value.
+
+*Parameters:*
+
+- *addr1* (32-bit): Address of the source image structure (width,
+ height, data address).
+- *addr2* (32-bit): Address of the destination image structure (width,
+ height, data address).
+- *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
+ starts.
+
+*Image Structure Format:*
+
+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)
+
+*Example:*
+#+begin_example
+0x1000 ; Source image structure
+0x2000 ; Destination image structure
+50 ; x
+50 ; y
+vidput
+#+end_example
+
+* 43: cmove - copy memory array
+:PROPERTIES:
+:CUSTOM_ID: opcode-cmove
+:ID: 79e1916f-4103-42cc-ac10-bb1ee776ed50
+:END:
+
+- *Stack Effect:*
+: addr1 addr2 len --
+
+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.
+- *Example:*
+ #+begin_example
+ 100 ; Length
+ 0x2000 ; Destination address
+ 0x1000 ; Source address
+ cmove
+ #+end_example
+
+* 44: cfill
+:PROPERTIES:
+:CUSTOM_ID: opcode-cfill
+:ID: fecb9438-d9d2-4224-a017-cc87dba9209e
+:END:
+- *Stack Effect:* =byte addr len --=
+- *Description:* Fills =len= bytes of memory starting at =addr= with the
+ specified byte value.
+- *Notes:* Simple byte-by-byte filling operation. Used for initializing
+ memory regions.
+- *Example:*
+ #+begin_example
+ 0xFF ; Fill byte
+ 0x1000 ; Start address
+ 100 ; Length
+ cfill
+ #+end_example
+
+* 45: tvidput - put image with transparency support
+:PROPERTIES:
+:CUSTOM_ID: opcode-tvidput
+:ID: ab45247c-44c3-464d-9e2a-337f483b4616
+:END:
+Stack Footprint:
+: 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* (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).
+- *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
+ starts.
+
+*Image Structure Format:*
+
+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)
+
+*Example:*
+#+begin_example
+0x1000 ; Source image structure
+0x2000 ; Destination image structure
+50 ; x
+50 ; y
+tvidput
+#+end_example
+
+*Notes:*
+
+- Transparency is fixed at value 255 (0xFF).
+- The operation handles clipping automatically to ensure the copy stays
+ within destination bounds.
+- Source and destination image structures must be properly formatted.
+
+* 46: depth
+:PROPERTIES:
+:CUSTOM_ID: opcode-depth
+:ID: 77fa76d3-9cd0-49c1-882c-f30383347352
+:END:
+- *Stack Effect:* =-- depth=
+- *Description:* Pushes the current depth (number of items) on the data
+ stack onto the stack.
+- *Notes:* Calculated by comparing the data stack pointer to the base
+ address of the stack.
+- *Example:*
+ #+begin_example
+ 1 2 3
+ depth
+ #+end_example
+ After execution, stack contains 3 (depth) followed by 1 2 3.
+
+* 47: charput - draw text character
+:PROPERTIES:
+:CUSTOM_ID: opcode-charput
+:ID: 4bb479cf-aae0-4128-9868-f016c286a162
+:END:
+
+Stack Footprint:
+=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* (1 byte): Foreground color value (0-255). Typically a
+ grayscale value where 0 is black and 255 is white.
+- *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.
+- *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
+ starts.
+
+*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
+10 ; x
+charput
+#+end_example
+
+Character data for 'A' (ASCII 65):
+#+begin_example
+0x00, 0x18, 0x24, 0x24, 0x24, 0x3C, 0x24, 0x24
+#+end_example
+
+- Each byte represents a row:
+ - Row 0: =0x00= → all pixels black
+ - Row 1: =0x18= → bits 3-4 set (binary =00011000=)
+ - Row 2: =0x24= → bits 2 and 5 set (binary =00100100=)
+ - Row 3: =0x24= → same as row 2
+ - Row 4: =0x24= → same as row 2
+ - Row 5: =0x3C= → bits 2-5 set (binary =00111100=)
+ - Row 6: =0x24= → same as row 2
+ - Row 7: =0x24= → same as row 2
--- /dev/null
+:PROPERTIES:
+:ID: 9b251eb9-aff6-4025-94bf-25e89e26d54a
+:END:
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Fifth - virtual machine
+#+AUTHOR: Svjatoslav Agejenko
+#+LANGUAGE: en
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+
+* Overview
+
+Fifth is a hobby operating system built around a custom stack-based
+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
+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.
+
+* x86 Unreal Mode Execution
+
+The emulator runs as a standard DOS COM executable and uses x86
+real-mode assembly with a technique called "unreal mode" to access
+extended memory beyond the 640KB conventional DOS limit. In unreal
+mode, the CPU operates in real-mode (compatible with DOS) but with
+segment registers configured to allow access to the full 32-bit
+address space (up to 4GB).
+
+Key aspects:
+- Starts in real mode for DOS compatibility.
+- Switches temporarily to x86 protected mode to initialize segment
+ registers for 4 GB RAM flat address space.
+- Returns to real mode while retaining 4 GB RAM access.
+- Uses XMS (Extended Memory Specification) for large memory allocation.
+
+** Memory Management and XMS
+
+The emulator uses DOS XMS (Extended Memory Specification) to allocate
+large blocks of extended memory:
+
+- **XMS Allocation**: Allocates the largest available block of extended
+ memory using HIMEM.SYS
+- **Memory Locking**: Locks the allocated block to prevent swapping
+- **Address Mapping**: Uses xms_addr variable to store the linear
+ address of allocated memory
+- **Virtual-to-Physical**: All virtual CPU addresses are offset by
+ xms_addr to convert to physical memory addresses
+
+* Two-Stack Architecture
+
+The virtual CPU uses two separate stacks:
+
+- **Data Stack**: Main working stack for operations.
+- **Return Stack (variable resp)**: Stores return addresses for
+ subroutine calls.
+
+* Graphics System
+
+The virtual machine integrates directly with VESA BIOS for graphics
+output. It uses VESA 640x480 8-bit color mode.
+
+Graphics operations are performed through dedicated VM instructions.
+
+* Boot Process
+
+1. DOS loads emulator.com as a COM executable
+2. system_init() allocates XMS memory and sets up A20 gate
+3. Sets VESA graphics mode (640x480, 8-bit color)
+4. Loads first kilobyte of kernel (core.raw) from disk into beginning
+ of virtual RAM.
+5. Jumps to kernel entry point
+6. Kernel loads remaining 3 kibibytes of kernel bytecode into RAM. So
+ initial bytecode part of the kernel must not exceed 4 kibibytes.
+7. Kernel loads high-level Fifth boot code into RAM.
+8. Kernel starts compiling additional modules to itself from
+ high-level bytecode to gain filesystem support, dynamic RAM support
+ and other essential functions.
+9. Additional modules (keyboard / mouse drivers) and interactive shell
+ are now loaded from filesystem files.
+10. Enters interactive REPL
+
+* Implemented instructions
+
+Virtual CPU opcodes (most of them are available as directly executable commands
+in FIFTH programming language):
+
+
+| # | Name | Stack effect | Description | Notes |
+|----+----------+-----------------------+----------------------------------------------------------+---------------------------------------------------------------------|
+| 0 | [[file:opcodes_00_09.org::#opcode-nop][nop]] | -- | No operation; execution continues to next instruction | Used for padding or placeholder |
+| 1 | [[file:opcodes_00_09.org::#opcode-halt][halt]] | -- | Stop execution and exit emulator | Cleanly terminates the Fifth environment |
+| 2 | [[file:opcodes_00_09.org::#opcode-kbd-read][kbd@]] | -- scancode | Read keyboard scancode; returns 0 if no key pending | Make codes for press, break codes (value+128) for release |
+| 3 | [[file:opcodes_00_09.org::#opcode-num][num]] | -- n | Push 32-bit literal from instruction stream | Followed by 4 bytes (little-endian) |
+| 4 | [[file:opcodes_00_09.org::#opcode-jmp][jmp]] | -- | Unconditional jump to address from instruction stream | Followed by 4-byte target address |
+| 5 | [[file:opcodes_00_09.org::#opcode-call][call]] | -- | Call subroutine; push return address on return stack | Use ret (opcode 11) to return; follow with 4-byte target address |
+| 6 | [[file:opcodes_00_09.org::#opcode-1plus][1+]] | n -- n+1 | Increment top of data stack by 1 | Efficient single-byte operation for counters and pointers |
+| 7 | [[file:opcodes_00_09.org::#opcode-1minus][1-]] | n -- n-1 | Decrement top of data stack by 1 | Efficient single-byte operation for countdown loops |
+| 8 | [[file:opcodes_00_09.org::#opcode-dup][dup]] | n -- n n | Duplicate top of data stack | Preserve values before consuming operations |
+| 9 | [[file:opcodes_00_09.org::#opcode-drop][drop]] | n -- | Remove and discard top of data stack | Clean up stack after operations |
+| 10 | [[file:opcodes_10_19.org::#opcode-if][if]] | n -- | Jump to address if top of stack is zero | |
+| 11 | [[file:opcodes_10_19.org::#opcode-ret][ret]] | | Return from subroutine (pop return address) | |
+| 12 | [[file:opcodes_10_19.org::#opcode-cfetch][c@]] | addr -- byte | Read byte from memory at specified address | |
+| 13 | [[file:opcodes_10_19.org::#opcode-cstore][c!]] | byte addr -- | Store byte to specified memory address | |
+| 14 | [[file:opcodes_10_19.org::#opcode-push][push]] | n -- | Move top of data stack to return stack | |
+| 15 | [[file:opcodes_10_19.org::#opcode-pop][pop]] | -- n | Move top of return stack to data stack | |
+| 16 | <unused> | | | |
+| 17 | [[file:opcodes_10_19.org::#opcode-rot][rot]] | n1 n2 n3 -- n2 n3 n1 | Rotate top three stack elements | |
+| 18 | [[file:opcodes_10_19.org::#opcode-disk-read][disk@]] | sector addr -- | Read 1KB from disk into RAM at specified address | |
+| 19 | [[file:opcodes_10_19.org::#opcode-disk-write][disk!]] | addr sector -- | Write 1KB from RAM to disk at specified sector | |
+| 20 | [[file:opcodes_20_29.org::#opcode-fetch][@]] | addr -- n | Read 32-bit value from memory | |
+| 21 | [[file:opcodes_20_29.org::#opcode-store][!]] | n addr -- | Store 32-bit value to memory | |
+| 22 | [[file:opcodes_20_29.org::#opcode-over][over]] | n1 n2 -- n1 n2 n1 | Duplicate second item on data stack | |
+| 23 | [[file:opcodes_20_29.org::#opcode-swap][swap]] | n1 n2 -- n2 n1 | Swap top two items on data stack | |
+| 24 | [[file:opcodes_20_29.org::#opcode-add][+]] | n1 n2 -- n1+n2 | Add top two items on data stack | |
+| 25 | [[file:opcodes_20_29.org::#opcode-sub][-]] | n1 n2 -- n1-n2 | Subtract second item from top item on data stack | TODO: verify argument order |
+| 26 | [[file:opcodes_20_29.org::#opcode-mul][*]] | n1 n2 -- n1*n2 | Multiply top two items on data stack | |
+| 27 | [[file:opcodes_20_29.org::#opcode-div][/]] | n1 n2 -- n2/n1 | Divide top item by second item on data stack | TODO: verify argument order |
+| 28 | [[file:opcodes_20_29.org::#opcode-gt][>]] | n1 n2 -- result | Compare n1 > n2 (returns true, false otherwise) | TODO: document, what true and false means |
+| 29 | [[file:opcodes_20_29.org::#opcode-lt][<]] | n1 n2 -- result | Compare n1 < n2 (returns true, false otherwise) | TODO: document, what true and false means |
+| 30 | [[file:opcodes_30_39.org::#opcode-not][not]] | n -- ~n | Bitwise NOT on top of data stack | |
+| 31 | [[file:opcodes_30_39.org::#opcode-i][i]] | -- n | Push top of return stack to data stack | |
+| 32 | [[file:opcodes_30_39.org::#opcode-cprt-read][cprt@]] | port -- byte | Read byte from hardware I/O port | |
+| 33 | [[file:opcodes_30_39.org::#opcode-cprt-write][cprt!]] | byte port -- | Write byte to hardware I/O port | |
+| 34 | [[file:opcodes_30_39.org::#opcode-i2][i2]] | -- n | Push second item from return stack to data stack | |
+| 35 | [[file:opcodes_30_39.org::#opcode-i3][i3]] | -- n | Push third item from return stack to data stack | |
+| 36 | [[file:opcodes_30_39.org::#opcode-shl][shl]] | value count -- result | Left shift value by count bits; shifted-out bits discarded |
+| 37 | [[file:opcodes_30_39.org::#opcode-shr][shr]] | value count -- result | Logical right shift; zeros shifted in, shifted-out bits discarded |
+| 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 | |
+| 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 | |
+| 45 | [[file:opcodes_40_49.org::#opcode-tvidput][tvidput]] | addr1 addr2 x y -- | Blit image1 to image2 at (x,y) with transparency support | |
+| 46 | [[file:opcodes_40_49.org::#opcode-depth][depth]] | -- depth | Push current data stack depth | |
+| 47 | [[file:opcodes_40_49.org::#opcode-charput][charput]] | fg bg src dest x y -- | Draw character from source buffer to destination buffer |
+
+
+
cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -- "$0" T; exit; fi;
cd ..
-# Function to export org to html using emacs in batch mode
-export_org_to_html() {
- local org_file=$1
- local dir=$(dirname "$org_file")
- local base=$(basename "$org_file" .org)
- (
- cd "$dir" || return 1
- local html_file="${base}.html"
- if [ -f "$html_file" ]; then
- rm -f "$html_file"
- fi
- echo "Exporting: $org_file → $dir/$html_file"
- emacs --batch -l ~/.emacs --visit="${base}.org" --funcall=org-html-export-to-html --kill
- if [ $? -eq 0 ]; then
- echo "✓ Successfully exported $org_file"
- else
- echo "✗ Failed to export $org_file"
- return 1
- fi
- )
-}
+# Export changed Documentation/*.org pages to html via the shared incremental
+# exporter (requires svjatoslav_commons checked out next to this repo).
+"../svjatoslav_commons/Tools/Org export incremental" \
+ --root Documentation --pattern '*.org' \
+ --state .org-export-state
-echo "🔍 Searching for .org files in doc/ ..."
-echo "======================================="
+# Upload assembled documentation to server
+SERVER_DIR="n0@www3.svjatoslav.eu:/mnt/big/projects/fifth/"
-mapfile -t ORG_FILES < <(find doc -type f -name "*.org" | sort)
-
-if [ ${#ORG_FILES[@]} -eq 0 ]; then
- echo "❌ No .org files found!"
- echo ""
- echo "Press ENTER to close this window."
- read
- exit 1
-fi
-
-echo "Found ${#ORG_FILES[@]} .org file(s):"
-printf '%s\n' "${ORG_FILES[@]}"
-echo "======================================="
-
-SUCCESS_COUNT=0
-FAILED_COUNT=0
-
-for org_file in "${ORG_FILES[@]}"; do
- export_org_to_html "$org_file"
- if [ $? -eq 0 ]; then
- ((SUCCESS_COUNT++))
- else
- ((FAILED_COUNT++))
- fi
-done
-
-echo "======================================="
-echo "📊 SUMMARY:"
-echo " ✓ Successful: $SUCCESS_COUNT"
-echo " ✗ Failed: $FAILED_COUNT"
-echo " Total: $((SUCCESS_COUNT + FAILED_COUNT))"
-echo ""
-
-echo "📤 Uploading to server..."
-rsync -avz --delete -e 'ssh -p 10006' doc/ n0@www3.svjatoslav.eu:/mnt/big/projects/fifth/
+echo "📤 Uploading to $SERVER_DIR ..."
+rsync -avz --delete -e 'ssh -p 10006' Documentation/ "$SERVER_DIR"
if [ $? -eq 0 ]; then
echo "✓ Upload completed successfully!"
+++ /dev/null
-.( loading editor ) update\r
-.( done )\r
-.( ETh - for text editor help )\r
-\r
-var handletxt\r
-var handlebuf\r
-Dstralloc const editfile\r
-var shiftx var shifty\r
-var curlocx var curlocy\r
-Dstr \listF\TXT_ET" tmphelpfile\r
-\r
-: disppage ( -- )\r
-handletxt @\r
-dynp shifty @ 50 * +\r
-0 1 at!\r
-1270 type ;\r
-\r
-: fclose ( close opened file )\r
-handletxt @ dup if dynde handletxt off else drop then ;\r
-\r
-: ETnewpage fclose 27100 dynal dup handletxt ! dynp\r
-FF swap 27100 cfill ;\r
-\r
-var tmp1\r
-: fopen ( strhand -- )\r
-fclose\r
-dup editfile Dstr2Dstr fsDloadnew dup push dynp ( addrinp R: handinp )\r
-27100 dynal dup handletxt ! dynp ( addrinp addrout R: handinp )\r
-FF over 27100 cfill\r
-tmp1 off\r
-i dyns do ( addrinp addrout )\r
-over c@ ( addrinp addrout c )\r
-dup FE = if\r
- drop\r
- 50 tmp1 @ - + tmp1 off\r
-else\r
- dup FD = if\r
- drop tmp1 @ tab ( addrinp addrout numspaces )\r
- tmp1 @ over + tmp1 ! + 0\r
- else\r
- over c! 1+ 0\r
- tmp1 @ 1+ tmp1 !\r
- then\r
- drop\r
-then\r
-swap 1+ swap\r
-loop 2drop\r
-pop dynde ; rh tmp1\r
-\r
-: dispchar ( x y -- )\r
-2dup\r
-swap shiftx @ -\r
-swap shifty @ - 1+\r
-at!\r
-50 * + handletxt @ dynp + c@ emit ;\r
-\r
-: dispcur curlocx @ curlocy @ dispchar ;\r
-\r
-: displine ( line )\r
-0 over shifty @ - 1+ at!\r
-50 * handletxt @ dynp + shiftx @ + 50 type ;\r
-\r
-: dispstatbar\r
-colneg\r
-0 0 at! 50 do space loop\r
-0 0 at! ." col:" curlocx @ .\r
-7 0 at! ." line:" curlocy @ .\r
-11 0 at! ." ascii:" curlocy @ 50 * curlocx @ + handletxt @ dynp + c@ .\r
-1B 0 at! ." file:" editfile Dstr.\r
-49 0 at! ." F1-help"\r
-colnorm ;\r
-\r
-: charins ( char x y )\r
-50 * handletxt @ dynp + ( char x addr )\r
-over push\r
-+ dup dup 1+ 4F pop - cmove ( char addr )\r
-c! ;\r
-\r
-: charcut ( x y -- char )\r
-over push\r
-50 * handletxt @ dynp + +\r
-dup c@ swap ( char addr )\r
-dup dup 1+ swap ( char addr addr+1 addr )\r
-4F pop - dup push cmove ( char addr )\r
-pop + FF swap c! ;\r
-\r
-: lineins ( line )\r
-50 * dup push handletxt @ dynp + ( addr )\r
-dup dup 50 + handletxt @ dyns 50 - pop - cmove ( addr )\r
-FF swap 50 cfill ;\r
-\r
-: linecut ( line )\r
-50 * dup push handletxt @ dynp + ( addr )\r
-dup 50 + swap handletxt @ dyns 50 - pop - cmove ;\r
-\r
-: screenaim\r
-0 curlocy @ 1E - 794 bound shifty !\r
-disppage ;\r
-\r
-var tmp1\r
-: length ( line -- length )\r
-50 * 4F + handletxt @ dynp + ( addr )\r
-51 do\r
- i tmp1 !\r
- dup c@ FF - if pop drop 0 push then\r
- 1-\r
-loop\r
-drop\r
-tmp1 @ ; rh tmp1\r
-\r
-var tmp2 var tmp3\r
-: fsavechar ( char ) tmp2 @ swap over c! 1+ tmp2 ! ;\r
-: fsave ( -- )\r
-27200 dynal dup dynp tmp2 !\r
-0\r
-7CF do\r
- i length if drop pop 1+ 0 push then\r
-loop\r
-0 swap for ( desthand )\r
- tmp3 off\r
- i 50 * handletxt @ dynp +\r
- 0 i length for ( desthand srcaddr )\r
- dup c@\r
- i tab 8 = if tmp3 @ if FD fsavechar tmp3 off then then\r
- dup FF = if\r
- drop tmp3 @ 1+ tmp3 !\r
- else\r
- tmp3 @ do FF fsavechar loop tmp3 off\r
- fsavechar\r
- then\r
- 1+\r
- loop drop\r
- FE fsavechar\r
-loop\r
-tmp2 @ over dynp - over dynresize\r
-dup editfile fsDsave\r
-dynde ; rh tmp2 rh tmp3\r
-\r
-str TXT_default" tmpdefault\r
-\r
-: ET\r
-handletxt @ if\r
-disppage\r
-until\r
- colneg dispcur dispstatbar KBD_FW@ colnorm dispcur\r
-\r
- dup 415 = if ( DEL )\r
- curlocx @ curlocy @ charcut drop\r
- curlocy @ displine\r
- then\r
-\r
- dup FC = if ( BACKSPACE )\r
- curlocx @ if\r
- curlocx @ 1- curlocy @ charcut drop\r
- curlocy @ displine\r
- drop 413\r
- else\r
- curlocy @ if\r
- curlocy @ 1- dup length dup curlocx ! ( y-1 strlen )\r
- swap 50 * handletxt @ dynp + ( strlen y1addr )\r
- 2dup + swap 50 + ( strlen dest src )\r
- swap rot 50 swap - cmove\r
- curlocy @ linecut\r
- drop 410\r
- disppage\r
- then\r
- then\r
- then\r
-\r
- dup FD = if ( TAB )\r
- curlocx @ tab\r
- dup do FF curlocx @ curlocy @ charins loop\r
- curlocx @ + curlocx !\r
- curlocy @ displine\r
- then\r
-\r
- dup FE = if ( ENTER )\r
- curlocy @ 7CF < if\r
- curlocy @ 1+ lineins\r
- curlocx @ 0\r
- 50 curlocx @ - do\r
- over curlocy @ charcut\r
- over curlocy @ 1+ charins\r
- 1+\r
- loop 2drop\r
- curlocx off\r
- drop 412\r
- disppage\r
- then\r
- then\r
-\r
- dup 418 = if curlocy @ 3A - curlocy ! then ( PG-UP )\r
- dup 419 = if curlocy @ 3A + curlocy ! then ( PG-DOWN )\r
- dup 416 = if curlocx off then ( HOME )\r
- dup 417 = if curlocy @ length curlocx ! then ( END )\r
- dup 401 = if tmphelpfile fsdisp disppage then ( F1 )\r
- dup 402 = if fsave then ( F2 )\r
- dup 403 = if fclose drop 1B then ( F3 )\r
- dup 400 = if done 0 0 at! screen cls drop -1 then ( ESC )\r
-\r
- 0 over F0 bound? if -1 else 0 then ( NORMAL KEY )\r
- over FF = if drop -1 then\r
- if\r
- curlocx @ curlocy @ charins\r
- curlocy @ displine\r
- 411\r
- then\r
-\r
- dup 411 = if curlocx @ 1+ curlocx ! then ( RIGHT )\r
- dup 413 = if curlocx @ 1- curlocx ! then ( LEFT )\r
- dup 412 = if curlocy @ 1+ curlocy ! then ( DOWN )\r
- dup 410 = if curlocy @ 1- curlocy ! then ( UP )\r
- drop\r
-\r
- 0 curlocx @ 4F bound curlocx !\r
- 0 curlocy @ 7CF bound curlocy !\r
-\r
- curlocy @ shifty @ - 3B > if screenaim then\r
-\r
- curlocy @ shifty @ - 3B - 0 = if\r
- 0 0 8 screen scrollf\r
- shifty @ 1+ shifty !\r
- curlocy @ displine\r
- then\r
-\r
- curlocy @ shifty @ - -1 < if screenaim then\r
-\r
- curlocy @ shifty @ - 1+ 0 = if\r
- 0 0 -8 screen scrollf\r
- shifty @ 1- shifty !\r
- curlocy @ displine\r
- then\r
-\r
-loop\r
-else\r
- tmpdefault editfile str2Dstr\r
- ETnewpage\r
- ET\r
-then ;\r
-\r
-: ETl\r
-fopen\r
-curlocx off\r
-curlocy off\r
-shiftx off\r
-shifty off\r
-ET ;\r
-\r
-: ETs\r
-editfile Dstr2Dstr\r
-fsave ." saved " ;\r
-\r
-: ETh tmphelpfile fs. ;\r
-\r
-rh handletxt\r
-rh handlebuf\r
-rh editfile\r
-rh shiftx rh shifty\r
-rh curlocx rh curlocy\r
-rh tmphelpfile\r
-rh disppage\r
-rh fclose rh fopen\r
-rh dispchar rh dispcur\r
-rh displine rh dispstatbar\r
-rh charins rh charcut\r
-rh lineins rh linecut\r
-rh screenaim rh length\r
-rh fsavechar rh fsave\r
-rh ETnewpage rh tmpdefault\r
+++ /dev/null
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine, operating system, programming language
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-
-* General
-- This program is free software: released under Creative Commons Zero
- (CC0) license
-
-- Program author:
- - Svjatoslav Agejenko
- - Homepage: https://svjatoslav.eu
- - Email: mailto://svjatoslav@svjatoslav.eu
-
-- [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]]
-
-** Source code
-- [[https://www2.svjatoslav.eu/gitweb/?p=fifth.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]]
-
-- [[https://www2.svjatoslav.eu/gitweb/?p=fifth.git;a=summary][Browse Git repository online]]
-
-- Clone Git repository using command:
- : git clone https://www3.svjatoslav.eu/git/fifth.git
-
-* Introduction
-
-Fifth is self-hosting hobby computing environment that integrates a
-[[id:9b251eb9-aff6-4025-94bf-25e89e26d54a][custom virtual machine]], a [[id:da7fff9b-0b67-4843-828a-52a404d7f401][Forth-inspired programming language]], and
-operating system.
-
-At its core lies a stack-based virtual CPU with two stacks (data and
-return), approximately 50 instructions, and a 4GB flat address
-space. This virtual machine executes the Fifth language — a concise,
-interactive, and extensible environment where every operation is a
-command that can be defined, redefined, or extended by the user. The
-language uses a custom character encoding (FSCII) with unique
-representations for control characters (e.g., space as FF instead of
-20 in ASCII) and features an innovative hexadecimal number system
-visualized as squares split into four triangles, each representing a
-single bit—similar to Bibi-binary.
-
-It includes a disk-based filesystem, a built-in text editor, 2D
-graphics capabilities, mouse and keyboard drivers, and tools for file
-management and memory allocation.
-
-Released under the CC0 public domain license, Fifth is free for anyone
-to use, study, and modify. Whether you're a retro computing
-enthusiast, a systems programming student, or a curious developer.
-
-** Screenshots
-
-#+attr_html: :class responsive-img
-#+attr_latex: :width 1000px
-[[file:screenshots/start.png]]
-
-Startup screen diplaying Fifth logo and full file list.
-
-#+attr_html: :class responsive-img
-#+attr_latex: :width 1000px
-[[file:screenshots/dictionary.png]]
-
-Sample words defined. Most of the words are commands that can be
-executed interactively from the command line or from a file. When
-executed, they can be selectively compiled or interpreted.
-
-#+attr_html: :class responsive-img
-#+attr_latex: :width 1000px
-[[file:screenshots/text editor.png]]
-
-Built in text editor.
-
-* Installation
-Just unpack all files, witout altering original directory structure,
-somewhere in your hard disk. For example:
-
-: C:\MISC\FIFTH\
-
-To run fifth you need minimally just 2 files:
-- EMULATOR.COM :: Virtual CPU emulator
-- DISK.RAW :: Virtual disk file
-
-For more information, please refer to [[id:0759f3e0-28bb-4901-9e4f-09ef41732173][Fifth distribution directory
-tree description]].
-
-* Fifth distribution directory tree description
-:PROPERTIES:
-:CUSTOM_ID: ID-0759f3e0-28bb-4901-9e4f-09ef41732173
-:ID: 0759f3e0-28bb-4901-9e4f-09ef41732173
-:END:
-After downloading and unpacking the ZIP file you shoud get directory
-tree similar to this:
-
-#+BEGIN_VERSE
-*DOC* - Fifth documentation
- *commands* - documentation on Fifth built-in commands
- *modules* - documentation on additional commands, realized as loadable modules
- *shots* - Fifth screenshots
-
-*imageFile* - files contained within 'disk.raw', just an extracted form.
-
-*source* - source files
- *emulator* - emulator source
- *util* - utilites
-
-*disk.raw* - Virtual disk file, has filesystem inside.
-*emulator.com* - main executable.
-#+END_VERSE
-
-* Requirements
-** Software
-- MS-DOS 6.22 with HIMEM.SYS loaded.
-- Mouse driver (optional, if you have a mouse).
-- CPU is initialized into [[https://en.wikipedia.org/wiki/Unreal_mode][Unreal Mode]] during operation.
-- To recompile ASM sources, you can use FASM (Flat Assembler).
-- To run Quick Basic utilities, use Microsoft Quick Basic 4.5.
-- VESA support through BIOS or external driver (UNIVBE).
-
-** Hardware
-- A minimum of a i386 CPU.
-- 64 KB of free RAM below 640KB.
-- 2 MB of free [[https://en.wikipedia.org/wiki/Extended_memory][extended memory]].
-- A VESA-compatible video card.
-
-* Numbers representation within Fifth
-
-Because we are in full experimentation mode here (no regard for
-compatibility whatsoever), why not to try also alternative number
-representation ?
-
-Here alternative hexadecimal number representation format is devised:
-
-#+attr_html: :class responsive-img
-#+attr_latex: :width 1000px
-[[file:numbers.png][file:numbers.png]]
-
-Essentially square is split into 4 triangles. Each triangle represents
-one bit.
-
-Fifth uses this hexadecimal format as primary throughout entire
-system.
-
-See also: [[https://en.wikipedia.org/wiki/Bibi-binary][Bibi-binary]].
-
-* Disk file map and its data structures
-
-The `disk.raw` file is a raw disk image that contains both the boot
-code (loaded before filesystem initialization) and the filesystem
-structure. The disk layout is as follows:
-
-** Disk Allocation Table
-| Offset | Length | Description |
-|---------+---------+----------------------------------------------------------------------|
-| 0 | ~4 KiB | Operating system kernel in virtual CPU byte-code |
-| 4 KiB | ~32 KiB | Human readable, early system initialization code |
-| 37 KiB | ~65 KiB | FAT (File Allocation Table) |
-| 101 KiB | ~16 MiB | Filesystem data area (stores file contents and directory structures) |
-
-** FAT Entry Format
-
-The FAT tracks which disk sectors belong to each file.
-
-| Code | Meaning |
-|----------+----------------------------------------------|
-| -2 | Last sector of a file chain |
-| -1 | Unused (empty) sector |
-| 0 -- ... | Pointer to the next sector in the file chain |
-
-** File Directory Entry Format
-| Offset | Length | Description |
-|--------+--------+-----------------------------------------------------|
-| 0 | 4 | File extension (4-byte FSCII string, e.g., "TXT ") |
-| 4 | 16 | Filename (16-byte FSCII string, padded with spaces) |
-| 20 | 4 | First block address |
-| 24 | 4 | File size in bytes |
-| 28 | 4 | Last modification timestamp (seconds since epoch) |
-
-The directory entries are stored within the filesystem data
-area. Directory is just a file that list other files.
-
-* Core architecture
-
-Fifth core is simply some amount of already compiled into machine code
-and linked together modules (entries in other words). In compilation
-process modules is compiled one by one and simply stored on top of
-already existing and growing core. Separately from core is kept
-dictionary, this is special list that contain names of compiled
-modules, variables etc. and they locations in core. Constants use
-dictionary space only. Random word can be removed from dictionary at
-any time. Currently dictionary can contain at most 1000 entries.
-
-** Dictionary entry format
-| offset | length | description |
-|--------+--------+-----------------------|
-| 0 | 4 | 0 < previous entry |
-| | | 0 = last |
-| | | -1 = empty |
-|--------+--------+-----------------------|
-| 4 | 15 | module name string |
-|--------+--------+-----------------------|
-| 19 | 1 | entry type |
-|--------+--------+-----------------------|
-| 20 | 4 | entry data |
-
-Core headers as linked list of module names make up something like
-dictionary. When some entry address is needed compiler can quickly
-run through headers backwards and find needed entry.
-
-** Possible module types
-
-| type | description | "execute" action |
-|------+----------------+----------------------------|
-| 0 | data | compile "num" instruction |
-| | | with address to module |
-|------+----------------+----------------------------|
-| 1 | submodule | compile "call" instruction |
-| | | with address to module |
-|------+----------------+----------------------------|
-| 2 | imm. submodule | immediately call to module |
-
-** Memory map
-
-| location | size | description |
-|----------+--------+-----------------------------|
-| 0 | ~4096 | core |
-| 1500000 | ~32000 | highlevel Fifth boot code |
-| 200000h | | core startup messages area |
-| 5200000 | | end of dynamic memory space |
-
-* Dynamically loadable modules
-** Keyboard driver
-#+BEGIN_VERSE
-
-KBD_@ ( -- code ) get scancodes for pressed keys from keyboard.
-KBD_down? ( key -- result ) check is key with specified scancode
- currently pressed down.
-KBD_SC2FSCII ( code -- FSCII ) convert key scancode into FSCII code,
- or in FSK (Fifth standard keycode).
-KBD_F@ ( -- FSCII ) read pressed key FSCII or FSK, returns -1 if no
- keys are pressed.
-KBD_FW@ ( -- FSCII ) read pressed key FSCII or FSK, if no keys is
- are pressed then waits until there is.
-
- FSK
- ---
-In HEX.
-
-FC backspace
-FD TAB
-FE enter
-FF space
-
-400 ESC
-401 ... F1 ...
-410 up
-411 right
-412 down
-413 left
-414 INS
-415 DEL
-416 home
-417 end
-418 PG/UP
-419 PG/DN
-#+END_VERSE
-** Mouse driver
-#+BEGIN_VERSE
-mousex var Mouse x coordinate.
-mousey var Mouse y coordinate.
-mousekeyl var Mouse left key.
-mousekeym var Mouse middle key.
-mousekeyr var Mouse right key.
-mousec var Display current mouse coordinates in top left part of screen,
- if true. (good for debugging)
-mousepointer var Image buffer, holding current mouse pointer.
-mouseadd ( ModuleAddr x1 x2 y1 y2 -- ) Add specified area on screen,
- into mause click buffer. If any mouse button is clicked on
- that area, module at "ModuleAddr" will be executed.
-mousebe var Amount of buffer elements.
-mousedo ( -- ) Updates mouse coordinates and keys. Parse mouse
- click buffer, and draw mouse cursor to "screen".
-#+END_VERSE
-** 2D graphic library
-
-+ lineh ( color len x y imgbuf -- ) :: draws horisontal line from X,Y
- coordinates to right, with specified length.
-
-+ linev ( color len x y imgbuf -- ) :: draws vertical line down, from
- coordinates X,Y, with specified length.
-
-+ box ( color x2 x1 y2 y1 imgbuf -- ) :: draws rectangular box. x2
- bust be >= x1, y2 must be >= y1.
-
- #+begin_example
- x1,y1-----------+
- | |
- | |
- +-----------x2,y2
- #+end_example
-
-+ flipv ( imgbuf -- ) :: flip image vertically.
-
-+ imgcoltrans ( ImgBuf Color ToColor -- ) :: Translate all pixels in
- specified image with "Color" into "ToColor".
-
-+ imgfill ( color x y imgbuf -- ) :: Fill image region starting at
- location X & Y with specified color.
-
-** Trigonometry functions
-
-
-*** sin ( a -- result )
-:PROPERTIES:
-:CUSTOM_ID: ID-9a66ca9c-eb5f-45aa-8116-71763081f2fb
-:ID: 9a66ca9c-eb5f-45aa-8116-71763081f2fb
-:END:
-
-Return sine of angle "a", where the:
-
-- Input is scaled such that: 360 degrees = 2000 units (i.e., 1 unit =
- 0.18 degrees).
-
-- The output is scaled to a 16-bit integer range of -10000 to 10000,
- where ±10000 represents ±1.0 in floating-point terms.
-
-This fixed-point representation allows efficient computation using
-precomputed lookup tables, avoiding floating-point operations which
-are not supported by the virtual CPU's integer-only instruction
-set. The lookup table provides fast, sine values within the system's
-precision.
-
-*** cos ( a -- result )
-
-Return cosine of angle "a", using the same scaling and fixed-point
-representation as the [[id:9a66ca9c-eb5f-45aa-8116-71763081f2fb][sin]] function. Input angles are scaled (360° =
-2000 units), and outputs range from -10000 to 10000. Precomputed
-values in a lookup table ensure high performance for trigonometric
-calculations.
+++ /dev/null
-:PROPERTIES:
-:ID: da7fff9b-0b67-4843-828a-52a404d7f401
-:END:
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - language
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-
-- [[file:5TH_ET.txt][Example Fifth source file - text editor]]
-
-* Fifth source format
-
-Fifth uses a different character table and codes than ASCII (still
-almost similar). I call it FSCII (Fifth Standard Code for Information
-Interchange) for example space character is not 32 but 255 instead. I
-plan to use mainly HEX numbers, and create new characters to represent
-numeric values. So typical nemric characters "0123..." is treated
-like ordinary letters.
-
-** FSCII
-
-| DEC | HEX | function |
-|--------+-------+----------------------------------------|
-| 0 - 15 | 0 - F | HEX numbers |
-| 252 | FC | backspace |
-| 253 | FD | tabulator (TAB) |
-| 254 | FE | carriage return (CR) |
-| 255 | FF | space |
-| else | | ordinary characters, same as in ASCII. |
-
-* Fifth commands
-** Compilation & miscellaneous
-*** init ( -- )
-
-- Description :: First module, control is passed to on startup. Contains
- initialization routines. Also it is the last core module. All new
- modules on top of it comes as result of executing external source
- files.
-
-*** head <name> ( -- )
-
-- Description :: Compiles new dictionary entry without specifying new
- module type.
-- Example ::
- : head myentry
-
-*** : <name> ( -- )
-
-- Description :: Creates new code module.
-
-*** ; ( -- )
-
-- Description :: Ends module (immideate).
-- Example ::
- : : hello ." hi there" ;
-
-*** const <name> ( n -- )
-
-- Description :: Defines new constant.
-- Example ::
- : 2147483647 const max
-
-*** :i <name> ( -- )
-
-- Description :: Same as =:= but this module will be executed
- immideately even in compile mode.
-- Example ::
- : :i ( 41 scan ;
-
-*** create <name> ( -- )
-
-- Description :: Same as =head=, but specify module type as data.
-- Example ::
- : create LotoResults 5 , 13 , 52 , 12 , 11 , 3 ,
-
-*** allot ( n -- )
-
-- Description :: Allocate *n* bytes in dictionary.
-- Example ::
- : create MyArray 100 allot
-
-*** " <string>" ( -- )
-
-- Description :: Compile string and its size into core.
-- Example ::
- : create Mystring " This is it's contects"
-
-*** str <name> <string>" ( -- )
-
-- Description :: Just shorter way for defining strings.
-- Example ::
- : str Mystring This is it's contenc"
-
-*** var <name> ( -- )
-
-- Description :: Define new 32 bit variable.
-- Example ::
- : var result
-
-*** ' <module> ( -- n )
-
-- Description :: Return memory address of given entry.
-- Example ::
- : ' init
-
-*** forget <name> ( -- )
-
-- Description :: Erases from RAM given entry and all entries what was
- defined after it.
-- Example ::
- : forget myprog
-
-*** [ ( -- )
-
-- Description :: Set interpret mode (immideate).
-
-*** ] ( n -- )
-
-- Description :: Set compile mode and compile top stack element in as
- literal. Together =[ .... ]= combination provides good way to
- compute some values only once, at compile time, rather than every
- time while program is running.
-- Example ::
- : : calculate - [ 4 MyConst1 + MyConst2 * ] ;
-
-*** defer <name> ( -- )
-
-- Description :: Creates new module, with jump instruction. Later
- address where to jump can be modified by =is= command. This
- provides method of forward referencing. So you can use modules what
- not yet exist.
-
-*** is ( address1 address2 -- )
-
-- Description :: *address1* - where to jump, *address2* - address of
- module created by =defer= command.
-- Example ::
- : defer dispver
- : : run dispver ." running ..." ;
- : ... whatever ...
- : : (dispver ." Version 9.99 " ;
- : ' (dispver ' dispver is
-
- Now if I type "run" on the screen appears:
- : Version 9.99 running ...
-
-*** asc <char> ( -- )
-
-- Description :: Reads char ascii code and treats it as literal
- (immideate).
-- Example ::
- : : BreakLine 30 do asc - emit loop ;
-
- same as:
- : : BreakLine 30 do 45 emit loop ;
-
-*** dyninc ( handle -- )
-
-- Description :: Execute code in dynamic memory handle. Automatically
- deallocates it when done.
-
-*** include ( filenumber -- )
-
-- Description :: Execute code in specified file.
-
-*** words ( -- )
-
-- Description :: Display existing blocks in core.
-
-*** bye ( -- )
-
-- Description :: Exit from Fifth.
-
-*** fkey ( -- c )
-
-- Description :: Read one byte from input stream.
-
-*** sadd ( c addr -- )
-
-- Description :: Add one byte *c* to string located at *addr* and
- updates string length.
-
-*** scan ( c -- )
-
-- Description :: Read input stream and store it to =pad= until it
- finds *c*. It ignores all *c* bytes until it finds any non *c*
- byte. Is useful for breaking text lines into words.
-- Example ::
- : c is: "
- : input stream: """"This is test !"aoeu idh
- : result: This is test !
-
-*** skey ( -- c )
-
-- Description :: So called safe =fkey=. Reads data from input stream
- but converts characters with ASCII codes: 9 13 10 to spaces.
-
-*** str=str? ( adr1 adr2 -- result )
-
-- Description :: Compares string at *adr1* with string at *adr2*,
- returns true flag if they are equal or false if they are not.
- true = -1, false = 0.
-
-*** find ( -- addr )
-
-- Description :: Searches whole dictionary for word in =pad=. If
- found, returns its address, if not, returns 0.
-
-*** execute ( -- )
-
-- Description :: Execute word located in =pad=. Depending on =mode=.
-
-*** dta ( addr -- DataAddr )
-
-- Description :: Calculates address of dictionary entry data area,
- from entry point.
-
-*** 2num ( -- num result )
-
-- Description :: Attempt to convert string located in =pad= into
- numeric value. If succeed returns number and true as result. If
- not, returns whatever and false as result.
-
-*** dadd ( addr length -- )
-
-- Description :: Add to dictionary data located at *addr*, with
- specified length.
-
-*** lit ( n -- )
-
-- Description :: Act with number depending on =mode=. When
- interpreting, leaves it in stack.
-
-*** incmod ( addr -- )
-
-- Description :: Add to dictionary data located at *addr*+1, length
- is taken from *addr*.
-
-*** here ( -- n )
-
-- Description :: Return =h= contents.
-
-*** mode ( var, 8 bit )
-
-- Description :: Holds input stream parser operation mode. 0 =
- interpreting, 1 = compiling.
-
-*** pad ( var, 128 bytes )
-
-- Description :: Holds temporary strings.
-
-*** h ( var, 32 bit )
-
-- Description :: Pointer to free byte in memory, always at the end of
- the dictionary. Each time when something is stored by =c,= command,
- pointer is increased.
-
-*** lp ( var, 32 bit )
-
-- Description :: Pointer to last dictionary word. Each time when new
- word is compiled or erased by =forget=, this pointer is updated.
-
-*** modulechk ( Dstr<filename> -- )
-
-- Description :: Check if module is loaded, if not immideately load it.
-
-*** ne ( entrydata entrytype -- )
-
-- Description :: Compile new dictionary entry. Its name must be in
- =pad=.
-
-** Conditionals & control flow
-*** if ( flag -- )
-
-- Description :: =if 1.. else 2.. then= or =if 1.. then=
- construction. Conditional execution (immideate). Performs "1.." if
- *flag* was true, elseway performs "2.." if exist. Execution
- continues after word =then=.
-- Example ::
- : 1 if ." nonzero" else ." zero" then
-
-*** >= ( n1 n2 -- result )
-
-- Description :: True if (n1 = n2) or (n1 > n2).
-- Example ::
- : 5 3 >= if ." first number is greater or equal" then
-
-*** <= ( n1 n2 -- result )
-
-- Description :: True if (n1 = n2) or (n1 < n2).
-
-*** = ( n1 n2 -- result )
-
-- Description :: True if n1 = n2.
-
-*** do ( count -- )
-
-- Description :: =do .. loop= construction (immideate). Performs ".."
- *count* times. In every step *count* is decreased until it is 0.
-- Example ::
- : : test 5 do i .d loop ;
- result: 4 3 2 1 0
-
-*** doexit ( -- )
-
-- Description :: Exit from =do .. loop=.
-
-*** for ( count top -- )
-
-- Description :: =for .. loop= construction (immideate). Performs ".."
- (top - count) times. In every step *count* is increased until it
- reaches *top*.
-- Example ::
- : : test 4 10 for i .d loop ;
- result: 4 5 6 7 8 9
-
-*** forexit ( -- )
-
-- Description :: Exit from =for .. loop=.
-
-*** until ( -- )
-
-- Description :: =until .. loop= construction (immideate). Performs
- ".." until flag become true. False by default. Top of return stack
- holds flag.
-
-*** done ( -- )
-
-- Description :: Exit from =until .. loop=.
-
-** Disk & file access
-*** diskload ( FromDisk ToMem amount -- )
-
-- Description :: Load specified amount of bytes from disk into memory.
-
-*** disksave ( FromMem ToDisk amount -- )
-
-- Description :: Save specified amount of bytes from memory into disk.
-
-*** format ( -- )
-
-- Description :: Erase all files.
-
-*** fsDfilesize@ ( handle -- size )
-
-- Description :: Return size of opened file.
-
-*** fsDcurloc@ ( handle -- location )
-
-- Description :: Return current location in file.
-
-*** fsDupdated@ ( handle -- updated? )
-
-- Description :: Return true if file was updated, ie. write operations
- occurred.
-
-*** fssave ( FromMem DestFileHandle amount -- )
-
-- Description :: Save data to file.
-
-*** fsload ( SrcFileHandle ToMem amount -- )
-
-- Description :: Load data from file.
-
-*** fseof ( handle -- bytesLeft )
-
-- Description :: Return amount of bytes left till end of file. Useful
- before read operation.
-
-*** fsls ( -- )
-
-- Description :: List all files and lists (directories, folders) in
- current path.
-
-*** fslsr ( -- )
-
-- Description :: Same as =fsls= but recursively scans also sub lists.
-
-*** fscl ( DynStrHand -- )
-
-- Description :: Change list (path).
-
-*** fscreate ( DynStrHand -- DescPnt )
-
-- Description :: Create new file or list. Can create multiple lists at
- once.
-- Example ::
- When creating:
- : "\listGAMES\listSTRATEGY\listSIMWORLD\5th-runme"
- and only =\listGAMES\= already exist, then =listSTRATEGY= and
- =listSIMWORLD= lists will be created, and empty file =5th-runme=
- placed in there.
-
-*** fsDsave ( DynHand<data> DynStrHand<filename> -- )
-
-- Description :: Create new file and save all data from dynamic memory
- block to it.
-
-*** fsDload ( DynStr<SrcFileName> DynHand<DataDest> -- )
-
-- Description :: Load whole file into dynamic memory block.
-
-*** fsDloadnew ( DynStr<SrcFileName> -- DynHand<DataDest> )
-
-- Description :: Load whole file into new dynamic memory block.
-
-** Dynamic memory
-*** dynal ( size -- handle )
-
-- Description :: Allocate dynamic memory block and return its handle.
-
-*** dynde ( handle -- )
-
-- Description :: Deallocate dynamic memory block.
-
-*** dynp ( handle -- addr )
-
-- Description :: Returns pointer to memory where dynamic block data
- begins.
-
-*** dyns ( handle -- size )
-
-- Description :: Returns size of dynamic block.
-
-*** dynresize ( NewSize handle -- )
-
-- Description :: Nondestructively resize dynamic block.
-
-*** dync@ ( addr handle )
-
-- Description :: Read one byte from dynamic block.
-
-*** dync! ( byte addr dynhandle )
-
-- Description :: Write one byte to dynamic block.
-
-*** dyn@ ( addr handle )
-
-- Description :: Read 32 bit number from dynamic block. Address will
- specify which number, not byte.
-
-*** dyn! ( 32BitNum addr dynhandle )
-
-- Description :: Write 32 bit number to dynamic block. Address will
- specify which number, not byte.
-
-*** dyncon ( size "name" -- )
-
-- Description :: Allocate dynamic block with specified size, and
- create constant holding its handle.
-- Example ::
- : 100 dyncon MyNewBlock
-
-*** dyn. ( handle -- )
-
-- Description :: Write content of dynamic memory block to screen.
-
-** Graphics and text
-*** . ( n -- )
-
-- Description :: Print number on screen.
-
-*** d. ( n -- )
-
-- Description :: Print number on screen in decimal.
-
-*** ? ( addr -- )
-
-- Description :: Print 32 bit value located at *addr*.
-
-*** ." <string>" ( -- )
-
-- Description :: Print string into screen. Immideately compiles.
-- Example ::
- : : greeting ." Hello, World" ;
-
-*** tab. ( -- )
-
-- Description :: Print tabulator.
-
-*** calccol ( b g r -- c )
-
-- Description :: Calculate color what best matches given Blue Green &
- Red values. Values must be in range 0 - 255.
-
-*** imgalloc ( xsize ysize -- imgbuf )
-
-- Description :: Allocate image buffer for specified size.
-
-*** imgsize ( imgbuf -- )
-
-- Description :: Print on the screen X & Y size of image buffer.
-
-*** point ( x y imgbuf -- addr )
-
-- Description :: Returns memory address for specified pixel.
-
-*** pset ( color x y imgbuf -- )
-
-- Description :: Set graphic point.
-
-*** boxf ( x1 x2 y1 y2 imgbuf color -- )
-
-- Description :: Draw filled box.
-
-*** cls ( imgbuf -- )
-
-- Description :: Clear image buffer.
-
-*** setpal ( b g r color -- )
-
-- Description :: Set palette value for specified color. Values must be
- in range 0 - 63.
-
-*** putchar ( char color x y imgbuf -- )
-
-- Description :: Put graphic character in imagebuffer to specified (x
- & y) location.
-
-*** scroll ( x y imgbuf -- )
-
-- Description :: Scroll in imgbuf.
-
-*** scrollf ( color x y screen -- )
-
-- Description :: Scroll and fill empty space with given color.
-
-*** at! ( x y -- )
-
-- Description :: Set cursor location.
-
-*** curc! ( color -- )
-
-- Description :: Set text color.
-
-*** curb! ( color -- )
-
-- Description :: Set background color.
-
-*** colnorm ( -- )
-
-- Description :: Set text color to normal.
-
-*** colneg ( -- )
-
-- Description :: Set text color to negative (selected).
-
-*** dyntype ( dynhandle -- )
-
-- Description :: Display content of dynamic memory on screen.
-
-*** fsdisp ( file -- )
-
-- Description :: Clear screen, display file, and wait for key.
-
-*** type ( addr length -- )
-
-- Description :: Types on the screen string, from memory at *addr*
- and specified length.
-
-*** write ( addr -- )
-
-- Description :: Types on the screen string, from memory at *addr*+1,
- length is taken from *addr*.
-
-*** screen ( const, 32 bit )
-
-- Description :: Holds handle of screen buffer.
-
-*** copyscreen ( SrcImgHandle DestImgHandle -- )
-
-- Description :: Copy content of source image to destination image.
- Source and destination images must have same size.
-
-** Math, memory & stack manipulation
-*** off ( n -- )
-
-- Description :: Writes 0 to given address, good for zeroing variable.
-- Example ::
- : MyVariable off
-
-*** on ( n -- )
-
-- Description :: Writes -1 (true flag) to given address.
-- Example ::
- : MyVariable on
-
-*** 2dup ( n1 n2 -- n1 n2 n1 n2 )
-*** 2drop ( n1 n2 -- )
-*** nip ( n1 n2 -- n2 )
-*** neg ( n1 -- -n1 )
-:PROPERTIES:
-:CUSTOM_ID: ID-a7cf0a47-40f1-49bc-afde-f6e86ac2c6d8
-:ID: a7cf0a47-40f1-49bc-afde-f6e86ac2c6d8
-:END:
-+ See also: [[id:861f65e6-7ecb-43f0-9927-396855fb993f][abs ( n -- |n| )]]
-
-- Description :: Invert sign (negate) of a numerical value.
-
-*** bit@ ( n bit -- result )
-
-- Description :: Return specified bit from n.
-- Example ::
- : 38 2 bit@
- result will be 1
-
-*** to32bit ( n1 n2 n3 n4 -- n32 )
-
-- Description :: Treat 4 last stack elements as bytes and unite them
- into 32 bit double word. Most significant byte on top.
-- Example ::
- : 12 76 23 11 to32bit
- result: 186076172
-
-*** to8bit ( n32 -- n1 n2 n3 n4 )
-
-- Description :: Break 32 bit number into 4 bytes. Useful if you need
- to send 32 bit numbers though 8 bit COM port.
-- Example ::
- : 186076172 to8bit
- result: 12 76 23 11
-
-*** mod ( n1 n2 -- reminder )
-
-- Description :: Divide *n1* by *n2* and returns reminder.
-- Example ::
- : 12 5 mod
- result: 2
-
-*** bound ( low n high -- n )
-
-- Description :: Check if *n* is in given bounds (upper and lower
- bounds are both inclusive). If *n* if outside of bounds, increase or
- decrease its value accordingly to stay within bounds.
-- Examples ::
- : 5 80 15 bound
- result: 15
-
- : 5 10 15 bound
- result: 10
-
- : 5 -10 15 bound
- result: 5
-
-*** bound? ( low n high -- result )
-
-- Description :: Returns true if *n* is in the given bounds. Upper and
- lower bounds are both inclusive.
-
-*** tab ( col -- spaces )
-
-- Description :: Calculate amount of spaces to add to reach next
- tabulation from given column.
-
-*** count ( addr -- addr+1 n )
-
-- Description :: Useful for returning bytes from constantly
- incareasing address. Module =type= is nice example.
-
-*** c, ( n -- )
-
-- Description :: Store one byte at memory specified by =h=. And
- incarease =h= by 1.
-
-*** , ( n -- )
-
-- Description :: Store 32 bit number at memory specified by =h=. And
- incarease =h= by 4.
-
-*** cmove ( addr1 addr2 n -- )
-
-- Description :: Copy *n* amount of bytes from memory at *addr1* to
- memory at *addr2*.
-
-*** rnd ( limit -- result )
-
-- Description :: Generates random number in range 0 to =limit= - 1.
-
-*** abs ( n -- |n| )
-:PROPERTIES:
-:CUSTOM_ID: ID-861f65e6-7ecb-43f0-9927-396855fb993f
-:ID: 861f65e6-7ecb-43f0-9927-396855fb993f
-:END:
-+ See also: [[id:a7cf0a47-40f1-49bc-afde-f6e86ac2c6d8][neg ( n1 -- -n1 )]]
-
-- Description :: Returns absolute value of *n*.
-
-** Dynamic & static strings
-
-Fifth supports both static and dynamic strings. Static strings must
-have predefined space reserved, and string mustn't exceed this
-length. They manipulation is faster. But they use more memory. Static
-string memory address is used to refer to the string.
-
-Dynamic strings can have at any time length form 0 to 0FFh, They take
-up only memory they currently need. They are held in dynamic memory
-blocks, so dynamic block handle is used to refer to this string.
-
-Both types of strings are stored in the way, where first (0th) byte
-holds current string length, following bytes are string itself.
-
-*** Dstral ( -- handle )
-
-- Description :: Allocate new dynamic string.
-
-*** Dstrlen ( handle -- length )
-
-- Description :: Return string length.
-
-*** c+Dstr ( chr handle -- )
-
-- Description :: Add one byte to end of the string.
-
-*** c+lDstr ( chr handle -- )
-
-- Description :: Add one byte to left side (beginning) of the string.
-
-*** Dstr. ( handle -- )
-
-- Description :: Write content of string into screen.
-
-*** Dstrsure ( size Dstr -- )
-
-- Description :: Makes sure that at least requested *size* (amount of
- characters) is allocated for given dynamic string.
-
-*** Dstr2str ( handle address -- )
-
-- Description :: Copy dynamic string into static memory space.
-
-*** str2Dstr ( address handle -- )
-
-- Description :: Copy static string into dynamic string.
-
-*** Dstr+str ( Dstr addr -- )
-
-- Description :: Add content of dynamic string to static string.
-
-*** D" any string" ( -- Dstr )
-
-- Description :: Moves specified string into dynamic string called
- =defDstr=.
-
-*** D> any_string ( -- Dstr )
-
-- Description :: Moves specified string into dynamic string called
- =defDstr=. Space marks end of string!
-
-*** D>2 any_string ( -- Dstr )
-
-- Description :: Moves specified string into dynamic string called
- =defDstr2=. Space marks end of string!
-
-*** Dstr+Dstr ( Dstr1 Dstr2 -- )
-
-- Description :: Adds *Dstr1* to *Dstr2* and places result into
- *Dstr2*.
-
-*** Dstrclear ( Dstr -- )
-
-- Description :: Clears content of dynamic string.
-
-*** Dstr2Dstr ( Dstr1 Dstr2 -- )
-
-- Description :: Moves *Dstr1* to *Dstr2*.
-
-*** Dstr ( data" name -- )
-
-- Description :: Creates new dynamic string and moves specified data
- into it. Then creates new constant with given *name* holding
- created dynamic string handle.
-- Example ::
- : Dstr Hello, my name is Sven!" message \ creates it
- : message Dstr. \ tests it
-
-*** Dstrlscan ( char Dstr -- loc )
-
-- Description :: Searches dynamic string for *char*, from left to
- right, returns first found *char* location in string, or 0, if not
- found.
-
-*** Dstrrscan ( char Dstr -- loc )
-
-- Description :: Searches dynamic string for *char*, from right to
- left, returns first found *char* location in string, or 0, if not
- found.
-
-*** Dstrlscane ( char Dstr -- loc )
-
-- Description :: Same as =Dstrlscan= but returns string length+1 as
- location.
-
-*** Dstrleft ( amo Dstr -- )
-
-- Description :: Only specified amount of characters from left remains
- in dynamic string. ie. cut right part out.
-
-*** Dstrright ( amo Dstr -- )
-
-- Description :: Only specified amount of characters from right
- remains in dynamic string. ie. cut left part out.
-
-*** Dstrcutl ( amo Dstr -- )
-
-- Description :: Cut specified amount of characters from left of
- dynamic string out.
-
-*** Dstrsp ( char Dstr1 Dstr2 -- )
-
-- Description :: Separate dynamic string in *Dstr1* into two parts,
- using *char* as separator. First part will be stored in *Dstr2*,
- second part in *Dstr1*.
-- Example ::
- : asc \ \ ..separator
- : D> listF\listLIB\5TH_DRVMOUSE \ ..separate from
- : defDstr2 \ ..place result in
- : Dstrsp \ separation command
- : defDstr Dstr. \ will be: listLIB\5TH_DRVMOUSE
- : defDstr2 Dstr. \ will be: listF
-
-*** Dv ( addr -- )
-
-- Description :: Allocates empty dynamic string, and places its
- handle into given address.
-
-*** Df ( addr -- )
-
-- Description :: Reads dynamic string handle from given address and
- deallocates (frees) it.
-- Example ::
- : var mystring1
- : : testmodule
- : mystring1 Dv \ allocates string
- :
- : <whatever>
- :
- : mystring1 Df ; \ deallocates it again when no longer needed.
+++ /dev/null
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine opcodes 0 -- 9
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-* Overview
-
-This document describes virtual machine opcodes 0 through 9. These are
-the fundamental instructions for program flow control, stack
-manipulation, and basic I/O operations.
-
-*Quick Reference:*
-
-| Opcode | Name | Stack Effect | Bytecode Size | Purpose |
-|--------+------+------------------+---------------+---------------------------------|
-| 0 | nop | -- | 1 byte | Do nothing (placeholder) |
-| 1 | halt | -- | 1 byte | Exit emulator |
-| 2 | kbd@ | -- scancode | 1 byte | Read keyboard input |
-| 3 | num | -- n | 5 bytes | Push literal 32-bit number |
-| 4 | jmp | -- | 5 bytes | Unconditional jump |
-| 5 | call | -- (R: -- ret) | 5 bytes | Call subroutine |
-| 6 | 1+ | n -- n+1 | 1 byte | Increment by 1 |
-| 7 | 1- | n -- n-1 | 1 byte | Decrement by 1 |
-| 8 | dup | n -- n n | 1 byte | Duplicate top of stack |
-| 9 | drop | n -- | 1 byte | Discard top of stack |
-
-* 0: nop (No Operation)
-:PROPERTIES:
-:CUSTOM_ID: opcode-nop
-:ID: 4f0dee8a-647e-4d91-a252-aaf4782b5edc
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+-------------------|
-| Opcode | 0 |
-| Name | nop |
-| Stack | ( -- ) |
-| Bytecode | Single byte: =00= |
-
-*Description*
-
-Does nothing. Execution continues immediately to the next instruction.
-This opcode is handled as the entry point to the emulator's instruction
-fetch loop, meaning it effectively "falls through" to the next opcode.
-
-*When to Use:*
-
-- Padding bytecode for alignment purposes
-- Placeholder during development or debugging
-- Safe target for jump instructions when you need a "do nothing" branch
-- Filling unused entries in jump tables
-
-*Example:*
-
-#+BEGIN_SRC
-00 ; nop - do nothing
-03 05 00 00 00 ; num 5 - push 5 onto stack
-#+END_SRC
-
-No observable effect. The instruction pointer simply advances to the
-next opcode.
-
-* 1: halt (Stop Execution)
-:PROPERTIES:
-:CUSTOM_ID: opcode-halt
-:ID: 7c8841d3-2335-4ee9-b6b9-518d05354841
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+-------------------|
-| Opcode | 1 |
-| Name | halt |
-| Stack | ( -- ) |
-| Bytecode | Single byte: =01= |
-
-** Description
-
-Terminates the emulator process cleanly. This is the only proper way
-to exit the Fifth environment. When executed, the emulator:
-
-1. Restores text video mode (mode 3)
-2. Closes the virtual disk file handle
-3. Restores the original keyboard interrupt handler
-4. Returns control to DOS
-
-** When to Use
-
-- Terminating the program cleanly and exiting the Fifth environment
-- Implementing a "quit" or "bye" command in REPL
-- Shutting down after fatal errors
-
-** Example
-
-#+BEGIN_SRC
-01 ; halt - exit emulator immediately
-#+END_SRC
-
-In Fifth source code, this is typically invoked via the =bye= word,
-which compiles to the halt opcode.
-
-* 2: kbd@ (Keyboard Input)
-:PROPERTIES:
-:CUSTOM_ID: opcode-kbd-read
-:ID: 820b1b90-4f4c-4ab1-b49f-9b4a52ea2528
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+-------------------|
-| Opcode | 2 |
-| Name | kbd@ |
-| Stack | ( -- scancode ) |
-| Bytecode | Single byte: =02= |
-
-** Description
-
-Reads a keyboard scancode from the internal ring buffer and pushes it
-onto the data stack. This is a non-blocking operation: if no key is
-pending, it pushes 0 immediately.
-
-** Scan Code Behavior
-
-The keyboard hardware generates scan codes for every key event:
-
-- *Make code*: Generated when a key is pressed (values 1-127)
-- *Break code*: Generated when a key is released (make code + 128)
-
-For example:
-- Pressing 'A' produces scan code =1E=
-- Releasing 'A' produces scan code =9E= (1E + 80 hex)
-
-** Common Scan Codes (US QWERTY)
-
-| Key | Make Code | Break Code |
-|-------------+-----------+------------|
-| Escape | 01 | 81 |
-| 1-9 | 02-0A | 82-8A |
-| 0 | 0B | 8B |
-| Enter | 1C | 9C |
-| Space | 39 | B9 |
-| Left Arrow | 4B | CB |
-| Right Arrow | 4D | CD |
-| Up Arrow | 48 | C8 |
-| Down Arrow | 50 | D0 |
-| A | 1E | 9E |
-| Z | 2C | AC |
-
-** When to Use
-
-- Building interactive programs (games, text editors)
-- Reading raw keyboard input without translation to characters
-- Detecting key press/release events separately
-- Implementing custom keyboard drivers or input handlers
-
-** Example
-
-Basic key reading:
-
-#+BEGIN_SRC
-02 ; kbd@ - read scancode
- ; data stack now contains: scancode (or 0 if no key)
-#+END_SRC
-
-** Return Values
-
-- Returns =0= if no key is available in the buffer
-- Returns scancode (1-255) when a key event is pending
-- Each call consumes one scancode from the buffer
-
-** Important Notes
-
-- This returns raw scan codes, not ASCII characters
-- Use a keyboard layout file (like =5TH_KBD_US=) to translate scan
- codes to FSCII characters
-- The buffer holds 128 scan codes; older codes are overwritten if full
-
-* 3: num (Push Literal Number)
-:PROPERTIES:
-:CUSTOM_ID: opcode-num
-:ID: 8721dc8c-b237-4e9a-a509-0ded1c02329b
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+------------------------------------------|
-| Opcode | 3 |
-| Name | num |
-| Stack | ( -- n ) |
-| Bytecode | =03= followed by 4 bytes (little-endian) |
-
-** Description
-
-Pushes a 32-bit literal value onto the data stack. The value is encoded
-directly in the instruction stream immediately after the opcode byte.
-After execution, the instruction pointer advances by 4 bytes.
-
-** Bytecode Format
-
-#+BEGIN_SRC
-03 xx xx xx xx
-│ └──────────┘ 32-bit value (little-endian)
-└ opcode
-#+END_SRC
-
-** When to Use
-
-- Loading constant values for calculations
-- Pushing memory addresses onto the stack
-- Setting up parameters for function calls
-- Initializing counters, pointers, and loop variables
-
-** Example
-
-Push the value =0x12345678=:
-
-#+BEGIN_SRC
-03 ; num opcode
-78 56 34 12 ; 32-bit value 0x12345678 (little-endian)
- ; stack now contains: 0x12345678
-#+END_SRC
-
-In Fifth source code, simply write a number:
-
-#+BEGIN_SRC fifth
-12345678 ; Compiles to: 03 78 56 34 12
-FF ; Compiles to: 03 FF 00 00 00
-1000 ; Compiles to: 03 00 10 00 00
-#+END_SRC
-
-** Important Notes
-
-- Numbers in Fifth source code are **hexadecimal by default**
-- Use =d.= to display numbers in decimal for debugging
-- All 32 bits are pushed; high bytes are zero-filled for small values
-- The value is stored in little-endian byte order in bytecode
-
-* 4: jmp (Unconditional Jump)
-:PROPERTIES:
-:CUSTOM_ID: opcode-jmp
-:ID: a5c53b60-ffe9-4ba4-ab87-30c2ccc51a45
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+------------------------------------------|
-| Opcode | 4 |
-| Name | jmp |
-| Stack | ( -- ) |
-| Bytecode | =04= followed by 4-byte target address |
-
-** Description
-
-Transfers execution unconditionally to the specified address. The
-target address is embedded in the instruction stream immediately after
-the opcode. No return address is saved.
-
-** Bytecode Format
-
-#+BEGIN_SRC
-04 aa aa aa aa
-│ └──────────┘ 32-bit target address (little-endian)
-└ opcode
-#+END_SRC
-
-** When to Use
-
-- Creating infinite loops (combined with conditional exits)
-- Skipping over code blocks
-- Implementing forward jumps in control structures
-- Jump tables and dispatch mechanisms
-
-** Example
-
-Jump to address =0x1000=:
-
-#+BEGIN_SRC
-04 ; jmp opcode
-00 10 00 00 ; target address 0x1000 (little-endian)
- ; execution continues at 0x1000
-#+END_SRC
-
-Create an infinite loop:
-
-#+BEGIN_SRC
-; At address 0x100:
-04 00 01 00 00 ; jmp 0x100 - infinite loop
-#+END_SRC
-
-Skip over code:
-
-#+BEGIN_SRC
-04 ... ; jmp to skip_target
-... ; code to skip
-; skip_target:
-#+END_SRC
-
-** Important Notes
-
-- The address is a **virtual address** in the VM's address space
-- The emulator adds =xms_addr= to convert to a physical memory address
-- No return address is saved; use =call= for subroutines
-
-* 5: call (Call Subroutine)
-:PROPERTIES:
-:CUSTOM_ID: opcode-call
-:ID: 66efbd35-c2b6-4657-ba0f-ff00a3a2c312
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|--------------+----------------------------------------------|
-| Opcode | 5 |
-| Name | call |
-| Stack | ( -- ) |
-| Return Stack | ( -- ret-addr ) |
-| Bytecode | =05= followed by 4-byte target address |
-
-** Description
-
-Calls a subroutine at the specified address. The return address (the
-address of the instruction immediately following this call) is saved
-on the return stack. Use =ret= (opcode 11) to return to the caller.
-
-** Bytecode Format
-
-#+BEGIN_SRC
-05 aa aa aa aa
-│ └──────────┘ 32-bit target address (little-endian)
-└ opcode
-#+END_SRC
-
-** When to Use
-
-- Calling reusable code blocks (functions, procedures)
-- Implementing Forth word definitions
-- Building modular programs with separate subroutines
-- Recursion (with proper stack management)
-
-** Example
-
-#+BEGIN_SRC
-; Main code at 0x100:
-05 00 20 00 00 ; call 0x2000
-03 01 00 00 00 ; num 1 (this is where we return to)
-
-; Subroutine at 0x2000:
-... ; subroutine code
-0B ; ret - return to caller
-#+END_SRC
-
-** Stack Effects
-
-| Stack | Before | After |
-|--------------+--------+-----------|
-| Data Stack | ... | ... |
-| Return Stack | ... | ret-addr |
-
-The return address is the virtual address immediately after the 4-byte
-argument (i.e., the address of the next instruction).
-
-** Important Notes
-
-- The return stack is **separate** from the data stack
-- The return stack is also used by =push= and =pop= opcodes
-- Always balance =call= with =ret= to avoid return stack corruption
-- Nesting depth is limited by return stack size
-
-* 6: 1+ (Increment)
-:PROPERTIES:
-:CUSTOM_ID: opcode-1plus
-:ID: 76fe1c8f-756d-406e-9e8a-4ecc8f1d7369
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 6 |
-| Name | 1+ |
-| Stack | ( n -- n+1 ) |
-| Bytecode | Single byte: =06= |
-
-** Description
-
-Adds 1 to the value on top of the data stack, modifying it in place.
-This is a highly efficient single-byte operation.
-
-** When to Use
-
-- Incrementing loop counters
-- Advancing pointers through arrays or buffers
-- Computing successor values
-- Any situation where you need to add exactly 1
-
-** Example
-
-#+BEGIN_SRC
-03 05 00 00 00 ; num 5 - stack: [ 5 ]
-06 ; 1+ - stack: [ 6 ]
-06 ; 1+ - stack: [ 7 ]
-#+END_SRC
-
-** Efficiency Comparison
-
-| Operation | Bytecode | Size |
-|-------------+---------------------------+-------|
-| 1+ | =06= | 1 byte|
-| 1 + | =03 01 00 00 00= =18= | 6 bytes|
-
-Use =1+= instead of =1 += for better code density.
-
-** Important Notes
-
-- Modifies the value in place (no additional stack space needed)
-- Works identically on signed and unsigned 32-bit values
-- For adding larger values, use =+= (opcode 24)
-
-* 7: 1- (Decrement)
-:PROPERTIES:
-:CUSTOM_ID: opcode-1minus
-:ID: 523f93a3-359e-4a6d-b296-df25008db403
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 7 |
-| Name | 1- |
-| Stack | ( n -- n-1 ) |
-| Bytecode | Single byte: =07= |
-
-** Description
-
-Subtracts 1 from the value on top of the data stack, modifying it in
-place. This is a highly efficient single-byte operation.
-
-** When to Use
-
-- Decrementing loop counters
-- Moving pointers backward through arrays or buffers
-- Computing predecessor values
-- Counting down to zero
-
-** Example
-
-#+BEGIN_SRC
-03 0A 00 00 00 ; num 10 - stack: [ 10 ]
-07 ; 1- - stack: [ 9 ]
-07 ; 1- - stack: [ 8 ]
-#+END_SRC
-
-** Common Pattern: Countdown Loop
-
-#+BEGIN_SRC
-: loop ( n -- )
- begin
- dup . ; display counter
- 1- dup ; decrement and copy for test
- 0= until ; loop until zero
- drop ; clean up
-;
-
-A loop ; prints A, 9, 8, ... 1
-#+END_SRC
-
-** Important Notes
-
-- Modifies the value in place (no additional stack space needed)
-- Works identically on signed and unsigned 32-bit values
-- For subtracting larger values, use =-= (opcode 25)
-
----------------------------------------------------------------------
-
-* 8: dup (Duplicate)
-:PROPERTIES:
-:CUSTOM_ID: opcode-dup
-:ID: 2a6a449a-fc76-421c-a81c-c2024a15fc78
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 8 |
-| Name | dup |
-| Stack | ( n -- n n ) |
-| Bytecode | Single byte: =08= |
-
-** Description
-
-Duplicates the value on top of the data stack, pushing a copy. The
-original value remains, and an identical copy is placed on top.
-
-** When to Use
-
-- Preserving a value before consuming operations
-- Using the same value multiple times in a calculation
-- Displaying values without losing them
-- Setting up parameters for operations that consume values
-
-** Example
-
-#+BEGIN_SRC
-03 2A 00 00 00 ; num 42 - stack: [ 42 ]
-08 ; dup - stack: [ 42 42 ]
-#+END_SRC
-
-** Common Patterns
-
-*** Read and Keep Address
-
-#+BEGIN_SRC
-addr dup @ ; stack: [ addr value ]
- ; read from address while keeping the address
-#+END_SRC
-
-*** Display and Keep
-
-#+BEGIN_SRC
-value dup . ; display value while keeping it on stack
-#+END_SRC
-
-*** Test Without Consuming
-
-#+BEGIN_SRC
-value dup 0= ; test if zero, keep original value
-if
- ... ; handle zero case (value still on stack)
-then
-drop ; now consume it
-#+END_SRC
-
-** Important Notes
-
-- Increases stack depth by 1
-- Often paired with =drop= to test values without consuming them
-- For duplicating the second stack item, use =over= (opcode 22)
-
----------------------------------------------------------------------
-
-* 9: drop (Discard)
-:PROPERTIES:
-:CUSTOM_ID: opcode-drop
-:ID: 4600dbeb-1833-4e7d-af7e-f6cc6c98d022
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 9 |
-| Name | drop |
-| Stack | ( n -- ) |
-| Bytecode | Single byte: =09= |
-
-** Description
-
-Removes and discards the value from the top of the data stack. The
-value is completely lost.
-
-** When to Use
-
-- Cleaning up the stack after calculations
-- Discarding values that are no longer needed
-- Consuming function results you don't need
-- Balancing stack operations
-
-** Example
-
-#+BEGIN_SRC
-03 2A 00 00 00 ; num 42 - stack: [ 42 ]
-03 64 00 00 00 ; num 100 - stack: [ 42 100 ]
-09 ; drop - stack: [ 42 ]
-09 ; drop - stack: [ ]
-#+END_SRC
-
-** Common Patterns
-
-*** Discard Unused Return Values
-
-#+BEGIN_SRC
-some-operation drop ; call operation, ignore result
-#+END_SRC
-
-*** Execute for Side Effect Only
-
-#+BEGIN_SRC
-addr @ drop ; read from memory, discard value
- ; useful for prefetching or triggering hardware
-#+END_SRC
-
-*** Clean Up After Conditional
-
-#+BEGIN_SRC
-condition if
- ... ; condition was true (consumed by if)
-else
- ... ; condition was false
-then
-#+END_SRC
-
-** Important Notes
-
-- Reduces stack depth by 1
-- The value is completely lost; ensure you don't need it
-- Often paired with =dup= for testing values without consuming them
-- For discarding two values, use =2drop= (defined in high-level Fifth)
+++ /dev/null
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine opcodes 10 -- 19
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-* Overview
-
-This document describes virtual machine opcodes 10 through 19. These
-opcodes provide conditional branching, subroutine return, byte-level
-memory access, stack-to-return-stack operations, stack rotation, and
-disk I/O.
-
-*Quick Reference:*
-
-| Opcode | Name | Stack Effect | Bytecode Size | Purpose |
-|--------+--------+----------------+---------------+-------------------------------------------------|
-| 10 | if | flag -- | 5 bytes | Conditional jump (jump if zero) |
-| 11 | ret | -- | 1 byte | Return from subroutine |
-| 12 | c@ | addr -- byte | 1 byte | Read byte from memory |
-| 13 | c! | byte addr -- | 1 byte | Write byte to memory |
-| 14 | push | n -- | 1 byte | Move data to return stack |
-| 15 | pop | -- n | 1 byte | Move data from return stack |
-| 16 | unused | -- | 1 byte | Reserved for future use. Currently acts as nop. |
-| 17 | rot | a b c -- b c a | 1 byte | Rotate three stack elements |
-| 18 | disk@ | sector addr -- | 1 byte | Read 1KB sector from disk |
-| 19 | disk! | addr sector -- | 1 byte | Write 1KB sector to disk |
-
-* 10: if (Conditional Jump)
-:PROPERTIES:
-:CUSTOM_ID: opcode-if
-:ID: d6f834b6-9a37-4414-91b3-62b3e1d920c1
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+------------------------------------------|
-| Opcode | 10 (0x0A) |
-| Name | if |
-| Stack | ( flag -- ) |
-| Bytecode | =0A= followed by 4-byte target address |
-
-*Description:*
-
-Pops the top value from the data stack. If the value is zero, jumps to
-the address embedded in the instruction stream. If the value is
-non-zero, skips the address and continues with the next instruction.
-
-This "jump if false" semantics is exactly what is needed to jump/skip
-over code because comparison condition was not met:
-
-: #1: <comparison logic, leaves comparison result on the top of data stack>
-: #2: <jmp instruction + pointer to #4 address>
-: #3: <this code will be executed conditionally, if comparison was true>
-: #4: <any other code that follows after IF ... THEN ... block.>
-
-
-*Bytecode Format:*
-
-#+BEGIN_SRC
-0A aa aa aa aa
-│ └──────────┘ 32-bit target address (little-endian)
-└ opcode
-#+END_SRC
-
-*When to Use*
-
-- Implementing conditional branches in control structures
-- Building if-then-else constructs (the "else" branch)
-- Loop exit conditions
-
-*Important Notes:*
-
-- The jump is taken when flag is ZERO (false condition)
-- This is actually "jump if false" semantics
-- High-level Fifth words like =if= =then= =else= compile to this opcode
-- The address is a virtual address in the VM's address space
-
-* 11: ret (Return from Subroutine)
-:PROPERTIES:
-:CUSTOM_ID: opcode-ret
-:ID: 6e683977-a985-4bb8-9d2c-c860d30e1df6
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|----------+-------------------|
-| Opcode | 11 (0x0B) |
-| Name | ret |
-| Stack | ( -- ) |
-| R. Stack | ( ret-addr -- ) |
-| Bytecode | Single byte: =0B= |
-
-*Description:*
-
-Returns from a subroutine by popping the return address from the
-return stack and jumping to it. This is the counterpart to the =call=
-opcode (5).
-
-
-*When to Use:*
-
-- Ending subroutine/word definitions
-- Early exit from subroutines
-
-*Example:*
-
-#+BEGIN_SRC
-; Subroutine at address 0x2000:
-03 2A 00 00 00 ; num 42 - push a value
-0B ; ret - return to caller
-#+END_SRC
-
-In Fifth source code:
-
-#+BEGIN_SRC fifth
-: myword ( -- n )
- 2A \ push 42
-; \ compiles to ret
-#+END_SRC
-
-** Important Notes
-
-- Each =ret= must have a matching =call= to maintain return stack balance
-- Unbalanced call/ret pairs will corrupt the return stack
-- The return stack is also used by =push= and =pop= opcodes
-- Return addresses are virtual addresses (without xms_addr offset)
-
-* 12: c@ (Fetch Byte from Memory)
-:PROPERTIES:
-:CUSTOM_ID: opcode-cfetch
-:ID: a2ce44f7-b661-44e0-909b-644ff52aa38e
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 12 (0x0C) |
-| Name | c@ (c-fetch) |
-| Stack | ( addr -- byte ) |
-| Bytecode | Single byte: =0C= |
-
-*Description:*
-
-Reads a single byte from the specified memory address and pushes it
-onto the data stack. The byte value is zero-extended to 32 bits
-(padded with zeros in the upper 24 bits).
-
-*When to Use:*
-
-- Reading character data from strings
-- Parsing binary data structures
-- Reading individual pixels from image buffers
-
-
-*Example:*
-
-Read a byte from address 0x1000:
-
-#+BEGIN_SRC
-03 00 10 00 00 ; num 0x1000 - push address
-0C ; c@ - read byte
-; Stack now contains: byte-value (0-255, zero-extended)
-#+END_SRC
-
-
-*Important Notes:*
-
-- The byte is zero-extended: reading 0xFF gives 0x000000FF on stack
-- The address is a virtual address (xms_addr is added internally)
-
-* 13: c! (Store Byte to Memory)
-:PROPERTIES:
-:CUSTOM_ID: opcode-cstore
-:ID: f129ef87-a31a-40fe-b8d3-984da2db90e2
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 13 (0x0D) |
-| Name | c! (c-store) |
-| Stack | ( byte addr -- ) |
-| Bytecode | Single byte: =0D= |
-
-*Description:*
-
-Stores a single byte to the specified memory address. The byte value
-is taken from the low 8 bits of the value on the stack. The upper 24
-bits are ignored.
-
-*When to Use:*
-
-- Writing character data to strings
-- Building binary data structures
-- Drawing individual pixels to image buffers
-
-*Example:*
-
-Store byte 0x42 at address 0x1000:
-
-#+BEGIN_SRC
-03 42 00 00 00 ; num 0x42 - byte value to store
-03 00 10 00 00 ; num 0x1000 - destination address
-0D ; c! - store byte
-; Memory at 0x1000 now contains 0x42
-#+END_SRC
-
-
-*Important Notes:*
-
-- Upper 24 bits are ignored (no error if value > 255)
-- The address is a virtual address
-
-* 14: push (Move Data to Return Stack)
-:PROPERTIES:
-:CUSTOM_ID: opcode-push
-:ID: e4bcbaf1-7724-4051-b19c-1aa7cd06eae6
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 14 (0x0E) |
-| Name | push |
-| Stack | ( n -- ) |
-| R. Stack | ( -- n ) |
-| Bytecode | Single byte: =0E= |
-
-*Description:*
-
-Moves the top value from the data stack to the return stack. This
-provides temporary storage that doesn't interfere with data stack
-operations.
-
-*When to Use:*
-
-- Temporarily saving values during complex stack manipulations
-
-*Example:*
-
-Save a value for later retrieval:
-
-#+BEGIN_SRC
-03 2A 00 00 00 ; num 42 - push value onto data stack
-0E ; push - move 42 to return stack
-; Data stack is now empty
-; Return stack now contains 42
-
-; ... do other work ...
-
-0F ; pop - retrieve 42 from return stack
-; Data stack now contains 42
-#+END_SRC
-
-
-*Important Notes:*
-
-- Must be balanced with =pop= to avoid return stack corruption
-- Do not mix carelessly with =call= /=ret= pairs
-- The return stack grows downward (like the data stack)
-- Popping too many times can corrupt return addresses
-
-* 15: pop (Move Data from Return Stack)
-:PROPERTIES:
-:CUSTOM_ID: opcode-pop
-:ID: 21871d09-4d58-440f-8c94-231105aa4e3f
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------|
-| Opcode | 15 (0x0F) |
-| Name | pop |
-| Stack | ( -- n ) |
-| R. Stack | ( n -- ) |
-| Bytecode | Single byte: =0F= |
-
-*Description:*
-
-Moves the top value from the return stack to the data stack. This is
-the inverse of the =push= opcode.
-
-*When to Use:*
-
-- Retrieving temporarily saved values
-
-*Example:*
-
-Retrieve a saved value:
-
-#+BEGIN_SRC
-; Assuming return stack contains 42 (from previous 'push')
-0F ; pop - move 42 from return stack to data stack
-; Data stack now contains 42
-; Return stack top item removed
-#+END_SRC
-
-Important Notes:
-- Popping from an empty return stack causes undefined behavior
-- Always ensure there's a value on the return stack before calling =pop=
-- Balance every =push= with a corresponding =pop=
-
-** Related Opcode: i
-
-The =i= opcode (31) copies (rather than moves) the top of the return
-stack to the data stack, leaving the return stack unchanged.
-
-| Opcode | Action | R. Stack Effect |
-|--------+------------------------+-----------------|
-| i | Copy top to data stack | <unchanged> |
-| pop | Move top to data stack | n -- |
-
-* 16: (Unused/Reserved)
-:PROPERTIES:
-:CUSTOM_ID: opcode-unused
-:END:
-
-Opcode 16 (0x10) is reserved for future use. Opcode currently behaves as *nop* instruction.
-
-* 17: rot (Rotate Three Stack Elements)
-:PROPERTIES:
-:CUSTOM_ID: opcode-rot
-:ID: 4cee73f7-c105-4b96-9380-ff89bd7fedad
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+----------------------------|
-| Opcode | 17 (0x11) |
-| Name | rot |
-| Stack | ( n1 n2 n3 -- n2 n3 n1 ) |
-| Bytecode | Single byte: =11= |
-
-*Description*
-
-Rotates the top three values on the data stack. The third item from
-the top moves to the top, while the top two items shift down.
-
-*Visual Representation:*
-#+BEGIN_SRC
-Before: [ ... n1 n2 n3 ] (n3 is on top)
-After: [ ... n2 n3 n1 ] (n1 is now on top)
-#+END_SRC
-
-Rotation direction: the deepest of the three (n1) rotates to the top.
-
-*When to Use:*
-- Accessing values buried deeper on the stack
-- Swapping values in multi-item calculations
-
-
-*Example:*
-
-#+BEGIN_SRC
-03 01 00 00 00 ; num 1 - push n1
-03 02 00 00 00 ; num 2 - push n2
-03 03 00 00 00 ; num 3 - push n3
-; Stack: [ 1 2 3 ] with 3 on top
-
-11 ; rot
-; Stack: [ 2 3 1 ] with 1 on top
-#+END_SRC
-
-Important Notes:
-
-- Requires at least three items on the data stack
-- Rotating with fewer items causes undefined behavior
-- This is a rotation, not a swap - all three elements move
-
-* 18: disk@ (Read Disk Sector)
-:PROPERTIES:
-:CUSTOM_ID: opcode-disk-read
-:ID: bed1aa27-66ac-4c73-bbb9-e49ff2aa67c5
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------------|
-| Opcode | 18 (0x12) |
-| Name | disk@ (disk-fetch) |
-| Stack | ( sector addr -- ) |
-| Bytecode | Single byte: =12= |
-
-*Description:*
-
-Reads one sector (1024 bytes) from the virtual disk into memory at the
-specified address.
-
-*Parameters:*
-
-- *sector* (top of stack): Sector number to read from (0-based)
-- *addr* (second): Destination memory address for the data
-
-*When to Use:*
-
-- Loading program code from disk
-- Reading data files
-- Accessing filesystem structures
-
-*Example:*
-
-Read sector 16 into memory at 0x2000:
-
-#+BEGIN_SRC
-03 10 00 00 00 ; num 16 - sector number
-03 00 20 00 00 ; num 0x2000 - destination address
-12 ; disk@ - read sector
-; 1024 bytes from sector 16 now at address 0x2000
-#+END_SRC
-
-** Technical Details
-
-- 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
-
-** 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
-- The disk is accessed through DOS file handles, not BIOS interrupts
-
-* 19: disk! (Write Disk Sector)
-:PROPERTIES:
-:CUSTOM_ID: opcode-disk-write
-:ID: 02eda775-e483-4057-b809-de36d586579b
-:END:
-
-#+ATTR_HTML: :border 2 :rules all :frame border
-| Property | Value |
-|------------+--------------------------|
-| Opcode | 19 (0x13) |
-| Name | disk! (disk-store) |
-| Stack | ( addr sector -- ) |
-| Bytecode | Single byte: =13= |
-
-*Description*
-
-Writes one sector (1024 bytes) from memory to the virtual disk at the
-specified sector number.
-
-*Parameters:*
-
-- *addr* (top of stack): Source memory address
-- *sector* (second): Sector number to write to (0-based)
-
-*When to Use:*
-
-- Saving program data to disk
-- Modifying filesystem structures
-- Persisting state across emulator sessions
-
-*Example:*
-
-Write memory at 0x2000 to sector 16:
-
-#+BEGIN_SRC
-03 00 20 00 00 ; num 0x2000 - source address
-03 10 00 00 00 ; num 16 - sector number
-13 ; disk! - write sector
-; 1024 bytes from address 0x2000 written to sector 16
-#+END_SRC
-
-*Technical Details:*
-
-- One sector = 1024 bytes (1 KB)
-- Data is first copied to a temporary buffer in conventional memory
- before being written to disk (required for XMS memory access)
-- Writing to sector 0 may corrupt the bootloader/kernel
-
-*Stack Order Note:*
-
-The stack order is =( addr sector -- )=, which is reversed from
-=disk@= =( sector addr -- )=. This is because the data source (addr)
-is "produced" first, then the destination (sector) is specified.
-
-*Important Notes*
-
-- The address is a virtual address
-- Writing to the disk image is immediate (no buffering)
-- Be careful not to overwrite critical sectors (kernel, filesystem)
+++ /dev/null
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine opcodes 20 -- 29
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-
-* 20: @
-:PROPERTIES:
-:CUSTOM_ID: opcode-fetch
-:ID: 0a54f24b-e62a-4244-85e1-855a0007c81e
-:END:
-- *Stack Effect:* =addr -- value=
-- *Description:* Reads a 32-bit value from the specified memory address.
- Replaces the address on the stack with the value.
-- *Notes:* The address is adjusted by =xms_addr= before reading. This is
- the 32-bit equivalent of =c@=.
-- *Example:*
- #+begin_example
- 0x1000 ; Read 32-bit value from address 0x1000
- @
- #+end_example
-
-* 21: !
-:PROPERTIES:
-:CUSTOM_ID: opcode-store
-:ID: 50e0c841-d678-41b4-be5a-1bab792f367d
-:END:
-- *Stack Effect:* =value addr --=
-- *Description:* Stores a 32-bit value to the specified memory address.
-- *Notes:* Takes two values: the value to store and the address. The
- address is adjusted by =xms_addr= before writing.
-- *Example:*
- #+begin_example
- 0x12345678 ; Value to store
- 0x1000 ; Address to store to
- !
- #+end_example
-
-* 22: over
-:PROPERTIES:
-:CUSTOM_ID: opcode-over
-:ID: 29828b20-b14d-41ff-9705-ca576c352fb4
-:END:
-- *Stack Effect:* =n1 n2 -- n1 n2 n1=
-- *Description:* Duplicates the second item on the data stack, placing
- it on top.
-- *Notes:* Commonly used to access the second item without popping the
- top item.
-- *Example:*
- #+begin_example
- 1 2
- over
- #+end_example
- After execution, stack contains 1 2 1.
-
-* 23: swap
-:PROPERTIES:
-:CUSTOM_ID: opcode-swap
-:ID: 905894ae-d687-47cd-add0-f1904179a8a0
-:END:
-- *Stack Effect:* =n1 n2 -- n2 n1=
-- *Description:* Swaps the top two values on the data stack.
-- *Notes:* Essential for reordering stack items during complex
- operations.
-- *Example:*
- #+begin_example
- 1 2
- swap
- #+end_example
- After execution, stack contains 2 1.
-
-* 24: +
-:PROPERTIES:
-:CUSTOM_ID: opcode-add
-:ID: bf4434bb-91a5-40c1-93de-90a0e05a50ec
-:END:
-- *Stack Effect:* =n1 n2 -- n1+n2=
-- *Description:* Adds the top two values on the data stack. The result
- replaces both values.
-- *Notes:* Performs unsigned 32-bit addition. The top value (n1) is
- added to the second value (n2).
-- *Example:*
- #+begin_example
- 1 2
- +
- #+end_example
- After execution, stack contains 3.
-
-* 25: -
-:PROPERTIES:
-:CUSTOM_ID: opcode-sub
-:ID: 9afc77fc-ecee-4a69-b1d2-f511e64a0d72
-:END:
-- *Stack Effect:* =n1 n2 -- n2-n1=
-- *Description:* Subtracts the top value from the second value on the
- data stack. The result replaces both values.
-- *Notes:* Performs unsigned 32-bit subtraction. The second value (n2)
- minus the top value (n1).
-- *Example:*
- #+begin_example
- 1 2
- -
- #+end_example
- After execution, stack contains 1 (2 - 1).
-
-* 26: *
-:PROPERTIES:
-:CUSTOM_ID: opcode-mul
-:ID: 85833e5c-e3ae-41a7-ad3d-6e9f0ecc9cdb
-:END:
-- *Stack Effect:* =n1 n2 -- n1*n2=
-- *Description:* Multiplies the top two values on the data stack. The
- result replaces both values.
-- *Notes:* Uses 32-bit signed multiplication (IMUL). The top value (n1)
- is multiplied by the second value (n2).
-- *Example:*
- #+begin_example
- 2 3
- *
- #+end_example
- After execution, stack contains 6.
-
-* 27: /
-:PROPERTIES:
-:CUSTOM_ID: opcode-div
-:ID: e5ff13c7-a9e6-4035-8efd-220d05381bf1
-:END:
-- *Stack Effect:* =n1 n2 -- n2/n1=
-- *Description:* Divides the second value by the top value on the data
- stack. The quotient replaces both values.
-- *Notes:* Uses signed integer division (IDIV). The second value (n2) is
- divided by the top value (n1).
-- *Example:*
- #+begin_example
- 2 6
- /
- #+end_example
- After execution, stack contains 3 (6 / 2).
-
-* 28: >
-:PROPERTIES:
-:CUSTOM_ID: opcode-gt
-:ID: abcc14d3-6cbf-46c3-8a93-ee7180f350d4
-:END:
-- *Stack Effect:* =n1 n2 -- result=
-- *Description:* Compares the second value (n1) with the top value (n2).
- Returns -1 (true) if n1 > n2, otherwise 0.
-- *Notes:* The comparison is between the second item (n1) and top item
- (n2) on the stack.
-- *Example:*
- #+begin_example
- 2 3
- >
- #+end_example
- After execution, stack contains 0 (since 2 < 3).
-
-* 29: <
-:PROPERTIES:
-:CUSTOM_ID: opcode-lt
-:ID: 68362f38-c9e4-43f8-809c-8e15725a58c8
-:END:
-- *Stack Effect:* =n1 n2 -- result=
-- *Description:* Compares the second value (n1) with the top value (n2).
- Returns -1 (true) if n1 < n2, otherwise 0.
-- *Notes:* The comparison is between the second item (n1) and top item
- (n2) on the stack.
-- *Example:*
- #+begin_example
- 2 3
- <
- #+end_example
- After execution, stack contains -1 (since 2 < 3).
+++ /dev/null
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine opcodes 30 -- 39
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-
-* 30: not
-:PROPERTIES:
-:CUSTOM_ID: opcode-not
-:ID: 21994321-a1c6-42f6-9682-d5a3fbabb88a
-:END:
-- *Stack Effect:* =n -- ~n=
-- *Description:* Performs a bitwise NOT operation on the top value of
- the data stack.
-- *Notes:* Inverts all bits of the dword at the top of the stack.
-- *Example:*
- #+begin_example
- 0x0000FFFF
- not
- #+end_example
- After execution, stack contains 0xFFFF0000.
-
-* 31: i
-:PROPERTIES:
-:CUSTOM_ID: opcode-i
-:ID: 61fbdd36-4384-44a6-b162-b4e998c4c191
-:END:
-- *Stack Effect:* =-- r=
-- *Description:* Pushes the top value from the return stack onto the
- data stack.
-- *Notes:* Commonly used to access loop counters or subroutine
- parameters.
-- *Example:*
- #+begin_example
- 42
- push
- i
- #+end_example
- After execution, data stack contains 42.
-
-* 32: cprt@
-:PROPERTIES:
-:CUSTOM_ID: opcode-cprt-read
-:ID: 78c1ac50-9c26-4132-8e02-8311331f2a1c
-:END:
-- *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.
-- *Example:*
- #+begin_example
- 0x60 ; Keyboard port
- cprt@
- #+end_example
-
-* 33: cprt!
-:PROPERTIES:
-:CUSTOM_ID: opcode-cprt-write
-:ID: cfa8550e-bf8b-4d44-a3d1-9ba8ad183147
-:END:
-- *Stack Effect:* =byte port --=
-- *Description:* Writes a byte to the specified hardware I/O port.
-- *Notes:* Uses OUT instruction to write to the port. Consumes both the
- byte and port address.
-- *Example:*
- #+begin_example
- 0x42 ; Byte to write
- 0x60 ; Keyboard port
- cprt!
- #+end_example
-
-* 34: i2
-:PROPERTIES:
-:CUSTOM_ID: opcode-i2
-:ID: c3758859-c82e-40c1-9e92-87e5ef410cd2
-:END:
-- *Stack Effect:* =-- r2=
-- *Description:* Pushes the second value from the return stack onto the
- data stack.
-- *Notes:* Useful for accessing deeper items in the return stack without
- popping.
-- *Example:*
- #+begin_example
- 1 2
- push push
- i2
- #+end_example
- After execution, data stack contains 1.
-
-* 35: i3
-:PROPERTIES:
-:CUSTOM_ID: opcode-i3
-:ID: 304cadf7-9725-4e2d-8634-2e67b5ad49f3
-:END:
-- *Stack Effect:* =-- r3=
-- *Description:* Pushes the third value from the return stack onto the
- data stack.
-- *Notes:* Allows access to the third item in the return stack for
- deeper subroutine contexts.
-- *Example:*
- #+begin_example
- 1 2 3
- push push push
- i3
- #+end_example
- After execution, data stack contains 1.
-
-* 36: shl
-:PROPERTIES:
-:CUSTOM_ID: opcode-shl
-:ID: 978d1b75-3a1d-476f-8cdb-d3a6ee40b3f5
-:END:
-
-- *Stack Effect:* =value count -- result=
-- *Description:* Shifts =value= left by =count= bit positions. Each bit
- position shifted left multiplies the value by 2.
-- *Bits Shifted Out:* Bits that are shifted beyond the most
- significant bit (bit 31) are **discarded**.
-- *Count Masking:* Only the low byte of =count= is used. On x86
- processors, the actual shift amount is further masked to 5 bits
- (=count & 0x1F=), meaning the effective shift is always 0-31.
-
-
-*Corner Cases:*
-- =count = 0=: Value unchanged.
-- =count >= 32=: Due to 5-bit masking, the effective shift is
- =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.
-
-*Examples:*
-
-#+begin_example
-1 \ Value to shift
-2 \ Shift amount (count)
-shl
-#+end_example
-Result: 4 (0b0001 << 2 = 0b0100).
-
-#+begin_example
-FF \ Value: 255 (0x000000FF)
-4 \ Shift by 4
-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:
-:CUSTOM_ID: opcode-shr
-:ID: 9dbfcd07-6c34-41b7-b5dc-a33f0773e7cd
-:END:
-
-- *Stack Effect:* =value count -- result=
-- *Description:* Shifts =value= right by =count= bit positions. Each bit
- position shifted right divides the value by 2 (integer division).
-- *Bits Shifted Out:* Bits that are shifted beyond the least significant
- bit (bit 0) are **discarded**.
-- *Zero Fill:* This is a **logical shift right** (unsigned). Zeros are
- shifted in from the left (MSB side). This differs from an arithmetic
- shift right which would preserve the sign bit.
-- *Count Masking:* Only the low byte of =count= is used. On x86
- processors, the actual shift amount is further masked to 5 bits
- (=count & 0x1F=), meaning the effective shift is always 0-31.
-
-*Corner Cases:*
-- =count = 0=: Value unchanged.
-- =count >= 32=: Due to 5-bit masking, the effective shift is
- =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).
-
-*Examples:*
-#+begin_example
-8 \ Value to shift
-2 \ Shift amount (count)
-shr
-#+end_example
-Result: 2 (0b1000 >> 2 = 0b0010).
-
-#+begin_example
-1 \ Value: only bit 0 set
-1 \ Shift by 1
-shr
-#+end_example
-Result: 0. The single bit was shifted out and discarded.
-
-* 38: or
-:PROPERTIES:
-:CUSTOM_ID: opcode-or
-:ID: b38b971f-3a5f-4b16-9536-4193d2c5ae6b
-: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).
-- *Example:*
- #+begin_example
- 1 2
- or
- #+end_example
- After execution, stack contains 3 (0b01 | 0b10).
-
-* 39: xor
-:PROPERTIES:
-:CUSTOM_ID: opcode-xor
-:ID: e87a0d6d-89b0-4791-9500-654d60d99318
-: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).
-- *Example:*
- #+begin_example
- 1 2
- xor
- #+end_example
- After execution, stack contains 3 (0b01 ^ 0b10).
+++ /dev/null
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine opcodes 40 -- 49
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-
-* 40: vidmap
-:PROPERTIES:
-:CUSTOM_ID: opcode-vidmap
-:ID: fe47ff6d-768f-4b05-9f8c-f52352c106f4
-:END:
-Stack Footprint
-: addr --
-
-Copies memory from the specified address to the video memory (VGA or
-VESA mode). Used for displaying images directly on the screen.
-
-Parameters:
-- *addr* (32-bit): Source memory address containing the image data to
- copy to video memory.
-
-** Detailed Operation
-:PROPERTIES:
-:CUSTOM_ID: detailed-operation-4
-:END:
-1. *Video Memory Setup*:
- - Set video memory segment to =0xA000= (VGA standard segment for
- 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):
- - 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.
- - Use =STOSD= to write 32-bit words for efficiency.
- - The source address is incremented by 4KB after each page.
-4. *Final Cleanup*:
- - Restore original segments and clean up stack.
-
-** Example
-Copy a 640x480x8 image (307,200 bytes) from memory at =0x10000= to video
-memory:
-#+begin_example
-0x10000
-vidmap
-#+end_example
-
-** 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=.
-
-* 41: mouse@
-:PROPERTIES:
-:CUSTOM_ID: opcode-mouse
-:ID: b4bce653-d38f-45ed-82ec-13ca78c9860b
-:END:
-Stack Footprint:
-: -- x y buttons
-
-*Description:*
-
-Reads the current mouse position and button states from the mouse
-driver. Returns three values on the stack:
-- X-coordinate,
-- Y-coordinate,
-- 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).
- - *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)
-
-*Example:*
-
-#+begin_example
-mouse@
-#+end_example
-After execution, stack contains buttons (top), y, x (bottom).
-
-* 42: vidput - put image1 into image2, at location x, y
-:PROPERTIES:
-:CUSTOM_ID: opcode-vidput
-:ID: 238e8b03-57b6-424d-bfee-b6bb652cefbc
-:END:
-Stack Footprint:
-: 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
-support - every pixel is copied regardless of value.
-
-*Parameters:*
-
-- *addr1* (32-bit): Address of the source image structure (width,
- height, data address).
-- *addr2* (32-bit): Address of the destination image structure (width,
- height, data address).
-- *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
- starts.
-
-*Image Structure Format:*
-
-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)
-
-*Example:*
-#+begin_example
-0x1000 ; Source image structure
-0x2000 ; Destination image structure
-50 ; x
-50 ; y
-vidput
-#+end_example
-
-* 43: cmove - copy memory array
-:PROPERTIES:
-:CUSTOM_ID: opcode-cmove
-:ID: 79e1916f-4103-42cc-ac10-bb1ee776ed50
-:END:
-
-- *Stack Effect:*
-: addr1 addr2 len --
-
-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.
-- *Example:*
- #+begin_example
- 100 ; Length
- 0x2000 ; Destination address
- 0x1000 ; Source address
- cmove
- #+end_example
-
-* 44: cfill
-:PROPERTIES:
-:CUSTOM_ID: opcode-cfill
-:ID: fecb9438-d9d2-4224-a017-cc87dba9209e
-:END:
-- *Stack Effect:* =byte addr len --=
-- *Description:* Fills =len= bytes of memory starting at =addr= with the
- specified byte value.
-- *Notes:* Simple byte-by-byte filling operation. Used for initializing
- memory regions.
-- *Example:*
- #+begin_example
- 0xFF ; Fill byte
- 0x1000 ; Start address
- 100 ; Length
- cfill
- #+end_example
-
-* 45: tvidput - put image with transparency support
-:PROPERTIES:
-:CUSTOM_ID: opcode-tvidput
-:ID: ab45247c-44c3-464d-9e2a-337f483b4616
-:END:
-Stack Footprint:
-: 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* (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).
-- *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
- starts.
-
-*Image Structure Format:*
-
-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)
-
-*Example:*
-#+begin_example
-0x1000 ; Source image structure
-0x2000 ; Destination image structure
-50 ; x
-50 ; y
-tvidput
-#+end_example
-
-*Notes:*
-
-- Transparency is fixed at value 255 (0xFF).
-- The operation handles clipping automatically to ensure the copy stays
- within destination bounds.
-- Source and destination image structures must be properly formatted.
-
-* 46: depth
-:PROPERTIES:
-:CUSTOM_ID: opcode-depth
-:ID: 77fa76d3-9cd0-49c1-882c-f30383347352
-:END:
-- *Stack Effect:* =-- depth=
-- *Description:* Pushes the current depth (number of items) on the data
- stack onto the stack.
-- *Notes:* Calculated by comparing the data stack pointer to the base
- address of the stack.
-- *Example:*
- #+begin_example
- 1 2 3
- depth
- #+end_example
- After execution, stack contains 3 (depth) followed by 1 2 3.
-
-* 47: charput - draw text character
-:PROPERTIES:
-:CUSTOM_ID: opcode-charput
-:ID: 4bb479cf-aae0-4128-9868-f016c286a162
-:END:
-
-Stack Footprint:
-=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* (1 byte): Foreground color value (0-255). Typically a
- grayscale value where 0 is black and 255 is white.
-- *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.
-- *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
- starts.
-
-*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
-10 ; x
-charput
-#+end_example
-
-Character data for 'A' (ASCII 65):
-#+begin_example
-0x00, 0x18, 0x24, 0x24, 0x24, 0x3C, 0x24, 0x24
-#+end_example
-
-- Each byte represents a row:
- - Row 0: =0x00= → all pixels black
- - Row 1: =0x18= → bits 3-4 set (binary =00011000=)
- - Row 2: =0x24= → bits 2 and 5 set (binary =00100100=)
- - Row 3: =0x24= → same as row 2
- - Row 4: =0x24= → same as row 2
- - Row 5: =0x3C= → bits 2-5 set (binary =00111100=)
- - Row 6: =0x24= → same as row 2
- - Row 7: =0x24= → same as row 2
+++ /dev/null
-:PROPERTIES:
-:ID: 9b251eb9-aff6-4025-94bf-25e89e26d54a
-:END:
-#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
-#+TITLE: Fifth - virtual machine
-#+AUTHOR: Svjatoslav Agejenko
-#+LANGUAGE: en
-
-#+OPTIONS: H:20 num:20
-#+OPTIONS: author:nil
-
-#+begin_export html
-<style>
- .flex-center {
- display: flex; /* activate flexbox */
- justify-content: center; /* horizontally center anything inside */
- }
-
- .flex-center video {
- width: min(90%, 1000px); /* whichever is smaller wins */
- height: auto; /* preserve aspect ratio */
- }
-
- .responsive-img {
- width: min(100%, 1000px);
- height: auto;
- }
-</style>
-#+end_export
-
-
-* Overview
-
-Fifth is a hobby operating system built around a custom stack-based
-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
-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.
-
-* x86 Unreal Mode Execution
-
-The emulator runs as a standard DOS COM executable and uses x86
-real-mode assembly with a technique called "unreal mode" to access
-extended memory beyond the 640KB conventional DOS limit. In unreal
-mode, the CPU operates in real-mode (compatible with DOS) but with
-segment registers configured to allow access to the full 32-bit
-address space (up to 4GB).
-
-Key aspects:
-- Starts in real mode for DOS compatibility.
-- Switches temporarily to x86 protected mode to initialize segment
- registers for 4 GB RAM flat address space.
-- Returns to real mode while retaining 4 GB RAM access.
-- Uses XMS (Extended Memory Specification) for large memory allocation.
-
-** Memory Management and XMS
-
-The emulator uses DOS XMS (Extended Memory Specification) to allocate
-large blocks of extended memory:
-
-- **XMS Allocation**: Allocates the largest available block of extended
- memory using HIMEM.SYS
-- **Memory Locking**: Locks the allocated block to prevent swapping
-- **Address Mapping**: Uses xms_addr variable to store the linear
- address of allocated memory
-- **Virtual-to-Physical**: All virtual CPU addresses are offset by
- xms_addr to convert to physical memory addresses
-
-* Two-Stack Architecture
-
-The virtual CPU uses two separate stacks:
-
-- **Data Stack**: Main working stack for operations.
-- **Return Stack (variable resp)**: Stores return addresses for
- subroutine calls.
-
-* Graphics System
-
-The virtual machine integrates directly with VESA BIOS for graphics
-output. It uses VESA 640x480 8-bit color mode.
-
-Graphics operations are performed through dedicated VM instructions.
-
-* Boot Process
-
-1. DOS loads emulator.com as a COM executable
-2. system_init() allocates XMS memory and sets up A20 gate
-3. Sets VESA graphics mode (640x480, 8-bit color)
-4. Loads first kilobyte of kernel (core.raw) from disk into beginning
- of virtual RAM.
-5. Jumps to kernel entry point
-6. Kernel loads remaining 3 kibibytes of kernel bytecode into RAM. So
- initial bytecode part of the kernel must not exceed 4 kibibytes.
-7. Kernel loads high-level Fifth boot code into RAM.
-8. Kernel starts compiling additional modules to itself from
- high-level bytecode to gain filesystem support, dynamic RAM support
- and other essential functions.
-9. Additional modules (keyboard / mouse drivers) and interactive shell
- are now loaded from filesystem files.
-10. Enters interactive REPL
-
-* Implemented instructions
-
-Virtual CPU opcodes (most of them are available as directly executable commands
-in FIFTH programming language):
-
-
-| # | Name | Stack effect | Description | Notes |
-|----+----------+-----------------------+----------------------------------------------------------+---------------------------------------------------------------------|
-| 0 | [[file:opcodes_00_09.org::#opcode-nop][nop]] | -- | No operation; execution continues to next instruction | Used for padding or placeholder |
-| 1 | [[file:opcodes_00_09.org::#opcode-halt][halt]] | -- | Stop execution and exit emulator | Cleanly terminates the Fifth environment |
-| 2 | [[file:opcodes_00_09.org::#opcode-kbd-read][kbd@]] | -- scancode | Read keyboard scancode; returns 0 if no key pending | Make codes for press, break codes (value+128) for release |
-| 3 | [[file:opcodes_00_09.org::#opcode-num][num]] | -- n | Push 32-bit literal from instruction stream | Followed by 4 bytes (little-endian) |
-| 4 | [[file:opcodes_00_09.org::#opcode-jmp][jmp]] | -- | Unconditional jump to address from instruction stream | Followed by 4-byte target address |
-| 5 | [[file:opcodes_00_09.org::#opcode-call][call]] | -- | Call subroutine; push return address on return stack | Use ret (opcode 11) to return; follow with 4-byte target address |
-| 6 | [[file:opcodes_00_09.org::#opcode-1plus][1+]] | n -- n+1 | Increment top of data stack by 1 | Efficient single-byte operation for counters and pointers |
-| 7 | [[file:opcodes_00_09.org::#opcode-1minus][1-]] | n -- n-1 | Decrement top of data stack by 1 | Efficient single-byte operation for countdown loops |
-| 8 | [[file:opcodes_00_09.org::#opcode-dup][dup]] | n -- n n | Duplicate top of data stack | Preserve values before consuming operations |
-| 9 | [[file:opcodes_00_09.org::#opcode-drop][drop]] | n -- | Remove and discard top of data stack | Clean up stack after operations |
-| 10 | [[file:opcodes_10_19.org::#opcode-if][if]] | n -- | Jump to address if top of stack is zero | |
-| 11 | [[file:opcodes_10_19.org::#opcode-ret][ret]] | | Return from subroutine (pop return address) | |
-| 12 | [[file:opcodes_10_19.org::#opcode-cfetch][c@]] | addr -- byte | Read byte from memory at specified address | |
-| 13 | [[file:opcodes_10_19.org::#opcode-cstore][c!]] | byte addr -- | Store byte to specified memory address | |
-| 14 | [[file:opcodes_10_19.org::#opcode-push][push]] | n -- | Move top of data stack to return stack | |
-| 15 | [[file:opcodes_10_19.org::#opcode-pop][pop]] | -- n | Move top of return stack to data stack | |
-| 16 | <unused> | | | |
-| 17 | [[file:opcodes_10_19.org::#opcode-rot][rot]] | n1 n2 n3 -- n2 n3 n1 | Rotate top three stack elements | |
-| 18 | [[file:opcodes_10_19.org::#opcode-disk-read][disk@]] | sector addr -- | Read 1KB from disk into RAM at specified address | |
-| 19 | [[file:opcodes_10_19.org::#opcode-disk-write][disk!]] | addr sector -- | Write 1KB from RAM to disk at specified sector | |
-| 20 | [[file:opcodes_20_29.org::#opcode-fetch][@]] | addr -- n | Read 32-bit value from memory | |
-| 21 | [[file:opcodes_20_29.org::#opcode-store][!]] | n addr -- | Store 32-bit value to memory | |
-| 22 | [[file:opcodes_20_29.org::#opcode-over][over]] | n1 n2 -- n1 n2 n1 | Duplicate second item on data stack | |
-| 23 | [[file:opcodes_20_29.org::#opcode-swap][swap]] | n1 n2 -- n2 n1 | Swap top two items on data stack | |
-| 24 | [[file:opcodes_20_29.org::#opcode-add][+]] | n1 n2 -- n1+n2 | Add top two items on data stack | |
-| 25 | [[file:opcodes_20_29.org::#opcode-sub][-]] | n1 n2 -- n1-n2 | Subtract second item from top item on data stack | TODO: verify argument order |
-| 26 | [[file:opcodes_20_29.org::#opcode-mul][*]] | n1 n2 -- n1*n2 | Multiply top two items on data stack | |
-| 27 | [[file:opcodes_20_29.org::#opcode-div][/]] | n1 n2 -- n2/n1 | Divide top item by second item on data stack | TODO: verify argument order |
-| 28 | [[file:opcodes_20_29.org::#opcode-gt][>]] | n1 n2 -- result | Compare n1 > n2 (returns true, false otherwise) | TODO: document, what true and false means |
-| 29 | [[file:opcodes_20_29.org::#opcode-lt][<]] | n1 n2 -- result | Compare n1 < n2 (returns true, false otherwise) | TODO: document, what true and false means |
-| 30 | [[file:opcodes_30_39.org::#opcode-not][not]] | n -- ~n | Bitwise NOT on top of data stack | |
-| 31 | [[file:opcodes_30_39.org::#opcode-i][i]] | -- n | Push top of return stack to data stack | |
-| 32 | [[file:opcodes_30_39.org::#opcode-cprt-read][cprt@]] | port -- byte | Read byte from hardware I/O port | |
-| 33 | [[file:opcodes_30_39.org::#opcode-cprt-write][cprt!]] | byte port -- | Write byte to hardware I/O port | |
-| 34 | [[file:opcodes_30_39.org::#opcode-i2][i2]] | -- n | Push second item from return stack to data stack | |
-| 35 | [[file:opcodes_30_39.org::#opcode-i3][i3]] | -- n | Push third item from return stack to data stack | |
-| 36 | [[file:opcodes_30_39.org::#opcode-shl][shl]] | value count -- result | Left shift value by count bits; shifted-out bits discarded |
-| 37 | [[file:opcodes_30_39.org::#opcode-shr][shr]] | value count -- result | Logical right shift; zeros shifted in, shifted-out bits discarded |
-| 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 | |
-| 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 | |
-| 45 | [[file:opcodes_40_49.org::#opcode-tvidput][tvidput]] | addr1 addr2 x y -- | Blit image1 to image2 at (x,y) with transparency support | |
-| 46 | [[file:opcodes_40_49.org::#opcode-depth][depth]] | -- depth | Push current data stack depth | |
-| 47 | [[file:opcodes_40_49.org::#opcode-charput][charput]] | fg bg src dest x y -- | Draw character from source buffer to destination buffer |
-
-
-
-; Opcodes 20-29: @, !, over, swap, plus, minus, mul, div, great, less
+; Opcodes 20-29: Memory access, stack operations, arithmetic, and comparison
+;
+; This file implements opcodes 20-29 of the Fifth virtual machine:
+; 20: @ - Fetch 32-bit value from memory
+; 21: ! - Store 32-bit value to memory
+; 22: over - Duplicate second stack item
+; 23: swap - Exchange top two stack items
+; 24: + - Add two 32-bit values
+; 25: - - Subtract two 32-bit values
+; 26: * - Multiply two 32-bit values (signed)
+; 27: / - Divide two 32-bit values (signed)
+; 28: > - Greater than comparison
+; 29: < - Less than comparison
-op_20_x@: ; Fetch 32-bit: ( addr -- n )
+op_20_x@: ; Fetch 32-bit: ( addr -- value )
+; Reads a 32-bit value from the specified memory address and replaces
+; the address with the fetched value on the data stack.
+;
+; Stack behavior:
+; Before: [ addr ]
+; After: [ value ]
+;
+; Memory access:
+; - Takes virtual address from top of data stack
+; - Converts to physical address by adding xms_addr
+; - Reads 32-bit value from physical memory
+; - Replaces address with value on stack
+;
+; Notes:
+; - This is the 32-bit equivalent of c@ (opcode 16)
+; - 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
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 -- )
+op_21_x!: ; Store 32-bit: ( value addr -- )
+; Stores a 32-bit value to the specified memory address. Both the value
+; and address are consumed from the data stack.
+;
+; Stack behavior:
+; Before: [ value addr ]
+; After: [ ]
+;
+; Memory access:
+; - Takes address from top of stack (TOS)
+; - Takes value to store from second on stack
+; - Converts virtual address to physical address
+; - Stores 32-bit value to physical memory
+; - Removes both arguments from stack
+;
+; Notes:
+; - This is the 32-bit equivalent of c! (opcode 17)
+; - Virtual address translation provides memory protection
+; - 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)
add eax, [xms_addr] ; convert virtual address to physical address
mov ecx, [es:edi+4] ; ecx = value to store (second on stack)
jmp emu ; return to emulation loop
op_22_xover: ; Over: ( a b -- a b a )
+; Duplicates the second item on the data stack, placing a copy on top.
+; This allows access to the second item without disturbing the top item.
+;
+; Stack behavior:
+; Before: [ a b ] (b = TOS, a = second)
+; After: [ a b a ] (new a = TOS)
+;
+; Operation:
+; - Reads second item (a) from stack
+; - Grows stack by one element (decrement stack pointer)
+; - Writes copy of a to new top of stack
+;
+; Notes:
+; - Essential for preserving values while using the top item
+; - Often used with arithmetic operations that consume arguments
+; - More efficient than dup+swap+drop combination
+; - Increases stack depth by 1
+;
+; Common usage:
+; a b over -> a b a (can now use b while preserving 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 )
+; Exchanges the top two values on the data stack. This is essential for
+; reordering arguments when the natural stack order doesn't match the
+; required order for operations.
+;
+; Stack behavior:
+; Before: [ a b ] (b = TOS, a = second)
+; After: [ b a ] (a = TOS, b = second)
+;
+; Operation:
+; - Reads top item (b) into register
+; - Uses xchg to atomically exchange with second item (a)
+; - Writes result back to stack
+;
+; Notes:
+; - Fundamental stack manipulation operation
+; - Used extensively for argument reordering
+; - More efficient than multiple push/pop operations
+; - Stack depth remains unchanged
+;
+; Common usage:
+; a b swap -> b a (arguments now in correct order)
+; Often used before arithmetic operations to fix operand order
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 )
+; Adds the top two values on the data stack, replacing them with the sum.
+; This is a fundamental arithmetic operation for mathematical computations.
+;
+; Stack behavior:
+; Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+; After: [ sum ] (sum = n1 + n2)
+;
+; Operation:
+; - Reads top item (n2) into register
+; - Pops n2 off stack (n1 becomes new TOS)
+; - Adds n2 to n1 in place (n1 = n1 + n2)
+;
+; Notes:
+; - Performs unsigned 32-bit addition
+; - Overflow wraps around modulo 2^32
+; - Operates in place for efficiency (no extra stack slot needed)
+; - Reduces stack depth by 1
+;
+; Common usage:
+; 5 3 + -> 8
+; addr 4 + -> addr+4 (pointer arithmetic)
+;
+; Edge cases:
+; - 0xFFFFFFFF + 1 = 0 (wrap around)
+; - Any number + 0 = same number
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_26_xmul: ; Multiply (signed): ( n1 n2 -- n1*n2 )
+; Multiplies the top two values on the data stack using signed 32-bit
+; multiplication. The result is the low 32 bits of the product.
+;
+; Stack behavior:
+; Before: [ n1 n2 ] (n2 = TOS, n1 = second)
+; After: [ product ] (product = n1 * n2)
+;
+; Operation:
+; - Reads top item (n2) into eax
+; - Pops n2 off stack (n1 becomes new TOS)
+; - Clears edx (required for signed multiply)
+; - Performs signed multiply: edx:eax = eax * n1
+; - Stores low 32 bits (eax) as result
+;
+; Notes:
+; - Uses IMUL instruction (signed multiplication)
+; - Result is low 32 bits only (high bits in edx are discarded)
+; - Handles both positive and negative numbers correctly
+; - Overflow behavior: result wraps around modulo 2^32
+; - Reduces stack depth by 1
+;
+; Common usage:
+; 5 3 * -> 15
+; -2 4 * -> -8
+;
+; Edge cases:
+; - 0 * anything = 0
+; - Large products may overflow (wrap around)
+; - Division by zero not possible here
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
jmp emu ; return to emulation loop
op_27_xdiv: ; Divide (signed): ( n1 n2 -- n1/n2 )
+; Divides the second value by the top value using signed 32-bit division.
+; Returns the quotient only (remainder is discarded).
+;
+; Stack behavior:
+; Before: [ n1 n2 ] (n2 = divisor = TOS, n1 = dividend = second)
+; After: [ quotient ] (quotient = n1 / n2)
+;
+; Operation:
+; - Pops divisor (n2) off stack (dividend n1 becomes TOS)
+; - Reads dividend (n1) into eax
+; - Sign-extends eax into edx:eax (required for signed division)
+; - Performs signed division: eax = edx:eax / divisor
+; - Stores quotient (eax) as result
+;
+; Notes:
+; - Uses IDIV instruction (signed division)
+; - Returns quotient only (remainder discarded)
+; - Handles both positive and negative numbers correctly
+; - Division by zero causes CPU exception (not handled)
+; - Reduces stack depth by 1
+;
+; Common usage:
+; 15 3 / -> 5
+; -8 4 / -> -2
+;
+; Edge cases:
+; - Division by zero: CPU exception (crash)
+; - Negative division: follows standard signed division rules
+; - Remainder is discarded (use mod operation if needed)
+;
+; WARNING: Division by zero will crash the emulator!
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
jmp emu ; return to emulation loop
op_28_xgreat: ; Greater Than: ( a b -- flag )
+; Compares the second value (a) with the top value (b). Returns -1 (true)
+; if a > b, otherwise returns 0 (false). This is a fundamental comparison
+; operation for conditional logic.
+;
+; Stack behavior:
+; Before: [ a b ] (b = TOS, a = second)
+; After: [ flag ] (flag = -1 if a > b, 0 otherwise)
+;
+; Operation:
+; - Reads top item (b) into eax
+; - Pops b off stack (a becomes new TOS)
+; - Sets edx = 0 (false flag)
+; - Compares a with b
+; - If a > b, sets edx = -1 (true flag)
+; - Stores result flag on stack
+;
+; Notes:
+; - Returns -1 for true (all bits set), 0 for false
+; - Standard Forth truth value convention
+; - Signed comparison (works for negative numbers)
+; - Reduces stack depth by 1
+;
+; Truth value convention:
+; - -1 = true (all bits set, non-zero)
+; - 0 = false (zero)
+;
+; Common usage:
+; 5 3 > -> 0 (false, 5 is not greater than 3)
+; 3 5 > -> -1 (true, 3 is greater than 5)
+;
+; Edge cases:
+; - Equal values return 0 (false)
+; - Negative numbers compared correctly
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)
jmp emu ; return to emulation loop
op_29_xless: ; Less Than: ( a b -- flag )
+; Compares the second value (a) with the top value (b). Returns -1 (true)
+; if a < b, otherwise returns 0 (false). This is the complement of the
+; greater than operation and essential for conditional logic.
+;
+; Stack behavior:
+; Before: [ a b ] (b = TOS, a = second)
+; After: [ flag ] (flag = -1 if a < b, 0 otherwise)
+;
+; Operation:
+; - Reads top item (b) into eax
+; - Pops b off stack (a becomes new TOS)
+; - Sets edx = 0 (false flag)
+; - Compares a with b
+; - If a < b, sets edx = -1 (true flag)
+; - Stores result flag on stack
+;
+; Notes:
+; - Returns -1 for true (all bits set), 0 for false
+; - Standard Forth truth value convention
+; - Signed comparison (works for negative numbers)
+; - Reduces stack depth by 1
+; - Complementary to > (greater than) operation
+;
+; Truth value convention:
+; - -1 = true (all bits set, non-zero)
+; - 0 = false (zero)
+;
+; Common usage:
+; 3 5 < -> -1 (true, 3 is less than 5)
+; 5 3 < -> 0 (false, 5 is not less than 3)
+;
+; Edge cases:
+; - Equal values return 0 (false)
+; - Negative numbers compared correctly
+;
+; Relationship to other comparisons:
+; a b < is equivalent to b a >
+; a b <> (not equal) can be implemented as: over over <> = drop
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)