From: Svjatoslav Agejenko Date: Sat, 10 Oct 2026 18:44:53 +0000 (+0300) Subject: docs: rename doc/ to Documentation/, add incremental org export X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=32c4cc3e5078d359a0dc50846d57d25589efae6a;p=fifth.git docs: rename doc/ to Documentation/, add incremental org export - Rename doc/ to Documentation/ via git mv (history preserved) to match the house convention; update AGENTS.md references accordingly. - "Tools/Update web site" now exports via the shared incremental exporter (svjatoslav_commons/Tools/Org export incremental): only changed Documentation/*.org pages are re-exported, in one Emacs batch process; .org-export-state added to .gitignore. - Expand opcodes 20-29 documentation: per-opcode descriptions, stack effects, notes and examples in Documentation/opcodes_20_29.org, mirrored as comment blocks in emulator/opcodes_20_29.asm. --- diff --git a/.gitignore b/.gitignore index 90a65f0..3646344 100755 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ bin/ .aider* *.html + +# Incremental org export cache (Tools/Update web site) +.org-export-state diff --git a/AGENTS.md b/AGENTS.md index 35e5a23..e0496ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -110,10 +110,10 @@ Write standard ASCII when editing; conversion tools handle encoding. # 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 @@ -122,7 +122,7 @@ emulator/ # x86 real-mode assembly (FASM) 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 ``` @@ -134,7 +134,7 @@ 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 diff --git a/Documentation/5TH_ET.txt b/Documentation/5TH_ET.txt new file mode 100644 index 0000000..7fde517 --- /dev/null +++ b/Documentation/5TH_ET.txt @@ -0,0 +1,269 @@ +.( loading editor ) update +.( done ) +.( ETh - for text editor help ) + +var handletxt +var handlebuf +Dstralloc const editfile +var shiftx var shifty +var curlocx var curlocy +Dstr \listF\TXT_ET" tmphelpfile + +: disppage ( -- ) +handletxt @ +dynp shifty @ 50 * + +0 1 at! +1270 type ; + +: fclose ( close opened file ) +handletxt @ dup if dynde handletxt off else drop then ; + +: ETnewpage fclose 27100 dynal dup handletxt ! dynp +FF swap 27100 cfill ; + +var tmp1 +: fopen ( strhand -- ) +fclose +dup editfile Dstr2Dstr fsDloadnew dup push dynp ( addrinp R: handinp ) +27100 dynal dup handletxt ! dynp ( addrinp addrout R: handinp ) +FF over 27100 cfill +tmp1 off +i dyns do ( addrinp addrout ) +over c@ ( addrinp addrout c ) +dup FE = if + drop + 50 tmp1 @ - + tmp1 off +else + dup FD = if + drop tmp1 @ tab ( addrinp addrout numspaces ) + tmp1 @ over + tmp1 ! + 0 + else + over c! 1+ 0 + tmp1 @ 1+ tmp1 ! + then + drop +then +swap 1+ swap +loop 2drop +pop dynde ; rh tmp1 + +: dispchar ( x y -- ) +2dup +swap shiftx @ - +swap shifty @ - 1+ +at! +50 * + handletxt @ dynp + c@ emit ; + +: dispcur curlocx @ curlocy @ dispchar ; + +: displine ( line ) +0 over shifty @ - 1+ at! +50 * handletxt @ dynp + shiftx @ + 50 type ; + +: dispstatbar +colneg +0 0 at! 50 do space loop +0 0 at! ." col:" curlocx @ . +7 0 at! ." line:" curlocy @ . +11 0 at! ." ascii:" curlocy @ 50 * curlocx @ + handletxt @ dynp + c@ . +1B 0 at! ." file:" editfile Dstr. +49 0 at! ." F1-help" +colnorm ; + +: charins ( char x y ) +50 * handletxt @ dynp + ( char x addr ) +over push ++ dup dup 1+ 4F pop - cmove ( char addr ) +c! ; + +: charcut ( x y -- char ) +over push +50 * handletxt @ dynp + + +dup c@ swap ( char addr ) +dup dup 1+ swap ( char addr addr+1 addr ) +4F pop - dup push cmove ( char addr ) +pop + FF swap c! ; + +: lineins ( line ) +50 * dup push handletxt @ dynp + ( addr ) +dup dup 50 + handletxt @ dyns 50 - pop - cmove ( addr ) +FF swap 50 cfill ; + +: linecut ( line ) +50 * dup push handletxt @ dynp + ( addr ) +dup 50 + swap handletxt @ dyns 50 - pop - cmove ; + +: screenaim +0 curlocy @ 1E - 794 bound shifty ! +disppage ; + +var tmp1 +: length ( line -- length ) +50 * 4F + handletxt @ dynp + ( addr ) +51 do + i tmp1 ! + dup c@ FF - if pop drop 0 push then + 1- +loop +drop +tmp1 @ ; rh tmp1 + +var tmp2 var tmp3 +: fsavechar ( char ) tmp2 @ swap over c! 1+ tmp2 ! ; +: fsave ( -- ) +27200 dynal dup dynp tmp2 ! +0 +7CF do + i length if drop pop 1+ 0 push then +loop +0 swap for ( desthand ) + tmp3 off + i 50 * handletxt @ dynp + + 0 i length for ( desthand srcaddr ) + dup c@ + i tab 8 = if tmp3 @ if FD fsavechar tmp3 off then then + dup FF = if + drop tmp3 @ 1+ tmp3 ! + else + tmp3 @ do FF fsavechar loop tmp3 off + fsavechar + then + 1+ + loop drop + FE fsavechar +loop +tmp2 @ over dynp - over dynresize +dup editfile fsDsave +dynde ; rh tmp2 rh tmp3 + +str TXT_default" tmpdefault + +: ET +handletxt @ if +disppage +until + colneg dispcur dispstatbar KBD_FW@ colnorm dispcur + + dup 415 = if ( DEL ) + curlocx @ curlocy @ charcut drop + curlocy @ displine + then + + dup FC = if ( BACKSPACE ) + curlocx @ if + curlocx @ 1- curlocy @ charcut drop + curlocy @ displine + drop 413 + else + curlocy @ if + curlocy @ 1- dup length dup curlocx ! ( y-1 strlen ) + swap 50 * handletxt @ dynp + ( strlen y1addr ) + 2dup + swap 50 + ( strlen dest src ) + swap rot 50 swap - cmove + curlocy @ linecut + drop 410 + disppage + then + then + then + + dup FD = if ( TAB ) + curlocx @ tab + dup do FF curlocx @ curlocy @ charins loop + curlocx @ + curlocx ! + curlocy @ displine + then + + dup FE = if ( ENTER ) + curlocy @ 7CF < if + curlocy @ 1+ lineins + curlocx @ 0 + 50 curlocx @ - do + over curlocy @ charcut + over curlocy @ 1+ charins + 1+ + loop 2drop + curlocx off + drop 412 + disppage + then + then + + dup 418 = if curlocy @ 3A - curlocy ! then ( PG-UP ) + dup 419 = if curlocy @ 3A + curlocy ! then ( PG-DOWN ) + dup 416 = if curlocx off then ( HOME ) + dup 417 = if curlocy @ length curlocx ! then ( END ) + dup 401 = if tmphelpfile fsdisp disppage then ( F1 ) + dup 402 = if fsave then ( F2 ) + dup 403 = if fclose drop 1B then ( F3 ) + dup 400 = if done 0 0 at! screen cls drop -1 then ( ESC ) + + 0 over F0 bound? if -1 else 0 then ( NORMAL KEY ) + over FF = if drop -1 then + if + curlocx @ curlocy @ charins + curlocy @ displine + 411 + then + + dup 411 = if curlocx @ 1+ curlocx ! then ( RIGHT ) + dup 413 = if curlocx @ 1- curlocx ! then ( LEFT ) + dup 412 = if curlocy @ 1+ curlocy ! then ( DOWN ) + dup 410 = if curlocy @ 1- curlocy ! then ( UP ) + drop + + 0 curlocx @ 4F bound curlocx ! + 0 curlocy @ 7CF bound curlocy ! + + curlocy @ shifty @ - 3B > if screenaim then + + curlocy @ shifty @ - 3B - 0 = if + 0 0 8 screen scrollf + shifty @ 1+ shifty ! + curlocy @ displine + then + + curlocy @ shifty @ - -1 < if screenaim then + + curlocy @ shifty @ - 1+ 0 = if + 0 0 -8 screen scrollf + shifty @ 1- shifty ! + curlocy @ displine + then + +loop +else + tmpdefault editfile str2Dstr + ETnewpage + ET +then ; + +: ETl +fopen +curlocx off +curlocy off +shiftx off +shifty off +ET ; + +: ETs +editfile Dstr2Dstr +fsave ." saved " ; + +: ETh tmphelpfile fs. ; + +rh handletxt +rh handlebuf +rh editfile +rh shiftx rh shifty +rh curlocx rh curlocy +rh tmphelpfile +rh disppage +rh fclose rh fopen +rh dispchar rh dispcur +rh displine rh dispstatbar +rh charins rh charcut +rh lineins rh linecut +rh screenaim rh length +rh fsavechar rh fsave +rh ETnewpage rh tmpdefault diff --git a/Documentation/index.org b/Documentation/index.org new file mode 100644 index 0000000..fedd6a9 --- /dev/null +++ b/Documentation/index.org @@ -0,0 +1,359 @@ +#+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 + +#+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. diff --git a/Documentation/language.org b/Documentation/language.org new file mode 100644 index 0000000..39073ce --- /dev/null +++ b/Documentation/language.org @@ -0,0 +1,860 @@ +: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 + +#+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 ( -- ) + +- Description :: Compiles new dictionary entry without specifying new + module type. +- Example :: + : head myentry + +*** : ( -- ) + +- Description :: Creates new code module. + +*** ; ( -- ) + +- Description :: Ends module (immideate). +- Example :: + : : hello ." hi there" ; + +*** const ( n -- ) + +- Description :: Defines new constant. +- Example :: + : 2147483647 const max + +*** :i ( -- ) + +- Description :: Same as =:= but this module will be executed + immideately even in compile mode. +- Example :: + : :i ( 41 scan ; + +*** create ( -- ) + +- 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 + +*** " " ( -- ) + +- Description :: Compile string and its size into core. +- Example :: + : create Mystring " This is it's contects" + +*** str " ( -- ) + +- Description :: Just shorter way for defining strings. +- Example :: + : str Mystring This is it's contenc" + +*** var ( -- ) + +- Description :: Define new 32 bit variable. +- Example :: + : var result + +*** ' ( -- n ) + +- Description :: Return memory address of given entry. +- Example :: + : ' init + +*** forget ( -- ) + +- 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 ( -- ) + +- 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 ( -- ) + +- 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 -- ) + +- 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 DynStrHand -- ) + +- Description :: Create new file and save all data from dynamic memory + block to it. + +*** fsDload ( DynStr DynHand -- ) + +- Description :: Load whole file into dynamic memory block. + +*** fsDloadnew ( DynStr -- DynHand ) + +- 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*. + +*** ." " ( -- ) + +- 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 + : + : + : + : mystring1 Df ; \ deallocates it again when no longer needed. diff --git a/Documentation/numbers.png b/Documentation/numbers.png new file mode 100644 index 0000000..0ccd07b Binary files /dev/null and b/Documentation/numbers.png differ diff --git a/Documentation/opcodes_00_09.org b/Documentation/opcodes_00_09.org new file mode 100644 index 0000000..ddc530e --- /dev/null +++ b/Documentation/opcodes_00_09.org @@ -0,0 +1,628 @@ +#+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 + +#+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) diff --git a/Documentation/opcodes_10_19.org b/Documentation/opcodes_10_19.org new file mode 100644 index 0000000..336b62b --- /dev/null +++ b/Documentation/opcodes_10_19.org @@ -0,0 +1,504 @@ +#+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 + +#+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: +: #2: +: #3: +: #4: + + +*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 | | +| 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) diff --git a/Documentation/opcodes_20_29.org b/Documentation/opcodes_20_29.org new file mode 100644 index 0000000..35adc9b --- /dev/null +++ b/Documentation/opcodes_20_29.org @@ -0,0 +1,953 @@ +#+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 + +#+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 diff --git a/Documentation/opcodes_30_39.org b/Documentation/opcodes_30_39.org new file mode 100644 index 0000000..3d87fda --- /dev/null +++ b/Documentation/opcodes_30_39.org @@ -0,0 +1,240 @@ +#+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 + +#+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). diff --git a/Documentation/opcodes_40_49.org b/Documentation/opcodes_40_49.org new file mode 100644 index 0000000..1186994 --- /dev/null +++ b/Documentation/opcodes_40_49.org @@ -0,0 +1,314 @@ +#+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 + +#+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 diff --git a/Documentation/screenshots/dictionary.png b/Documentation/screenshots/dictionary.png new file mode 100644 index 0000000..73909b0 Binary files /dev/null and b/Documentation/screenshots/dictionary.png differ diff --git a/Documentation/screenshots/start.png b/Documentation/screenshots/start.png new file mode 100644 index 0000000..15b5c40 Binary files /dev/null and b/Documentation/screenshots/start.png differ diff --git a/Documentation/screenshots/text editor.png b/Documentation/screenshots/text editor.png new file mode 100644 index 0000000..153bf90 Binary files /dev/null and b/Documentation/screenshots/text editor.png differ diff --git a/Documentation/virtual machine.org b/Documentation/virtual machine.org new file mode 100644 index 0000000..4078fc4 --- /dev/null +++ b/Documentation/virtual machine.org @@ -0,0 +1,165 @@ +: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 + +#+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 | | | | | +| 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 | + + + diff --git a/Tools/Update web site b/Tools/Update web site index d7d5fd7..10b9574 100755 --- a/Tools/Update web site +++ b/Tools/Update web site @@ -2,66 +2,17 @@ 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!" diff --git a/doc/5TH_ET.txt b/doc/5TH_ET.txt deleted file mode 100644 index 7fde517..0000000 --- a/doc/5TH_ET.txt +++ /dev/null @@ -1,269 +0,0 @@ -.( loading editor ) update -.( done ) -.( ETh - for text editor help ) - -var handletxt -var handlebuf -Dstralloc const editfile -var shiftx var shifty -var curlocx var curlocy -Dstr \listF\TXT_ET" tmphelpfile - -: disppage ( -- ) -handletxt @ -dynp shifty @ 50 * + -0 1 at! -1270 type ; - -: fclose ( close opened file ) -handletxt @ dup if dynde handletxt off else drop then ; - -: ETnewpage fclose 27100 dynal dup handletxt ! dynp -FF swap 27100 cfill ; - -var tmp1 -: fopen ( strhand -- ) -fclose -dup editfile Dstr2Dstr fsDloadnew dup push dynp ( addrinp R: handinp ) -27100 dynal dup handletxt ! dynp ( addrinp addrout R: handinp ) -FF over 27100 cfill -tmp1 off -i dyns do ( addrinp addrout ) -over c@ ( addrinp addrout c ) -dup FE = if - drop - 50 tmp1 @ - + tmp1 off -else - dup FD = if - drop tmp1 @ tab ( addrinp addrout numspaces ) - tmp1 @ over + tmp1 ! + 0 - else - over c! 1+ 0 - tmp1 @ 1+ tmp1 ! - then - drop -then -swap 1+ swap -loop 2drop -pop dynde ; rh tmp1 - -: dispchar ( x y -- ) -2dup -swap shiftx @ - -swap shifty @ - 1+ -at! -50 * + handletxt @ dynp + c@ emit ; - -: dispcur curlocx @ curlocy @ dispchar ; - -: displine ( line ) -0 over shifty @ - 1+ at! -50 * handletxt @ dynp + shiftx @ + 50 type ; - -: dispstatbar -colneg -0 0 at! 50 do space loop -0 0 at! ." col:" curlocx @ . -7 0 at! ." line:" curlocy @ . -11 0 at! ." ascii:" curlocy @ 50 * curlocx @ + handletxt @ dynp + c@ . -1B 0 at! ." file:" editfile Dstr. -49 0 at! ." F1-help" -colnorm ; - -: charins ( char x y ) -50 * handletxt @ dynp + ( char x addr ) -over push -+ dup dup 1+ 4F pop - cmove ( char addr ) -c! ; - -: charcut ( x y -- char ) -over push -50 * handletxt @ dynp + + -dup c@ swap ( char addr ) -dup dup 1+ swap ( char addr addr+1 addr ) -4F pop - dup push cmove ( char addr ) -pop + FF swap c! ; - -: lineins ( line ) -50 * dup push handletxt @ dynp + ( addr ) -dup dup 50 + handletxt @ dyns 50 - pop - cmove ( addr ) -FF swap 50 cfill ; - -: linecut ( line ) -50 * dup push handletxt @ dynp + ( addr ) -dup 50 + swap handletxt @ dyns 50 - pop - cmove ; - -: screenaim -0 curlocy @ 1E - 794 bound shifty ! -disppage ; - -var tmp1 -: length ( line -- length ) -50 * 4F + handletxt @ dynp + ( addr ) -51 do - i tmp1 ! - dup c@ FF - if pop drop 0 push then - 1- -loop -drop -tmp1 @ ; rh tmp1 - -var tmp2 var tmp3 -: fsavechar ( char ) tmp2 @ swap over c! 1+ tmp2 ! ; -: fsave ( -- ) -27200 dynal dup dynp tmp2 ! -0 -7CF do - i length if drop pop 1+ 0 push then -loop -0 swap for ( desthand ) - tmp3 off - i 50 * handletxt @ dynp + - 0 i length for ( desthand srcaddr ) - dup c@ - i tab 8 = if tmp3 @ if FD fsavechar tmp3 off then then - dup FF = if - drop tmp3 @ 1+ tmp3 ! - else - tmp3 @ do FF fsavechar loop tmp3 off - fsavechar - then - 1+ - loop drop - FE fsavechar -loop -tmp2 @ over dynp - over dynresize -dup editfile fsDsave -dynde ; rh tmp2 rh tmp3 - -str TXT_default" tmpdefault - -: ET -handletxt @ if -disppage -until - colneg dispcur dispstatbar KBD_FW@ colnorm dispcur - - dup 415 = if ( DEL ) - curlocx @ curlocy @ charcut drop - curlocy @ displine - then - - dup FC = if ( BACKSPACE ) - curlocx @ if - curlocx @ 1- curlocy @ charcut drop - curlocy @ displine - drop 413 - else - curlocy @ if - curlocy @ 1- dup length dup curlocx ! ( y-1 strlen ) - swap 50 * handletxt @ dynp + ( strlen y1addr ) - 2dup + swap 50 + ( strlen dest src ) - swap rot 50 swap - cmove - curlocy @ linecut - drop 410 - disppage - then - then - then - - dup FD = if ( TAB ) - curlocx @ tab - dup do FF curlocx @ curlocy @ charins loop - curlocx @ + curlocx ! - curlocy @ displine - then - - dup FE = if ( ENTER ) - curlocy @ 7CF < if - curlocy @ 1+ lineins - curlocx @ 0 - 50 curlocx @ - do - over curlocy @ charcut - over curlocy @ 1+ charins - 1+ - loop 2drop - curlocx off - drop 412 - disppage - then - then - - dup 418 = if curlocy @ 3A - curlocy ! then ( PG-UP ) - dup 419 = if curlocy @ 3A + curlocy ! then ( PG-DOWN ) - dup 416 = if curlocx off then ( HOME ) - dup 417 = if curlocy @ length curlocx ! then ( END ) - dup 401 = if tmphelpfile fsdisp disppage then ( F1 ) - dup 402 = if fsave then ( F2 ) - dup 403 = if fclose drop 1B then ( F3 ) - dup 400 = if done 0 0 at! screen cls drop -1 then ( ESC ) - - 0 over F0 bound? if -1 else 0 then ( NORMAL KEY ) - over FF = if drop -1 then - if - curlocx @ curlocy @ charins - curlocy @ displine - 411 - then - - dup 411 = if curlocx @ 1+ curlocx ! then ( RIGHT ) - dup 413 = if curlocx @ 1- curlocx ! then ( LEFT ) - dup 412 = if curlocy @ 1+ curlocy ! then ( DOWN ) - dup 410 = if curlocy @ 1- curlocy ! then ( UP ) - drop - - 0 curlocx @ 4F bound curlocx ! - 0 curlocy @ 7CF bound curlocy ! - - curlocy @ shifty @ - 3B > if screenaim then - - curlocy @ shifty @ - 3B - 0 = if - 0 0 8 screen scrollf - shifty @ 1+ shifty ! - curlocy @ displine - then - - curlocy @ shifty @ - -1 < if screenaim then - - curlocy @ shifty @ - 1+ 0 = if - 0 0 -8 screen scrollf - shifty @ 1- shifty ! - curlocy @ displine - then - -loop -else - tmpdefault editfile str2Dstr - ETnewpage - ET -then ; - -: ETl -fopen -curlocx off -curlocy off -shiftx off -shifty off -ET ; - -: ETs -editfile Dstr2Dstr -fsave ." saved " ; - -: ETh tmphelpfile fs. ; - -rh handletxt -rh handlebuf -rh editfile -rh shiftx rh shifty -rh curlocx rh curlocy -rh tmphelpfile -rh disppage -rh fclose rh fopen -rh dispchar rh dispcur -rh displine rh dispstatbar -rh charins rh charcut -rh lineins rh linecut -rh screenaim rh length -rh fsavechar rh fsave -rh ETnewpage rh tmpdefault diff --git a/doc/index.org b/doc/index.org deleted file mode 100644 index fedd6a9..0000000 --- a/doc/index.org +++ /dev/null @@ -1,359 +0,0 @@ -#+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 - -#+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. diff --git a/doc/language.org b/doc/language.org deleted file mode 100644 index 39073ce..0000000 --- a/doc/language.org +++ /dev/null @@ -1,860 +0,0 @@ -: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 - -#+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 ( -- ) - -- Description :: Compiles new dictionary entry without specifying new - module type. -- Example :: - : head myentry - -*** : ( -- ) - -- Description :: Creates new code module. - -*** ; ( -- ) - -- Description :: Ends module (immideate). -- Example :: - : : hello ." hi there" ; - -*** const ( n -- ) - -- Description :: Defines new constant. -- Example :: - : 2147483647 const max - -*** :i ( -- ) - -- Description :: Same as =:= but this module will be executed - immideately even in compile mode. -- Example :: - : :i ( 41 scan ; - -*** create ( -- ) - -- 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 - -*** " " ( -- ) - -- Description :: Compile string and its size into core. -- Example :: - : create Mystring " This is it's contects" - -*** str " ( -- ) - -- Description :: Just shorter way for defining strings. -- Example :: - : str Mystring This is it's contenc" - -*** var ( -- ) - -- Description :: Define new 32 bit variable. -- Example :: - : var result - -*** ' ( -- n ) - -- Description :: Return memory address of given entry. -- Example :: - : ' init - -*** forget ( -- ) - -- 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 ( -- ) - -- 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 ( -- ) - -- 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 -- ) - -- 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 DynStrHand -- ) - -- Description :: Create new file and save all data from dynamic memory - block to it. - -*** fsDload ( DynStr DynHand -- ) - -- Description :: Load whole file into dynamic memory block. - -*** fsDloadnew ( DynStr -- DynHand ) - -- 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*. - -*** ." " ( -- ) - -- 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 - : - : - : - : mystring1 Df ; \ deallocates it again when no longer needed. diff --git a/doc/numbers.png b/doc/numbers.png deleted file mode 100644 index 0ccd07b..0000000 Binary files a/doc/numbers.png and /dev/null differ diff --git a/doc/opcodes_00_09.org b/doc/opcodes_00_09.org deleted file mode 100644 index ddc530e..0000000 --- a/doc/opcodes_00_09.org +++ /dev/null @@ -1,628 +0,0 @@ -#+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 - -#+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) diff --git a/doc/opcodes_10_19.org b/doc/opcodes_10_19.org deleted file mode 100644 index 336b62b..0000000 --- a/doc/opcodes_10_19.org +++ /dev/null @@ -1,504 +0,0 @@ -#+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 - -#+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: -: #2: -: #3: -: #4: - - -*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 | | -| 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) diff --git a/doc/opcodes_20_29.org b/doc/opcodes_20_29.org deleted file mode 100644 index 14905b8..0000000 --- a/doc/opcodes_20_29.org +++ /dev/null @@ -1,194 +0,0 @@ -#+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 - -#+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). diff --git a/doc/opcodes_30_39.org b/doc/opcodes_30_39.org deleted file mode 100644 index 3d87fda..0000000 --- a/doc/opcodes_30_39.org +++ /dev/null @@ -1,240 +0,0 @@ -#+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 - -#+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). diff --git a/doc/opcodes_40_49.org b/doc/opcodes_40_49.org deleted file mode 100644 index 1186994..0000000 --- a/doc/opcodes_40_49.org +++ /dev/null @@ -1,314 +0,0 @@ -#+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 - -#+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 diff --git a/doc/screenshots/dictionary.png b/doc/screenshots/dictionary.png deleted file mode 100644 index 73909b0..0000000 Binary files a/doc/screenshots/dictionary.png and /dev/null differ diff --git a/doc/screenshots/start.png b/doc/screenshots/start.png deleted file mode 100644 index 15b5c40..0000000 Binary files a/doc/screenshots/start.png and /dev/null differ diff --git a/doc/screenshots/text editor.png b/doc/screenshots/text editor.png deleted file mode 100644 index 153bf90..0000000 Binary files a/doc/screenshots/text editor.png and /dev/null differ diff --git a/doc/virtual machine.org b/doc/virtual machine.org deleted file mode 100644 index 4078fc4..0000000 --- a/doc/virtual machine.org +++ /dev/null @@ -1,165 +0,0 @@ -: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 - -#+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 | | | | | -| 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 | - - - diff --git a/emulator/opcodes_20_29.asm b/emulator/opcodes_20_29.asm index 03aba5b..b92a840 100644 --- a/emulator/opcodes_20_29.asm +++ b/emulator/opcodes_20_29.asm @@ -1,13 +1,61 @@ -; 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) @@ -16,18 +64,85 @@ op_21_x!: ; Store 32-bit: ( n addr -- ) 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) @@ -59,6 +174,35 @@ op_25_xminus: ; Subtract: ( n1 n2 -- n1-n2 ) 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 @@ -67,6 +211,37 @@ op_26_xmul: ; Multiply (signed): ( n1 n2 -- n1*n2 ) 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 @@ -75,6 +250,39 @@ op_27_xdiv: ; Divide (signed): ( n1 n2 -- n1/n2 ) 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) @@ -85,6 +293,44 @@ l3: mov [es:edi], edx ; store flag as new top of stack jmp emu ; return to emulation loop op_29_xless: ; Less Than: ( a b -- flag ) +; 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)