{"id":25531909,"url":"https://github.com/robert-van-engelen/forth850","last_synced_at":"2025-04-11T11:35:08.635Z","repository":{"id":63012235,"uuid":"564478966","full_name":"Robert-van-Engelen/Forth850","owner":"Robert-van-Engelen","description":"A fast Forth Standard system written in Z80 assembly for SHARP PC-G850 pocket computers","archived":false,"fork":false,"pushed_at":"2024-02-19T03:45:35.000Z","size":1955,"stargazers_count":25,"open_issues_count":0,"forks_count":3,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-25T08:01:31.639Z","etag":null,"topics":["forth","forth-2012","minifloat","pc-g850","pocketcomputer","single-precision-floating-point","z80","z80asm"],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Robert-van-Engelen.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-11-10T20:07:05.000Z","updated_at":"2025-02-10T16:33:45.000Z","dependencies_parsed_at":"2024-02-19T04:46:51.082Z","dependency_job_id":null,"html_url":"https://github.com/Robert-van-Engelen/Forth850","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Robert-van-Engelen%2FForth850","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Robert-van-Engelen%2FForth850/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Robert-van-Engelen%2FForth850/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Robert-van-Engelen%2FForth850/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Robert-van-Engelen","download_url":"https://codeload.github.com/Robert-van-Engelen/Forth850/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248384528,"owners_count":21094733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["forth","forth-2012","minifloat","pc-g850","pocketcomputer","single-precision-floating-point","z80","z80asm"],"created_at":"2025-02-20T01:42:12.212Z","updated_at":"2025-04-11T11:35:08.610Z","avatar_url":"https://github.com/Robert-van-Engelen.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n![PC-G850](PC-G850.jpg)\n\nA modern Forth 2012 standard compliant system for the vintage SHARP\nPC-G850(V)(S) pocket computer or any Z80 system (with a few tweaks to port).\n\nForth850 is under 8K and has [295 words](forth850-words).\n\nA more complete 11K version forth850-full is also included with:\n\n- [58 additional words](#additional-words-included-with-the-full-version)\n\n- single precision floating point math words implemented with a new and\nefficient [Z80 IEEE 754 floating point math](#z80-floating-point-math-routines)\nlibrary I wrote for Forth850.  More floating point math functions that use this\nlibrary are defined in Forth in [examples/MATH.FTH](examples/MATH.FTH)\n\n- a more capable Forth line editor with replay back feature (cursor keys)\n\nForth850 includes stack under/overflow checks, dictionary overflow checks and\ncan be interrupted by pressing BREAK.\n\nYou can write Forth source code in the PC-G850(V)(S) built-in TEXT editor and\ncompile it into Forth850 with the [TEXT](examples/TEXT.FTH) word included in\nthe full version.\n\nYou can extend Forth850 as you wish, including assembly code written on the\nmachine itself in the PC-G850(V)(S) TEXT editor and assembled with its Z80\nAssembler.  See [ASMDEMO1.FTH](examples/ASMDEMO1.FTH) for an example with an\nexplanation.  You can also use the Monitor to set breakpoints and run Forth850\nfrom the Monitor with `G100` to trigger them.  \n\nIf you want to rebuild Forth850 from source code, you will need to install\nthe [asz80](https://shop-pdp.net/ashtml/asz80.htm) assembler part of the\n[ASxxxx Cross Assemblers](https://shop-pdp.net/ashtml/asxget.php).\n\nIf you plan to use parts of Forth850 and/or the optimized Z80 code in a project\nthat you plan to share or redistribute, then please give me credit for my work\nas per [BCD-3 license](license.txt).\n\nDiscuss: [HP Forum](https://www.hpmuseum.org/forum/thread-19085.html) and\n[reddit](https://www.reddit.com/r/Forth/comments/ytat79/a_fast_forth_for_vintage_sharp_pcg850_pocket)\n\n## Performance\n\nI've implemented Forth850 as efficiently as possible in direct threaded code\n(DTC) with new Z80 code written from scratch, including faster Z80 integer and\nfloat math routines compared to other Z80 Forth implementations.  See the\n[technical implementation sections](#Implementation) why Forth850 is fast for a\nDTC implementation.  The [Forth850 source code](forth850.asm) is included and\nextensively documented.\n\nThe [n-queens benchmark](https://www.hpmuseum.org/cgi-sys/cgiwrap/hpmuseum/articles.cgi?read=700)\nis solved in 0.865 seconds, the fastest Forth implementation of the benchmarks.\nForth850 n-queens runs 5 times faster than the C n-queens benchmark on the\nSharp PC-G850VS.\n\n## Installation\n\nIn RUN MODE enter `MON` to enter the Monitor, then enter `USER3FFF` to reserve\n16K memory space:\n\n    *USER3FFF\n    FREE:0100-3FFF\n\nLoading via SIO (serial) requires a serial adapter.  See my post on the\n[HP Forum](https://www.hpmuseum.org/forum/thread-19431-post-169250.html#pid169250)\nhow to construct one as a DIY project.  After reserving memory in the Monitor\nas described above, use the `R` command to read the forth850.ihx file or the\nforth850-full.ihx full version sent from your PC to your PC-G850(V)(S):\n\n    *R100\n\nThe `R` command is used to transmit/receive data in Intel hex format over SIO.\nThis command is for receiving machine code from a personal computer or other\ndevice.  See the [Sharp PC-G850(V)(S) manual](https://pockemul.com/wp-content/uploads/2020/05/PC-G850VSEng_V3_0.pdf).\n\u003c!-- https://www.dropbox.com/s/02p3ahwlsbgkf21/Sharp%20PC-G850V%28S%29%20%28Eng%29%20V3.0.pdf?dl=0 --\u003e\n\nTo load via the cassette interface, press `BASIC` to return to RUN MODE.  Load\nforth850.wav using a cassette interface CE-126P or a CE-124:\n\n    BLOADM\n\nLoad the forth850-full.wav \"full version\" to include many\n[additional words](#additional-words-included-with-the-full-version) and\n[floating point words](#floating-point-math-words-included-with-the-full-version).\nThe full version will continue to evolve with new features.\n\n## How to switch between Forth and BASIC\n\nTo return to Forth, enter `CALL256` in RUN MODE.\n\nTo return to BASIC from Forth, press the BASIC key.  The TEXT key takes you to\nthe TEXT editor.\n\nTo turn the machine off, press the OFF key.  The machine will also power off\nautomatically after about 10 minutes waiting for user input at the prompt.\n\n## How to increase or decrease memory allocation for Forth850\n\nMemory allocation can be adjusted without affecting the Forth dictionary.\n\nIn RUN MODE enter `MON` to enter the Monitor, then enter `USERaddr` with an\nupper address `addr` larger than `23ff` (9K bytes.)  If words are added to\nForth850, you must make sure that `addr` is large enough, i.e. equal or larger\nthan the hex value displayed with:\n\n    HERE #708 + HEX . DECIMAL\n    23FF\n\nIn the Monitor specify `USERaddr` with the address displayed.  This leaves\nabout 200 bytes free dictionary space plus 40 bytes for the \"hold area\" to run\nForth850.  The largest size is `USER75FF` which gives about 21K free dictionary\nspace (but there won't be space left on the machine for files, BASIC or TEXT.)\n\n## Forth850 manual\n\nForth850 is 2012 standard compliant.  For help, see the manual included with\n[Forth for the Sharp PC-E500(S)](https://github.com/Robert-van-Engelen/Forth500)\nand [Forth 2012 Standard](https://forth-standard.org/standard/intro).\n\nForth850 implements a subset of the standard Forth words.  A list of Forth850\nwords with an explanation is given below.\n\n## Forth850 words\n\nList of Forth850 built-in words.  Reference implementations in Forth are\nincluded when applicable, although many words are implemented in Z80 code for\nspeed rather than in Forth.\n\n### (:)\n_-- ; R: -- ip_\ncall colon definition;\nruntime of the : compile-only word\n\n### (;)\n_-- ; R: ip --_\nreturn to caller from colon definition;\nruntime of the ; compile-only word\n\n### (EXIT)\n_-- ; R: ip --_\nreturn to caller from colon definition;\nruntime of the EXIT compile-only word\n\n### (;CODE)\n_-- ; R: ip --_\nset LASTXT cfa to ip and return from colon definition;\na runtime word compiled by the DOES\u003e compile-only word\n\n### (DOES)\n_addr -- addr ; R: -- ip_\ncalls the DOES\u003e definition with pfa addr;\na runtime word compiled by the DOES\u003e compile-only word coded as call dodoes\n\n### (VAR)\n_-- addr_\nleave parameter field address (pfa) of variable;\nruntime word of a VARIABLE coded as call dovar\n\n### (VAL)\n_-- x_\nfetch value;\nruntime word of a VALUE coded as call doval\n\n### (2VAL)\n_-- dx_\nfetch double value;\nruntime word of a 2VALUE coded as call dotwoval\n\n### (CON)\n_-- x_\nfetch constant;\nruntime word of a CONSTANT coded as call docon\n\n### (2CON)\n_-- x_\nfetch double constant;\nruntime word of a 2CONSTANT coded as call dotwocon\n\n### (DEF)\n_--_\nexecute deferred word;\nruntime word of a DEFER coded as call dodef\n\n### (LIT)\n_-- x_\nfetch literal;\nruntime word compiled by EVALUATE, INTERPRET and NUMBER\n\n### (2LIT)\n_-- x1 x2_\nfetch double literal;\nruntime word compiled by EVALUATE, INTERPRET and NUMBER\n\n### (SLIT)\n_-- c-addr u_\nfetch literal string;\nruntime word compiled by S\" and .\"\n\n### 0\n_-- 0_\nleave constant 0\n\n    0 CONSTANT 0\n\n### 1\n_-- 1_\nleave constant 1\n\n    1 CONSTANT 1\n\n### -1\n_-- -1_\nleave constant -1\n\n    -1 CONSTANT -1\n\n### BL\n_-- 32_\nleave constant 32 (space)\n\n    #32 CONSTANT BL\n\n### PAD\n_-- c-addr_\nleave address of the PAD;\nthe PAD is a free buffer space of 256 bytes not used by Forth850\n\n### TIB\n_-- c-addr_\nleave address of TIB;\nthe terminal input buffer used by Forth850\n\n### TMP\n_-- c-addr_\nleave address of the next temp string buffer;\nswitches between two string buffers of 256 free bytes each;\nused by S\" to store a string when interpreting\n\n### DROP\n_x --_\ndrop TOS\n\n### DUP\n_x -- x x_\nduplicate TOS\n\n### ?DUP\n_x -- x x or 0 -- 0_\nduplicate TOS if nonzero\n\n### SWAP\n_x1 x2 -- x2 x1_\nswap TOS with 2OS\n\n### OVER\n_x1 x2 -- x1 x2 x1_\ncopy 2OS over TOS\n\n### ROT\n_x1 x2 x3 -- x2 x3 x1_\nrotate cells\n\n    : ROT \u003eR SWAP R\u003e SWAP ;\n\n### -ROT\n_x1 x2 x3 -- x3 x1 x2_\nundo (or back, or left) rotate cells\n\n    : -ROT ROT ROT ;\n\n### NIP\n_x1 x2 -- x2_\nnip 2OS\n\n    : NIP SWAP DROP ;\n\n### TUCK\n_x1 x2 -- x2 x1 x2_\ntuck TOS under 2OS\n\n    : TUCK SWAP OVER ;\n\n### 2DROP\n_xd1 xd2 -- xd1_\ndrop double TOS\n\n    : 2DROP DROP DROP ;\n\n### 2DUP\n_xd -- xd xd_\nduplicate double TOS\n\n    : 2DUP OVER OVER ;\n\n### 2SWAP\n_xd1 xd2 -- xd2 xd1_\nswap double TOS with double 2OS\n\n    : 2SWAP ROT \u003eR ROT R\u003e ;\n    : 2SWAP 3 ROLL 3 ROLL ;\n\n### 2OVER\n_xd1 xd2 -- xd1 xd2 xd1_\ncopy double 2OS over double TOS\n\n    : 2OVER \u003eR \u003eR 2DUP R\u003e R\u003e 2SWAP ;\n    : 2OVER 3 PICK 3 PICK ;\n\n### DEPTH\n_-- u_\nparameter stack depth\n\n    : DEPTH sp0 @ SP@ - 2- 2/ ;\n\n### CLEAR\n_... --_\npurge parameter stack\n\n    : CLEAR sp0 @ SP! ;\n\n### .S\n_--_\ndisplay parameter stack\n\n    : .S DEPTH 0 ?DO sp0 @ I 2+ CELLS - ? LOOP ;\n\n### SP@\n_-- addr_\nfetch stack pointer\n\n### SP!\n_addr --_\nstore stack pointer\n\n### \u003eR\n_x -- ; R: -- x_\nmove TOS to the return stack\n\n### DUP\u003eR\n_x -- x ; R: -- x_\nduplicate TOS to the return stack, a single word for DUP \u003eR\n\n### R\u003e\n_R: x -- ; -- x_\nmove cell from the return stack\n\n### RDROP\n_R: x -- ; --_\ndrop cell from the return stack, a single word for R\u003e DROP\n\n### R@\n_R: x -- x ; -- x_\nfetch cell from the return stack\n\n### 2\u003eR\n_x1 x2 -- ; R: -- x1 x2_\nmove double TOS to the return stack, a single word for SWAP \u003eR \u003eR\n\n### 2R\u003e\n_R: x1 x2 -- ; -- x1 x2_\nmove double cell from the return stack, a single word for R\u003e R\u003e SWAP\n\n### 2R@\n_R: x1 x2 -- x1 x2 ; -- x1 x2_\nfetch double cell from the return stack\n\n### RP@\n_-- addr_\nfetch return stack pointer\n\n### RP!\n_addr --_\nstore return stack pointer\n\n### PICK\n_xu ... x0 u -- xu ... x0 xu_\npick u'th cell from the parameter stack;\n0 PICK is the same as DUP;\n1 PICK is the same as OVER\n\n    : PICK 1+ CELLS SP@ + @ ;\n\n### @\n_addr -- x_\nfetch from cell\n\n### C@\n_c-addr -- char_\nfetch char\n\n### 2@\n_addr -- x1 x2_\nfetch from double cell\n\n    : 2@ DUP CELL+ @ SWAP @ ;\n\n### !\n_x addr --_\nstore in cell\n\n### (TO)\n_x --_\nstore in value;\nruntime of the TO compile-only word\n\n### C!\n_char c-addr --_\nstore char\n\n### 2!\n_x1 x2 addr --_\nstore in double cell\n\n    : 2! TUCK ! CELL+ ! ;\n\n### (2TO)\n_dx --_\nstore in double value;\nruntime of the TO compile-only word\n\n### +!\n_n addr --_\nincrement cell\n\n### (+TO)\n_n --_\nincrement value;\nruntime of the +TO compile-only word\n\n### ON\n_addr --_\nstore TRUE (-1) in cell\n\n    : ON -1 SWAP ! ;\n\n### OFF\n_addr --_\nstore FALSE (0) in cell\n\n    : OFF 0 SWAP ! ;\n\n### +\n_n1 n2 -- n3_\nsum n1+n2\n\n### M+\n_d1 n -- d2_\ndouble sum d1+n\n\n### D+\n_d1 d2 -- d3_\ndouble sum d1+d2\n\n    : D+ \u003eR M+ R\u003e + ;\n\n### -\n_n1 n2 -- n3_\ndifference n1-n2\n\n### D-\n_d1 d2 -- d3_\ndouble difference d1-d2\n\n    : D- DNEGATE D+ ;\n\n### UM*\n_u1 u2 -- ud_\nunsigned double product u1*u2\n\n### M*\n_n1 n2 -- d_\nsigned double product n1*n2\n\n    : M*\n      2DUP XOR \u003eR\n      ABS SWAP ABS UM*\n      R\u003e 0\u003c IF DNEGATE THEN ;\n\n### *\n_n1|u1 n2|u2 -- n3|u3_\nsigned and unsigned product n1*n2\n\n    : * UM* DROP ;\n\n### UMD*\n_ud1 u -- ud2_\nunsigned double product ud1*u\n\n    : UMD* DUP\u003eR UM* DROP SWAP R\u003e UM* ROT + ;\n\n### MD*\n_d1 n -- d2_\nsigned double product d1*n\n\n    : MD*\n      2DUP XOR \u003eR\n      ABS -ROT DABS ROT\n      UMD*\n      R\u003e 0\u003c IF DNEGATE THEN ;\n\n### UM/MOD\n_ud u1 -- u2 u3_\nunsigned remainder and quotient ud/u1;\nthe result is undefined when u1 = 0\n\n### SM/REM\n_d1 n1 -- n2 n3_\nsymmetric remainder and quotient d1/n1 rounded towards zero;\nthe result is undefined when n1 = 0\n\n    : SM/REM\n      2DUP XOR \u003eR\n      OVER \u003eR\n      ABS -ROT DABS ROT\n      UM/MOD\n      R\u003e 0\u003c IF SWAP NEGATE SWAP THEN\n      R\u003e 0\u003c IF NEGATE THEN ;\n\n### FM/MOD\n_d1 n1 -- n2 n3_\nfloored signed modulus and quotient d1/n1 rounded towards negative (floored);\nthe result is undefined when n1 = 0\n\n    : FM/MOD\n      DUP\u003eR\n      SM/REM\n      DUP 0\u003c IF\n        SWAP R\u003e + SWAP 1-\n      ELSE\n        RDROP\n      THEN ;\n\n### /MOD\n_n1 n2 -- n3 n4_\nsymmetric remainder and quotient n1/n2;\nthe result is undefined when n2 = 0\n\n    : /MOD SWAP S\u003eD ROT SM/REM ;\n\n### MOD\n_n1 n2 -- n3_\nsymmetric remainder of n1/n2;\nthe result is undefined when n2 = 0\n\n    : MOD /MOD DROP ;\n\n### /\n_n1 n2 -- n3_\nquotient n1/n2;\nthe result is undefined when n2 = 0\n\n    : / /MOD NIP ;\n\n### */MOD\n_n1 n2 n3 -- n4 n5_\nproduct with symmetric remainder and quotient n1*n2/n3;\nthe result is undefined when n3 = 0\n\n    : */MOD -ROT M* ROT SM/REM ;\n\n### */\n_n1 n2 n3 -- n4_\nproduct with quotient n1*n2/n3;\nthe result is undefined when n3 = 0\n\n    : */ */MOD NIP ;\n\n### M*/\n_d1 n1 n2 -- d2_\ndouble product with quotient d1*n1/n2;\nthe result is undefined when n2 = 0\n\n    : M*/ \u003eR MD* R\u003e SM/REM NIP ;\n\n### AND\n_x1 x2 -- x1\u0026x2_\nbitwise and x1 with x2\n\n### OR\n_x1 x2 -- x1|x2_\nbitwise or x1 with x2\n\n### XOR\n_x1 x2 -- x1^x2_\nbitwise xor x1 with x2\n\n### =\n_x1 x2 -- flag_\ntrue if x1 = x2\n\n### \u003c\u003e\n_x1 x2 -- flag_\ntrue if x1 \u003c\u003e x2\n\n### \u003c\n_n1 n2 -- flag_\ntrue if n1 \u003c n2 signed\n\n    : \u003c\n      2DUP XOR 0\u003c IF\n        DROP 0\u003c\n        EXIT\n      THEN\n      - 0\u003c ;\n\n### \u003e\n_n1 n2 -- flag_\ntrue if n1 \u003e n2 signed\n\n    : \u003e SWAP \u003c ;\n\n### U\u003c\n_u1 u2 -- flag_\ntrue if u1 \u003c u2 unsigned\n\n    : U\u003c\n      2DUP XOR 0\u003c IF\n        NIP 0\u003c\n        EXIT\n      THEN\n      - 0\u003c ;\n\n### U\u003e\n_u1 u2 -- flag_\ntrue if u1 \u003e u2 unsigned\n\n    : U\u003e SWAP U\u003c ;\n\n### 0=\n_x -- flag_\ntrue if x = 0\n\n### 0\u003c\n_n -- flag_\ntrue if n \u003c 0\n\n### D0=\n_dx -- flag_\ntrue if dx = 0\n\n    : D0= OR 0= ;\n\n### D0\u003c\n_d -- flag_\ntrue if d \u003c 0\n\n    : D0\u003c NIP 0\u003c ;\n\n### S\u003eD\n_n -- d_\nwiden single to double\n\n### D\u003eS\n_d -- n_\nnarrow double to single;\nmay throw -11 \"result out of range\" valid range is -32768 to 65535\n\n### MAX\n_n1 n2 -- n3_\nsigned max of n1 and n2\n\n    : MAX\n      2DUP \u003c IF SWAP THEN\n      DROP ;\n\n### MIN\n_n1 n2 -- n3_\nsigned min of n1 and n2\n\n    : MIN\n      2DUP \u003e IF SWAP THEN\n      DROP ;\n\n### UMAX\n_u1 u2 -- u3_\nunsigned max of u1 and u2\n\n    : UMAX\n      2DUP U\u003c IF SWAP THEN\n      DROP ;\n\n### UMIN\n_u1 u2 -- u3_\nunsigned min of u1 and u2\n\n    : UMIN\n      2DUP U\u003e IF SWAP THEN\n      DROP ;\n\n### WITHIN\n_x1 x2 x3 -- flag_\ntrue if x1 is within x2 up to x3 exclusive\n\n    : WITHIN OVER - \u003eR - R\u003e U\u003c ;\n\n### INVERT\n_x1 -- x2_\none's complement ~x1\n\n    : INVERT 1+ NEGATE ;\n    : INVERT -1 XOR ;\n\n### NEGATE\n_n1 -- n2_\ntwo's complement -n1\n\n    : NEGATE 0 SWAP - ;\n    : NEGATE INVERT 1+ ;\n\n### ABS\n_n1 -- n2_\nabsolute value |n1|\n\n    : ABS DUP 0\u003c IF NEGATE THEN ;\n\n### DNEGATE\n_d1 -- d2_\ntwo's complement -d1\n\n    : DNEGATE SWAP INVERT SWAP INVERT 1 M+ ;\n\n### DABS\n_d1 -- d2_\nabsolute value |d1|\n\n    : DABS DUP 0\u003c IF DNEGATE THEN ;\n\n### LSHIFT\n_x1 u -- x2_\nlogical shift left x1 \u003c\u003c u\n\n### RSHIFT\n_x1 u -- x2_\nlogical shift right x1 \u003e\u003e u\n\n### 1+\n_n1 -- n2_\nincrement n1+1\n\n    : 1+ 1 + ;\n\n### 2+\n_n1 -- n2_\nincrement n1+2\n\n    : 2+ 2 + ;\n\n### 1-\n_n1 -- n2_\ndecrement n1-1\n\n    : 1- 1 - ;\n\n### 2-\n_n1 -- n2_\ndecrement n1-2\n\n    : 2- 2 - ;\n\n### 2*\n_n1 -- n2_\narithmetic shift left n1 \u003c\u003c 1\n\n    : 2* 2 * ;\n\n### 2/\n_n1 -- n2_\narithmetic shift right n1 \u003e\u003e 1\n\n    : 2/ 2 / ;\n\n### COUNT\n_c-addr1 -- c-addr2 u_\nconvert counted string to string\n\n    : COUNT DUP 1+ SWAP C@ ;\n\n### COMPARE\n_c-addr1 u1 c-addr2 u2 -- -1|0|1_\ncompare strings, leaves -1 = less or 0 = equal or 1 = greater\n\n### S=\n_c-addr1 u1 c-addr2 u2 -- flag_\ntrue if strings match\n\n    : S= COMPARE 0= ;\n\n### SEARCH\n_c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag_\ntrue if the second string is in the first;\nleaves matching address, remaining length and true;\nor leaves the first string and false\n\n### CMOVE\n_c-addr1 c-addr2 u --_\nmove u bytes from c-addr1 to c-addr2 (from begin)\n\n    : CMOVE\n      SWAP \u003eR\n      BEGIN DUP WHILE\n        NEXT-CHAR R@ C!\n        R\u003e 1+ \u003eR\n      REPEAT\n      RDROP\n      2DROP ;\n\n### CMOVE\u003e\n_c-addr1 c-addr2 u --_\nmove u bytes from c-addr1 to c-addr2 up (from end)\n\n### MOVE\n_c-addr1 c-addr2 u --_\nmove u bytes from c-addr1 to c-addr2\n\n    : MOVE\n      -ROT\n      2DUP U\u003c IF\n        ROT CMOVE\u003e\n      ELSE\n        ROT CMOVE\n      THEN ;\n\n### FILL\n_c-addr u char --_\nfill memory with char\n\n### ERASE\n_c-addr u --_\nfill memory with zeros\n\n    : ERASE 0 FILL ;\n\n### BLANK\n_c-addr u --_\nfill memory with 0x20 (BL) chars\n\n    : ERASE BL FILL ;\n\n### CHOP\n_c-addr u1 char -- c-addr u2_\ntruncate a string up to a matching char;\nleaves the string if char not found;\nchar = 0x20 (BL) chops 0x00 to 0x20 (white space and control)\n\n### TRIM\n_c-addr1 u1 char -- c-addr2 u2_\ntrim initial chars from a string;\nchar = 0x20 (BL) trims 0x00 to 0x20 (white space and control)\n\n### -TRIM\n_c-addr u1 char -- c-addr u2_\ntrim trailing chars from a string;\nchar = 0x20 (BL) trims 0x00 to 0x20 (white space and control)\n\n### -TRAILING\n_c-addr u1 -- c-addr u2_\ntrim trailing white space and control characters from a string\n\n    : -TRAILING BL -TRIM ;\n\n### /STRING\n_c-addr1 u1 n -- c-addr2 u2_\nslice n characters off the start of a string\n\n    : /STRING ROT OVER + -ROT - ;\n\n### NEXT-CHAR\n_c-addr1 u1 -- c-addr2 u2 char_\nget next char from a string;\nincrements the string address and decrements its length by one\n\n    : NEXT-CHAR OVER C@ \u003eR 1- SWAP 1+ SWAP R\u003e ;\n    : NEXT-CHAR OVER C@ -ROT 1- SWAP 1+ SWAP ROT ;\n\n### X!\n_u --_\nset cursor column 0 to 23\n\n### Y!\n_u --_\nset cursor row 0 to 5\n\n### X@\n_-- u_\nfetch cursor column 0 to 23, or 24 when beyond the right window edge\n\n### Y@\n_-- u_\nfetch cursor row 0 to 5\n\n### AT-XY\n_u1 u2 --_\nset column x to u1 (0 to 23) and row y to u2 (0 to 5)\n\n    : AT-XY Y! X! ;\n\n### EMIT\n_char --_\nemit char to screen;\nsupports the following control codes:\n 8 (BS backspace, cursor left),\n 9 (TAB),\n10 (LF line feed),\n11 (VT scroll),\n12 (FF clear screen),\n13 (CR carriage return),\n28 (cursor right),\n29 (cursor left),\n30 (cursor up),\n31 (cursor down)\n\n### TYPE\n_c-addr u --_\ntype string to output; string may contain control codes, see EMIT\n\n    : TYPE\n      BEGIN DUP WHILE\n        NEXT-CHAR EMIT\n      REPEAT\n      2DROP ;\n\n### CR\n_--_\ncarriage return and line feed\n\n    : CR $A EMIT ;\n\n### SPACE\n_--_\nemit a space (BL)\n\n    : SPACE BL EMIT ;\n\n### SPACES\n_n --_\nemit n spaces (zero or negative n does nothing)\n\n    : SPACES\n      DUP 0\u003c IF\n        DROP\n        EXIT\n      THEN\n      0 ?DO SPACE LOOP ;\n\n### PAGE\n_--_\nclear screen\n\n    : PAGE $C EMIT ;\n\n### BASE\n_-- addr_\nvariable with numeric base for conversion\n\n    VARIABLE BASE\n\n### DECIMAL\n_--_\nset BASE to 10\n\n    : DECIMAL #10 BASE ! ;\n\n### HEX\n_--_\nset BASE to 16\n\n    : HEX #16 BASE ! ;\n\n### HP\n_-- addr_\nhold pointer\n\n    0 VALUE HP\n\n### \u003c#\n_--_\nbegin pictured numeric output\n\n    : \u003c# HERE h_size + TO HP ;\n\n### HOLD\n_char --_\nhold char for pictured numeric output\n\n    : HOLD HP 1- DUP TO HP C! ;\n\n### #\n_ud1 -- ud2_\nhold digit\n\n    : #\n      0 BASE @ UM/MOD \u003eR\n      BASE @ UM/MOD\n      SWAP DUP #9 \u003e IF\n        #7 +\n      THEN\n      '0 + HOLD\n      R\u003e ;\n\n### #S\n_ud -- 0 0_\nhold all remaining digits\n\n    : #S BEGIN # 2DUP D0= UNTIL ;\n\n### SIGN\n_n --_\nhold minus sign if n \u003c 0\n\n    : SIGN 0\u003c IF '- HOLD THEN ;\n\n### #\u003e\n_ud -- c-addr u_\nend pictured numeric output, leave string\n\n    : #\u003e 2DROP HP HERE h_size + OVER - ;\n\n### D.R\n_d +n --_\noutput signed double d right aligned in field of +n chars wide\n\n    : D.R -ROT TUCK DABS \u003c# #S ROT SIGN #\u003e ROT OVER - SPACES TYPE ;\n\n### D.\n_d --_\noutput signed double d with a trailing space\n\n    : D. 0 D.R SPACE ;\n\n### U.R\n_u +n --_\noutput unsigned u right aligned in field of +n chars wide\n\n    : U.R 0 SWAP D.R ;\n\n### U.\n_u --_\noutput unsigned u with a trailing space\n\n    : U. 0 D. ;\n\n### .R\n_n +n --_\noutput signed n right aligned in field of +n chars wide\n\n    : .R SWAP S\u003eD ROT D.R ;\n\n### .\n_n --_\noutput signed n with a trailing space\n\n    : . S\u003eD D. ;\n\n### ?\n_addr --_\noutput signed cell stored at addr\n\n    : ? @ . ;\n\n### OUT\n_u1 u2 --_\noutput byte u1 to port u2\n\n### INP\n_u1 -- u2_\ninput from port u1\n\n### DRAW\n_c-addr u --_\ndraw pixel patterns on screen at xy;\nwrites string c-addr u of pixel patterns at xy;\nspecify xy with AT-XY, xy not changed after DRAW\n\n### VIEW\n_c-addr u --_\nview screen pixels at xy;\nread string of screen pixel patterns at xy into buffer c-addr u\nspecify xy with AT-XY, xy not changed after VIEW\n\n### REVERSE\n_+n --_\nreverse video of the +n characters displayed at xy;\nspecify xy with AT-XY\n\n### INKEY\n_-- x_\ncheck for key press and read key code of a key is pressed;\n0x00 = no key pressed and 0x52 = multiple keys pressed\n\n### GETKEY\n_-- char_\nwait and read key;\nleaves ASCII char or special key code:\nON      =$05,\nBS      =$08,\nDEL     =$09,\nCA      =$0b,\nCLS     =$0c,\nENTER   =$0d,\nDIGIT   =$0e,\nF-E     =$0f,\nINS     =$12,\nANS     =$15,\nCONST   =$17,\nRCM     =$19,\nM+      =$1a,\nM-      =$1b,\nright   =$1c,\nleft    =$1d,\nup      =$1e,\ndown    =$1f;\na space is produced for the TAB key by the GETCHR system call,\ncalc keys and BASIC keys produce BASIC tokens as key code $fe:\nSIN     =$fe register B = $95 BASIC token for SIN (ignored)\n\n### KEY\n_-- char_\ndisplay cursor and wait to read key;\nsame as GETKEY leaves ASCII char or special key code\n\n### EDIT\n_c-addr +n1 n2 n3 n4 -- c-addr +n5_\nedit buffer c-addr;\nbuffer size +n1;\nstring in buffer has length n2;\nplace cursor at n3;\nnon-editable left margin n4;\nleaves c-addr and length +n5\n\n### ACCEPT\n_c-addr +n1 -- +n2_\naccept user input into buffer c-addr +n1;\nleaves length +n2\n\n    : ACCEPT 0 0 0 EDIT NIP ;\n\n### \u003eIN\n_-- addr_\nvariable with offset into input buffer (TIB)\n\n    VARIABLE \u003eIN\n\n### SOURCE-ID\n_-- 0|-1_\nvalue with 0 = source input or -1 = string input\n\n    0 VALUE SOURCE-ID\n\n### SOURCE\n_-- c-addr u_\ndouble value with input source\n\n    TIB 0 2VALUE SOURCE\n\n### REFILL\n_-- flag_\nattempt to refill the input buffer;\nleaves false when end of input\n\n### SKIPS\n_char \"\u003cchars\u003e\" --_\nskips chars in input when present, 0x20 (BL) skips 0x00 to 0x20 (white space and control)\n\n    : SKIPS SOURCE \u003eIN @ /STRING ROT TRIM DROP SOURCE DROP - \u003eIN ! ;\n\n### PARSE\n_char \"ccc\u003cchar\u003e\" -- c-addr u_\nparse \"ccc\" up to char when present\n\n    : PARSE SOURCE \u003eIN @ /STRING ROT CHOP DUP 1+ \u003eIN @ + SOURCE NIP UMIN \u003eIN ! ;\n\n### PARSE-WORD\n_char \"\u003cchars\u003eccc\u003cchar\u003e\" -- c-addr u_\nparse char-delimited word;\nmay throw -18 \"parsed string overflow\"\n\n    : PARSE-WORD\n      DUP SKIPS PARSE\n      DUP tmp_size-1 U\u003e IF -18 THROW THEN ;\n\n### CHECK-NAME\n_c-addr u -- c-addr u_\ncheck if name is valid;\nmay throw -16 \"attempt to use a zero-length string as a name\";\nmay throw -19 \"definition name too long\"\n\n    : CHECK-NAME\n      DUP 0= IF -16 THROW THEN\n      DUP length_bits U\u003e IF -19 THROW THEN ;\n\n### PARSE-NAME\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- c-addr u_\nparse space-delimited name;\ncheck if name length is valid\n\n    : PARSE-NAME BL PARSE-WORD CHECK-NAME ;\n\n### (\n_\"ccc\u003cparen\u003e\" --_\nstart a comment block;\nparse and skip input up to the closing )\n\n    : (\n      ') PARSE\n      BEGIN\n        + DROP\n        SOURCE + = IF\n          DROP REFILL\n        ELSE\n          C@ ') \u003c\u003e IF\n            REFILL\n          ELSE\n            FALSE\n          THEN\n        THEN\n      0= UNTIL ; IMMEDIATE\n\n### \\\n_\"ccc\u003ceol\u003e\" --_\nstart a comment line;\nparse and skip input up to the end of line;\nnote that the PC-G850 symbol for \\ is ¥\n\n    : \\ $A PARSE 2SROP ;\n\n### .(\n_\"ccc\u003cparen\u003e\" --_\nemit CR then type \"ccc\" up to the closing )\n\n    : .( ') PARSE CR TYPE ; IMMEDIATE\n\n### \u003eDIGIT\n_char -- n_\nconvert char digit to numeric digit when within BASE;\nleaves -1 if char is invalid\n\n### \u003eNUMBER\n_ud1 c-addr1 u1 -- ud2 c-addr2 u2_\nconvert string to number;\nupdates accumulated double ud1 to ud2;\nleaves string with the remaining unconvertable chars or empty\n\n    : \u003eNUMBER\n      BEGIN DUP WHILE\n        NEXT-CHAR \u003eDIGIT\n        DUP 0\u003c IF\n          DROP -1 /STRING\n          EXIT\n        THEN\n        \u003eR\n        2SWAP\n        BASE @ UMD*\n        R\u003e M+\n        2SWAP\n      REPEAT ;\n\n### DBL\n_-- flag_\ntrue if \u003eDOUBLE or NUMBER produced a double\n\n    0 VALUE DBL\n\n### \u003eDOUBLE\n_c-addr u -- d true | false_\nconvert string to signed double;\nleaves the double and true if string is converted;\nleaves false if string is unconvertable\n\n### L\u003eNAME\n_lfa -- nt_\nconvert link field address to name token (nfa)\n\n### NAME\u003eSTRING\n_nt -- c-addr u_\nconvert name token (nfa) to string\n\n### NAME\u003e\n_nt -- xt_\nconvert name token (nfa) to execution token (cfa)\n\n### \u003eNAME\n_xt -- nt_\nconvert execution token (cfa) to name token (lfa);\nmay throw -24 \"invalid numeric argument\"\n\n### \u003eBODY\n_xt -- pfa_\nconvert execution token to parameter field address\n\n### FIND-WORD\n_c-addr u -- c-addr 0 | xt 1 | xt -1_\nsearch dictionary for matching word;\nleaves execution token and 1 = immediate or -1 = not immediate;\nleaves c-addr and 0 when not found\n\n### '\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- xt_\nparse name and get execution token;\nmay throw -13 \"undefined word\"\n\n    : ' PARSE-NAME FIND-WORD 0= IF -13 THROW THEN ;\n\n### WORDS\n_--_\ndisplay context vocabulary words\n\n### HERE\n_-- addr_\naddress of free memory after the dictionary;\nnew definitions are added here;\nnote that numeric output words use HERE for conversion\n\n### LASTXT\n_-- xt_\nleaves the last execution token defined\n\n    0 VALUE LASTXT\n\n### STATE\n_-- addr_\ncompilation state;\nSTATE @ leaves TRUE when compiling;\nSTATE @ leaves FALSE when interpreting\n\n    VARIABLE STATE\n\n### [\n_--_\nswitch state to interpreting\n\n    : [ STATE OFF ;\n\n### ]\n_--_\nswitch state to compiling\n\n    : ] STATE ON ;\n\n### HIDE\n_--_\nhide the last definition\n\n    : HIDE CURRENT @ L\u003eNAME DUP C@ smudge_bits OR SWAP C! ;\n\n### REVEAL\n_--_\nreveal the last definition\n\n    : REVEAL CURRENT @ L\u003eNAME DUP C@ ~smudge_bits AND SWAP C! ;\n\n### IMMEDIATE\n_--_\nmake the last definition immediate\n\n    : IMMEDIATE CURRENT @ L\u003eNAME DUP C@ immediate_bits OR SWAP C! ;\n\n### ?COMP\n_--_\ncheck if compiling;\nmay throw -14 \"interpreting a compile-only word\"\n\n### ?SYS\n_-- ; C: x --_\ncheck if compiled control structure matches x;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### UNUSED\n_-- u_\nunused dictionary space\n\n    : UNUSED top @ HERE - ;\n\n### ALLOT\n_n --_\nallocate n bytes starting from HERE in the dictionary;\nundo the last ALLOT with negative n to reclaim memory (only do this when no new words are defined);\nmay throw -8 \"dictionary overflow\"\n\n### COMPILE,\n_xt --_\nappend execution token to dictionary;\nmay throw -8 \"dictionary overflow\"\n\n    : COMPILE, , ;\n\n### ,\n_x --_\nappend cell to dictionary;\nmay throw -8 \"dictionary overflow\"\n\n### C,\n_char --_\nappend char to dictionary;\nmay throw -8 \"dictionary overflow\"\n\n### 2,\n_x1 x2 --_\nappend double cell to dictionary;\nmay throw -8 \"dictionary overflow\"\n\n    : 2, , , ;\n\n### NFA,\n_\"\u003cspaces\u003ename\u003cspace\u003e\" --_\nparse name and append dictionary entry with name;\nset LASTXT to HERE;\nmay throw -8 \"dictionary overflow\"\n\n    : NFA, PARSE-NAME HERE CURRENT @ , CURRENT ! DUP C, HERE SWAP DUP ALLOT CMOVE HERE TO LASTXT ;\n\n### CFA,\n_addr --_\nappend cfa call addr to dictionary;\nmay throw -8 \"dictionary overflow\"\n\n### CFA:,\n_-- addr colon_sys_\nappend cfa colon definition to dictionary;\nmake CONTEXT the CURRENT vocabulary;\nstart compiling;\nmay throw -8 \"dictionary overflow\"\n\n    : CFA:, ] HERE colon_sys ['] (:) CFA, CURRENT TO CONTEXT ;\n\n### POSTPONE\n_\"\u003cspaces\u003ename\u003cspace\u003e\" --_\npostpone compile action of name;\nif name is immediate, then compile name instead of executing it;\notherwise compile name into the current colon definition;\ncan be used to create macros, e.g. : TRUE POSTPONE -1 ; IMMEDIATE;\nmay throw -13 \"undefined word\";\nmay throw -14 \"interpreting a compile-only word\"\n\n### :\n_-- ; C: \"\u003cspaces\u003ename\u003cspace\u003e\" -- addr colon_sys_\ndefine name and start compiling\n\n    : : NFA, HIDE CFA:, ;\n\n### ;\n_-- ; C: addr colon_sys --_\nend colon definition and stop compiling;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n    : ; ?COMP colon_sys \u003c\u003e IF -22 THROW THEN DROP POSTPONE (;) REVEAL [ ; IMMEDIATE\n\n### EXIT\n_--_\nexit colon definition\n\n    : EXIT ?COMP POSTPONE (EXIT) ; IMMEDIATE\n\n### CREATE\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- addr_\ncreate name;\nexecuting name leaves address (HERE addr after CREATE)\n\n    : NFA, ['] (VAR) CFA, ;\n\n### DOES\u003e\n_-- ; ... -- ..._\nchange CREATE name behavior to execute code after DOES\u003e\n\n    : DOES\u003e ?COMP POSTPONE (;CODE) ['] (DOES) CFA, ; IMMEDIATE\n\n### VARIABLE\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- addr_\ndefine a variable;\nexecuting name leaves address of value (initialized to zero)\n\n    : VARIABLE CREATE 0 , ;\n\n### 2VARIABLE\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- addr_\ndefine a double variable;\nexecuting name leaves address of double value (initialized to zero)\n\n    : 2VARIABLE CREATE 0 0 2, ;\n\n### CONSTANT\n_x \"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- x_\ndefine a constant;\nexecuting name leaves x\n\n    : CONSTANT NFA, ['] (CON) CFA, , ;\n    : CONSTANT CREATE , DOES\u003e @ ;\n\n### 2CONSTANT\n_x1 x2 \"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- x1 x2_\ndefine a double constant;\nexecuting name leaves x1 x2\n\n    : 2CONSTANT NFA, ['] (2CON) CFA, 2, ;\n    : 2CONSTANT CREATE 2, DOES\u003e 2@ ;\n\n### VALUE\n_x \"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- x_\ndefine a value;\nexecuting name leaves x\n\n    : VALUE NFA, ['] (VAL) CFA, , ;\n\n### 2VALUE\n_dx \"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- dx_\ndefine a double value;\nexecuting name leaves dx\n\n    : 2VALUE NFA, ['] (2VAL) CFA, 2, ;\n\n### TO\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; x --_\nassign value name;\nmay throw -32 \"invalid name argument\"\n\n    : TO\n      '\n      DUP VALUE? IF\n        \u003eBODY\n        STATE @ IF\n          POSTPONE (TO)\n          ,\n          EXIT\n        THEN\n        !\n        EXIT\n      THEN\n      DUP 2VALUE? IF\n        \u003eBODY\n        STATE @ IF\n          POSTPONE (2TO)\n          ,\n          EXIT\n        THEN\n        2!\n        EXIT\n      THEN\n      #-32 THROW ; IMMEDIATE\n\n### +TO\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; n --_\nincrement value name;\nmay throw -32 \"invalid name argument\"\n\n    : +TO\n      '\n      DUP VALUE? IF\n        \u003eBODY\n        STATE @ IF\n          POSTPONE (+TO)\n          ,\n          EXIT\n          THEN\n        +!\n        EXIT\n      THEN\n      #-32 THROW ; IMMEDIATE\n\n### DEFER\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; ... -- ..._\ndefine a deferred name\n\n    : DEFER NFA, ['] (DEF) CFA, ['] UNDEF , ;\n\n### UNDEF\n_--_\nthrow -256 \"execution of an uninitialized deferred word\"\n\n    : UNDEF -256 THROW ;\n\n### DEFER!\n_xt1 xt2 --_\nstore xt1 in deferred xt2\n\n    : DEFER! \u003eBODY ! ;\n\n### DEFER@\n_xt1 -- xt2_\nfetch execution token from deferred xt1\n\n    : DEFER@ \u003eBODY @ ;\n\n### IS\n_xt \"\u003cspaces\u003ename\u003cspace\u003e\" --_\nassign execution token to deferred name;\nmay throw -32 \"invalid name argument\"\n\n    : IS\n      '\n      DUP DEFER? IF\n        STATE @ IF\n          LITERAL\n          POSTPONE DEFER!\n          EXIT\n        THEN\n        DEFER!\n        EXIT\n      THEN\n      #-32 THROW ; IMMEDIATE\n\n### ACTION-OF\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- xt_\nfetch execution token of deferred name;\nmay throw -32 \"invalid name argument\"\n\n    : ACTION-OF\n      '\n      DUP DEFER? IF\n        STATE @ IF\n          LITERAL\n          POSTPONE DEFER@\n          EXIT\n        THEN\n        DEFER@\n        EXIT\n      THEN\n      #-32 THROW ; IMMEDIATE\n\n### LITERAL\n_x -- ; -- x_\ncompile a literal\n\n    : LITERAL ?COMP POSTPONE (LIT) , ; IMMEDIATE\n\n### 2LITERAL\n_x1 x2 -- ; -- x1 x2_\ncompile a double literal\n\n    : 2LITERAL ?COMP POSTPONE (2LIT) 2, ; IMMEDIATE\n\n### SLITERAL\n_c-addr u -- ; -- c-addr u_\ncompile a string literal;\nmax literal string length is 255\n\n    : SLITERAL\n      ?COMP\n      DUP 255 U\u003e IF -18 THROW THEN\n      POSTPONE (SLIT)\n      DUP C,\n      HERE OVER ALLOT SWAP CMOVE ; IMMEDIATE\n\n### .\"\n_\"ccc\u003cquote\u003e\" -- ; --_\ntype \"ccc\" (compiled)\n\n    : .\" '\" PARSE SLITERAL POSTPONE TYPE ; IMMEDIATE\n\n### S\"\n_\"ccc\u003cquote\u003e\" -- ; -- c-addr u_\nleave string \"ccc\" (compiled and interpreted)\n\n    : S\"\n      '\" PARSE\n      STATE @ IF\n        SLITERAL\n        EXIT\n      THEN\n      TMP SWAP\n      2DUP 2\u003eR\n      CMOVE\n      2R\u003e ; IMMEDIATE\n\n### VALUE?\n_xt -- flag_\ntrue if xt is a VALUE\n\n    : VALUE? DUP C@ $CD = SWAP 1+ @ ['] (VAL) = AND ;\n\n### 2VALUE?\n_xt -- flag_\ntrue if xt is a 2VALUE\n\n    : 2VALUE? DUP C@ $CD = SWAP 1+ @ ['] (2VAL) = AND ;\n\n### DEFER?\n_xt -- flag_\ntrue if xt is a DEFER word\n\n    : DEFER? DUP C@ $CD = SWAP 1+ @ ['] (DEF) = AND ;\n\n### FENCE\n_-- addr_\nonly permit FORGET past the dictionary FENCE address\n\n    0 VALUE FENCE\n\n### FORGET\n_\"\u003cspaces\u003ename\u003cspace\u003e\" --_\ndelete name and all following definitions;\nbeware of vocabulary definitions crossings;\nmay throw -15 \"invalid FORGET\"\n\n### [']\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- xt_\ncompile xt of name as literal;\nmay throw -14 \"interpreting a compile-only word\"\n\n    : ['] ?COMP ' LITERAL ; IMMEDIATE\n\n### RECURSE\n_... -- ..._\nrecursively call the currently defined word;\nmay throw -14 \"interpreting a compile-only word\"\n\n    : RECURSE ?COMP LASTXT COMPILE, ; IMMEDIATE\n\n### ?STACK\n_--_\ncheck parameter stack bounds;\nmay throw -3 \"stack overflow\";\nmay throw -4 \"stack underflow\"\n\n### (UNTIL)\n_x --_\nbranch if x = 0;\nruntime of the UNTIL compile-only word\n\n### (IF)\n_x --_\nbranch if x = 0;\nruntime of the IF and WHILE compile-only words\n\n### (AGAIN)\n_--_\nbranch;\nruntime of the AGAIN and REPEAT compile-only words\n\n### (AHEAD)\n_--_\nbranch;\nruntime of the AHEAD, ELSE and ENDOF compile-only words\n\n### (OF)\n_x1 x2 -- x1 or x1 x2 --_\nbranch if x1 \u003c\u003e x2;\nruntime of the OF compile-only word\n\n### (LOOP)\n_--_\nrepeat loop unless loop counter crosses the limit;\nruntime of the LOOP compile-only word\n\n### (+LOOP)\n_--_\nincrement counter and repeat loop unless counter crosses the limit;\nruntime of the +LOOP compile-only word\n\n### (?DO)\n_n1|u1 n2|u2 --_\nbegin loop with limit n1|u1 and initial value n2|u2;\nskip loop when zero trip loop;\nruntime of the ?DO compile-only word\n\n### (DO)\n_n1|u1 n2|u2 --_\nbegin loop with limit n1|u1 and initial value n2|u2;\nloop at least once;\nruntime of the DO compile-only word\n\n### (UNLOOP)\n_R: ... --_\nremove loop parameters;\nruntime of the UNLOOP compile-only word\n\n### (LEAVE)\n_--_\ndiscard the loop parameters and exit the innermost do-loop;\nruntime of the LEAVE compile-only word\n\n### AHEAD\n_-- ; C: -- addr orig_\nbranch ahead to THEN;\nmay throw -14 \"interpreting a compile-only word\"\n\n### BEGIN\n_-- ; C: -- addr dest_\nbegin WHILE REPEAT;\nmay throw -14 \"interpreting a compile-only word\"\n\n### AGAIN\n_-- ; C: addr dest --_\nbranch back to BEGIN;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### UNTIL\n_x -- ; C: addr dest --_\nbranch back to BEGIN if x = 0;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### IF\n_x -- ; C: -- addr orig_\nbranch to closest ELSE or THEN if x = 0;\nmay throw -14 \"interpreting a compile-only word\"\n\n### THEN\n_-- ; C: addr orig --_\nclose AHEAD, IF, ELSE;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### ELSE\n_-- ; C: addr orig -- addr orig_\nclose IF and branch to THEN;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### WHILE\n_x -- ; C: addr sys -- addr orig addr sys_\nbranch to exit REPEAT if x = 0;\nmay throw -14 \"interpreting a compile-only word\"\n\n### REPEAT\n_-- ; C: addr orig addr dest --_\nbranch back to BEGIN after WHILE;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### DO\n_n1|u1 n2|u2 -- ; C: -- addr do_sys_\nbegin loop from initial value n2|u2 to the limit n1|u1;\nloop at least once;\nmay throw -14 \"interpreting a compile-only word\"\n\n### ?DO\n_n1|u1 n2|u2 -- ; C: -- addr do_sys_\nbegin loop from initial value n2|u2 to the limit n1|u1;\nskip loop when zero trip loop;\nmay throw -14 \"interpreting a compile-only word\"\n\n### LOOP\n_-- ; C: addr do_sys --_\nrepeat loop unless loop counter crosses the limit;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### +LOOP\n_n|u -- ; C: addr do_sys --_\nincrement counter and repeat loop unless counter crosses the limit;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### UNLOOP\n_--_\nremove loop parameters;\nmay throw -14 \"interpreting a compile-only word\"\n\n### LEAVE\n_--_\nexit the innermost do-loop;\nmay throw -14 \"interpreting a compile-only word\"\n\n### I\n_-- n_\ncounter of innermost do loop\n\n### J\n_-- n_\ncounter of outer (second) do loop\n\n### CASE\n_x -- ; C: -- 0_\nbegin CASE ENDCASE switch;\nmay throw -14 \"interpreting a compile-only word\"\n\n### OF\n_x1 x2 -- x1 or x1 x2 -- ; C: n1 -- orig n2_\ntake CASE arm if x1 = x2;\notherwise branch to next OF;\nmay throw -14 \"interpreting a compile-only word\"\n\n### ENDOF\n_-- ; C: n -- orig n_\nbranch to ENDCASE;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### ENDCASE\n_x -- ; C: n*orig n --_\nclose CASE;\nmay throw -14 \"interpreting a compile-only word\";\nmay throw -22 \"control structure mismatch\"\n\n### HANDLER\n_-- addr_\nvariable with saved return stack pointer\n\n    VARIABLE HANDLER\n\n### EXECUTE\n_... xt -- ..._\nexecute execution token xt\n\n### CATCH\n_... xt -- ... 0 or xt -- n_\nexecute xt leaving nonzero exception code n or 0 when no exception occurred;\nwhen an exception was caught, the parameter and return stacks are restored\nto their state before execution of xt\n\n    : CATCH\n      SP@ \u003eR\n      HANDLER @ \u003eR\n      RP@ HANDLER !\n      EXECUTE\n      R\u003e HANDLER !\n      RDROP\n      0 ;\n\n### THROW\n_0 -- or ... n -- ... n_\nthrow exception n if nonzero\n\n    : THROW\n      ?DUP IF\n        HANDLER @ ?DUP IF\n          RP!\n          R\u003e HANDLER !\n          R\u003e SWAP \u003eR\n          SP!\n          DROP\n          R\u003e\n          EXIT\n        THEN\n        \u003eR CLEAR R\u003e\n        ERROR\n        REPL\n      THEN ;\n\n### QUIT\n_... -- ; R: ... --_\nthrow -56 \"QUIT\";\nno exception error is displayed;\nunlike ABORT, the parameter stack is not cleared\n\n    : QUIT -56 THROW ;\n\n### (ABORT\")\n_... flag c-addr u -- ; R: ... --_\nif flag then abort with string message unless an active catch is present;\nruntime of the ABORT\" compile-only word;\nthrow -2 \"ABORT\"\"\n\n    : (ABORT\")\n      ROT IF\n        HANDLER @ IF\n          2DROP\n        ELSE\n          TYPE\n        THEN\n        -2 THROW\n      THEN\n      2DROP ;\n\n### ABORT\"\n_... flag -- ; C: \"ccc\u003cquote\u003e\" -- ; R: ... --_\nif flag then abort with string message unless an active catch is present;\nthrow -2 \"ABORT\"\";\nclears the parameter stack unless caught with CATCH;\nmay throw -14 \"interpreting a compile-only word\"\n\n    : ABORT\" ?COMP POSTPONE S\" POSTPONE (ABORT\") ; IMMEDIATE\n\n### ABORT\n_... -- ; R: ... --_\nthrow -1 \"ABORT\";\nclears the parameter stack unless caught with CATCH\n\n    : ABORT -1 THROW ;\n\n### ERROR\n_n --_\ndisplay exception n at the offending location in the input;\nn = -1 ABORT and n = -2 ABORT\" clear the stack;\nn = -56 QUIT stays silent;\nList of Forth850 errors:\n\ncode | error\n---- | ---------------------------------------------------------\n-1   | ABORT\n-2   | ABORT\"\n-3   | stack overflow\n-4   | stack underflow\n-8   | dictionary overflow\n-10  | division by zero\n-11  | result out of range\n-13  | undefined word\n-14  | interpreting a compile-only word\n-15  | invalid FORGET\n-16  | attempt to use zero-length string as a name\n-18  | parsed string overflow\n-19  | definition name too long\n-22  | control structure mismatch\n-24  | invalid numeric argument\n-28  | user interrupt (BREAK was pressed)\n-32  | invalid name argument (invalid TO name)\n-42  | floating-point divide by zero\n-43  | floating-point result out of range\n-46  | floating-point invalid argument\n-56  | QUIT\n-256 | execution of an uninitialized deferred word\n\n### NUMBER\n_c-addr u -- n|u|d|ud_\nconvert string to number;\nvalue DBL is set to -1 when the number is a double;\nmay throw -13 \"undefined word\" when string is not numeric\n\n### INTERPRET\n_--_\ninterpret input while input is available\n\n### EVALUATE\n_... c-addr u -- ..._\nevaluate string\n\n### REPL\n_--_\nread-evaluate-print loop\n\n    : REPL\n      rp0 @ RP!\n      HANDLER OFF\n      0 TO SOURCE-ID\n      CR\n      [\n      BEGIN\n        BEGIN ['] REFILL CATCH ?DUP WHILE\n          ERROR CR\n        REPEAT\n      WHILE\n        SPACE\n        ['] INTERPRET CATCH ?DUP IF\n          ERROR\n          REPL\n        THEN\n        STATE @ INVERT IF\n          .\" OK[\"\n          DEPTH 0 U.R\n          '] EMIT\n        THEN\n        CR\n      REPEAT\n      BYE ;\n\n### BYE\n_--_\nreturn to BASIC\n\n### CONTEXT\n_-- addr_\nleaves address of link of the last vocabulary context definition\n\n    ' FORTH VALUE CONTEXT\n\n### CURRENT\n_-- addr_\nleaves address of link of the last current vocabulary definition\n\n    ' FORTH VALUE CURRENT\n\n### DEFINITIONS\n_--_\nmake CURRENT the CONTEXT vocabulary\n\n    : DEFINITIONS CONTEXT TO CURRENT ;\n\n### VOCABULARY\n_\"\u003cspaces\u003ename\u003cspace\u003e\" --_\ndefine a new vocabulary\n\n    : VOCABULARY CREATE , fig_kludge , DOES\u003e TO CONTEXT ;\n\n### FORTH\n_--_\nmake FORTH the CONTEXT vocabulary\n\n    VOCABULARY FORTH\n\n\n## Additional words included with the full version\n\n### FALSE\n_-- 0_\nleave 0\n\n    0 CONSTANT FALSE\n\n### TRUE\n_-- -1_\nleave -1\n\n    -1 CONSTANT TRUE\n\n### 2ROT\n_xd1 xd2 xd3 -- xd2 xd3 xd1_\nrotate double cells\n\n    : 2ROT 5 ROLL 5 ROLL ;\n\n### ROLL\n_xu x(u+1) ... x1 x0 u -- x(u+1) ... x1 x0 xu_\nroll u cells on the parameter stack;\n0 ROLL does nothing;\n1 ROLL is the same as SWAP;\n2 ROLL is the same as ROT\n\n### D*\n_d1|ud1 d2|ud2 -- d3|ud3_\nsigned and unsigned double product d1*d2\n\n    : D* \u003eR ROT DUP\u003eR -ROT MD* 2R\u003e * 0 SWAP D+ ;\n\n### UD/MOD\n_ud1 ud2 -- ud3 ud4_\nunsigned double remainder and quotient ud1/ud2;\nthe result is undefined when ud2 = 0\n\n### D/MOD\n_d1 d2 -- d3 d4_\ndouble symmetric remainder and quotient d1/d2;\nthe result is undefined when d2 = 0\n\n    : D/MOD\n      DUP 3 PICK DUP\u003eR XOR \u003eR\n      DABS 2SWAP DABS 2SWAP\n      UD/MOD\n      R\u003e 0\u003c IF DNEGATE THEN\n      R\u003e 0\u003c IF 2SWAP DNEGATE 2SWAP THEN ;\n\n### DMOD\n_d1 d2 -- d3_\ndouble symmetric remainder of d1/d2;\nthe result is undefined when d2 = 0\n\n    : DMOD D/MOD 2DROP ;\n\n### D/\n_d1 d2 -- d3_\ndouble quotient d1/d2;\nthe result is undefined when d2 = 0\n\n    : D/ D/MOD 2SWAP 2DROP ;\n\n### D=\n_d1 d2 -- flag_\ntrue if d1 = d2\n\n    : D= D- D0= ;\n\n### D\u003c\n_d1 d2 -- flag_\ntrue if d1 \u003c d2\n\n    : D\u003c\n      DUP 3 PICK XOR 0\u003c IF\n        2DROP D0\u003c\n        EXIT\n      THEN\n      D- D0\u003c ;\n\n### DU\u003c\n_du1 du2 -- flag_\ntrue if ud1 \u003c ud2\n\n    : DU\u003c\n      DUP 3 PICK XOR 0\u003c IF\n        2SWAP 2DROP D0\u003c\n        EXIT\n      THEN\n      D- D0\u003c ;\n\n### DMAX\n_d1 d2 -- d3_\nsigned double max of d1 and d2\n\n    : DMAX\n      2OVER 2OVER D\u003c IF 2SWAP THEN\n      2DROP ;\n\n### DMIN\n_d1 d2 -- d3_\nsigned double min of d1 and d2\n\n    : DMIN\n      2OVER 2OVER D\u003c INVERT IF 2SWAP THEN\n      2DROP ;\n\n### CELL+\n_addr -- addr_\nincrement to next cell\n\n    : CELL+ 2+ ;\n\n### CELLS\n_n1 -- n2_\nconvert to cell unit\n\n    : CELLS 2* ;\n\n### CHAR+\n_n1 -- n1_\nincrement to next char\n\n    : CHAR+ 1+ ;\n\n### CHARS\n_n1 -- n2_\nconvert to char unit\n\n    : CHARS ;\n\n### DUMP\n_c-addr u --_\ndump memory in hex\n\n    : DUMP\n      BASE @ \u003eR\n      HEX\n      BEGIN DUP WHILE\n        NEXT-CHAR .\n      REPEAT\n      2DROP\n      R\u003e BASE ! ;\n\n### HOLDS\n_c-addr u --_\nhold string for pictured numeric output\n\n    : HOLDS\n      BEGIN DUP WHILE\n        1- 2DUP + C@ HOLD\n      REPEAT\n      2DROP ;\n\n### BEEP\n_--_\nsound the speaker for a short ~2KHz beep\n\n### KEY-CLEAR\n_--_\nwait until no keys are pressed\n\n    : KEY-CLEAR BEGIN INKEY 0= UNTIL ;\n\n### KEY?\n_-- flag_\ntrue if a key is pressed\n\n    : KEY? INKEY 0= 0= ;\n\n### WORD\n_char \"\u003cchars\u003eccc\u003cchar\u003e\" -- c-addr_\nparse word as a counted string\n\n    : WORD TMP DUP ROT PARSE-WORD ROT 2DUP C! 1+ SWAP CMOVE ;\n\n### CHAR\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- char_\nparse char;\nnote that the syntax 'char is preferred instead of this legacy word\n\n    : CHAR PARSE-NAME DROP C@ ;\n\n### FIND\n_c-addr -- c-addr 0 | xt 1 | xt -1_\nsearch dictionary for counted string;\nsee WORD, COUNT and FIND-WORD\n\n### BUFFER:\n_n \"\u003cspaces\u003ename\u003cspace\u003e\" -- ; -- addr_\ndefine buffer with n bytes;\nexecuting name leaves address of n bytes\n\n    : BUFFER: CREATE ALLOT ;\n\n### :NONAME\n_-- xt_\ncolon definition without name;\nleaves execution token of definition to be used or saved\n\n### C\"\n_\"ccc\u003cquote\u003e\" -- ; -- c-addr_\nleave counted string \"ccc\" (compiled);\nmay throw -18 \"parsed string overflow\"\n\n    : C\" ?COMP POSTPONE S\" POSTPONE DROP POSTPONE 1- ;\n\n### MARKER?\n_xt -- flag_\ntrue if xt is a MARKER word\n\n### MARKER\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; --_\ndefine a dictionary marker;\nexecuting name deletes marker and all definitions made after;\nbeware of vocabulary definitions crossings\n\n    : MARKER\n      CURRENT\n      DUP @\n      HERE\n      CREATE\n        , 2,\n      DOES\u003e\n        DUP CELL+ 2@\n        SWAP TO CONTEXT\n        DUP CONTEXT !\n        DEFINITIONS\n        L\u003eNAME NAME\u003e TO LASTXT\n        @ HERE - ALLOT ;\n\n### ANEW\n_\"\u003cspaces\u003ename\u003cspace\u003e\" -- ; --_\ndefine a dictionary marker;\ndeletes previously defined name and all following definitions;\nbeware of vocabulary definitions crossings\n\n    : ANEW\n      \u003eIN @ \u003eR\n      PARSE-NAME FIND-WORD\n      OVER MARKER?\n      AND IF\n        EXECUTE\n      ELSE\n        DROP\n      R\u003e \u003eIN !\n      MARKER ;\n\n### [CHAR]\n_\"\u003cspaces\u003echar\" -- ; -- char_\ncompile char as literal;\nnote that the syntax 'char is preferred instead of this legacy word;\nmay throw -14 \"interpreting a compile-only word\"\n\n    : [CHAR] ?COMP CHAR LITERAL ; IMMEDIATE\n\n### [COMPILE]\n_\"\u003cspace\u003ename\u003cspace\u003e\" -- ; ... -- ..._\ncompile name;\nnote that POSTPONE is preferred instead of this legacy word;\nmay throw -14 \"interpreting a compile-only word\"\n\n    : [COMPILE] ?COMP ' COMPILE, ; IMMEDIATE\n\n### K\n_-- n_\ncounter of outer (third) do loop\n\n### TEXT\n_--_\nread and evaluate TEXT editor area with Forth source code;\ncaveat: .( and ( in TEXT cannot span more than one line, they end at EOL\n\n    : TEXT\n      $7973 @ 1+ \u003eR\n      BEGIN\n        R\u003e                  \\ -- addr\n      DUP C@ $FF \u003c\u003e WHILE\n        2+ DUP C@ SWAP 1+   \\ -- len addr\n        2DUP + \u003eR\n        SWAP 1- EVALUATE\n      REPEAT\n      DROP ;\n\n\n## Floating point math words included with the full version\n\nFloating point values are doubles on the stack.  Double words, such as 2DUP,\ncan be used to manipulate floats.  Floats can be stored in 2CONSTANT, 2VARIABLE\nand 2VALUE assigned with TO (but not with +TO.)\n\nBeware that HEX prevents inputting floats and garbles the output of floats.\n\n### F+\n_r1 r2 -- r3_\nsum r1+r2;\nmay throw -43 \"floating-point result out of range\"\n\n### F-\n_r1 r2 -- r3_\ndifference r1-r2;\nmay throw -43 \"floating-point result out of range\"\n\n### F*\n_r1 r2 -- r3_\nproduct r1*r2;\nmay throw -43 \"floating-point result out of range\"\n\n### F/\n_r1 r2 -- r3_\nquotient r1/r2\nmay throw -42 \"floating-point divide by zero\";\nmay throw -43 \"floating-point result out of range\"\n\n### FTRUNC\n_r1 -- r2_\ntruncate float towards zero\n\n### FLOOR\n_r1 -- r2_\nfloor float towards negative infinity\nmay throw -43 \"floating-point result out of range\"\n\n### FROUND\n_r1 -- r2_\nround float to nearest;\nmay throw -43 \"floating-point result out of range\"\n\n### FNEGATE\n_r1 -- r2_\nnegate float\n\n### FABS\n_r1 -- r2_\nabsolute value |r1|\n\n    : FABS 2DUP F0\u003c IF FNEGATE THEN ;\n\n### F=\n_r1 r2 -- flag_\ntrue if r1 = r2\n\n    : F= D= ; ( works for IEEE 754 floating point without negative zero and inf/nan )\n\n### F\u003c\n_r1 r2 -- flag_\ntrue if r1 \u003c r2\n\n    : F\u003c\n      DUP 3 PICK AND 0\u003c IF\n        2SWAP\n      D\u003c ; ( works for IEEE 754 floating point without negative zero and inf/nan )\n\n### F0=\n_r -- flag_\ntrue if r = 0.0e0\n\n    : F0= D0= ; ( works for IEEE 754 floating point without negative zero and inf/nan )\n\n### F0\u003c\n_r -- flag_\ntrue if r \u003c 0.0e0\n\n    : F0\u003c D0\u003c ; ( works for IEEE 754 floating point without negative zero and inf/nan )\n\n### FMAX\n_r1 r2 -- r3_\n\nmax of r1 and r2\n\n    : FMAX\n      2OVER 2OVER F\u003c IF 2SWAP THEN\n      2DROP ;\n\n### FMIN\n_r1 r2 -- r3_\n\nmin of r1 and r2\n\n    : FMIN\n      2OVER 2OVER F\u003c INVERT IF 2SWAP THEN\n      2DROP ;\n\n### D\u003eF\n_d -- r_\nwiden signed double to float\n\n### S\u003eF\n_n -- r_\nwiden signed single to float\n\n### F\u003eD\n_r -- d_\nnarrow float to a signed double;\nmay throw -11 \"result out of range\"\n\n### F\u003eS\n_r -- n_\nnarrow float to a signed single;\nmay throw -11 \"result out of range\"\n\n### \u003eFLOAT\n_c-addr u -- r true | false_\nconvert string to float;\nleaves the float and true if string is converted;\nleaves false if string is unconvertable\n\n### REPRESENT\n_r c-addr u -- n flag true_\nconvert float to string;\nstore decimal digits of the float in the non-empty buffer c-addr u;\nleaves decimal exponent n+1 and flag = true if negative\n\n### PRECISION\n_-- +n_\nfloating point output precision, the number of decimal digits displayed is 7 by default\n\n    7 VALUE PRECISION\n\n### F.\n_r --_\noutput float with a trailing space;\noutput fixed notation when 1e-1 \u003c= |r| \u003c 1e+7, otherwise output scientific notation\n\n    : F.\n      HERE PRECISION REPRESENT DROP IF\n        '- EMIT\n      THEN\n      DUP 0 PRECISION 1+ WITHIN IF\n        HERE OVER TYPE\n        '. EMIT\n        HERE OVER +\n        PRECISION ROT - '0 -TRIM TYPE SPACE\n        EXIT\n      THEN\n      HERE C@ EMIT\n      '. HERE C!\n      HERE PRECISION '0 -TRIM TYPE\n      'E EMIT\n      1- . ;\n\n## Dictionary structure\n\nThe Forth850 dictionary is organized as follows:\n\n         low address\n          _________\n    +---\u003e| $0000   |     last link is zero (2 bytes)\n    ^    |---------|\n    |    | 3       |     length of \"(:)\" (1 byte)\n    |    |---------|\n    |    | (:)     |     \"(:)\" word characters (3 bytes)\n    |    |---------|\n    |    | code    |     machine code\n    |    |=========|\n    +\u003c==\u003e+ link    |     link to previous entry (2 bytes)\n    ^    |---------|\n    :    :         :\n    :    :         :\n    :    :         :\n    |    |=========|\n    +\u003c==\u003e| link    |     link to previous entry (2 bytes)\n    ^    |---------|\n    |    | $80+5   |     length of \"aword\" (1 byte) with IMMEDIATE bit set\n    |    |---------|\n    |    | aword   |     \"aword\" word characters (5 bytes)\n    |    |---------|\n    |    | code    |     Forth code and/or data\n    |    |=========|\n    +\u003c---| link    |\u003c--- last link to previous entry (2 bytes)\n         |---------|\n         | 7       |     length of \"my-word\" (1 byte)\n         |---------|\n         | my-word |     \"my-word\" word characters (7 bytes)\n         |---------|\n         | code    |\u003c--- LASTXT points to code (last xt)\n         |=========|\u003c--- HERE pointer\n         | hold    |     hold area for numerical output (40 bytes)\n         |---------|\n         |         |\n         | free    |     unused dictionary space\n         | space   |\n         |         |\n         |=========|\u003c--- dictionary limit\n         |         |\n         | data    |     stack of 256 bytes (128 cells)\n         | stack   |     grows toward lower addresses\n         |         |\u003c--- SP stack pointer\n         |=========|\n         |         |\n         | return  |     return stack of 256 bytes (128 cells/calls)\n         | stack   |     grows toward lower addresses\n         |         |\u003c--- RP return stack pointer\n         |_________|\u003c--- USER address\n                    \u003c--- USER+1 address\n\n         high address set with USER in Monitor MON\n\nA link field points to the previous link field.  The last link field at the\nlowest address of the dictionary is zero.\n\n`LASTXT` returns the execution token of the last definition, which is the\nlocation where the machine code of the last word starts.\n\nForth850 is a Direct Threaded Code Forth implementation.  Code is either\nmachine code or starts with a jump or call machine code instruction of 3 bytes,\nfollowed by Forth code (a sequence of execution tokens in a colon definition)\nor data (constants, variables, values and other words created with `CREATE`.)\n\nImmediate words are marked with the length byte high bit 7 set ($80).  Hidden\nwords have the \"smudge\" bit 6 ($40) set.  A word is hidden until successfully\ncompiled.  `HIDE` hides the last defined word by setting the smudge bit.\n`REVEAL` reveals it.  Incomplete colon definitions with compilation errors\nshould never be revealed.\n\n## Implementation\n\nThe following sections explain parts of the technical implementation of\nForth850.  I will explain the new Forth system routines, the new Z80 math\nroutines and the string routines.\n\nForth850 is built with the [asz80](https://shop-pdp.net/ashtml/asz80.htm)\nassembler and aslink linker.\n\n## Z80 Forth system routines\n\nForth850 uses direct threaded code (DTC).  Faster would be to use subroutine\nthreaded code (STC), but this would significantly increase the overall code\nsize and Forth compilation complexity, which are less desirable for a small\nZ80-based system.\n\nThe following Z80 Forth routines are inspired by the article\n[Moving Forth](https://www.bradrodriguez.com/papers/moving1.htm).  However,\nI've decided to use a different Z80 register mapping that is more efficient:\n\n- BC: instruction pointer (IP)\n- DE: top of stack (TOS)\n- IY: address of the \"next routine\", for `jp (iy)` \n\nBy contrast to the article, having the TOS in DE makes it quicker to perform\naddress arithmetic with the TOS, because we can exchange DE with HL with `ex\nde,hl` in just 4 CPU cycles.  Moving BC to HL takes 8 CPU cycles.\n\nI've placed the return stack pointer (RP) in RAM.  There is no advantage to use\nregister IX for RP as the article suggests.  In fact, the colon call and return\nhave the same cycle counts, but almost all of the return stack operations, such\nas `\u003eR`, require more cycles with the RP in IX compared to the RP in RAM.\n\nA jump to the \"next routine\" is with `jp (iy)` takes 8 CPU cycles, compared to\na `jp next` that takes 10 cycles.  Inlining the \"next routine\" eliminates\nthis overhead, but increases the code size.  Inlining should only be applied to\nperformance-critical words that are frequently used.  See macros `NEXT` and\n`JP_NEXT` defined in the section below.\n\n### Next fetch and execute\n\nFetching an execution token (xt) from the instruction pointer (IP) address,\nincrementing IP and executing the token takes 38 CPU cycles in the \"next\nroutine\":\n\n    .macro          NEXT\n                    ld a,(bc)       ;  7    ;\n                    ld l,a          ;  4    ;\n                    inc bc          ;  6    ;\n                    ld a,(bc)       ;  7    ;\n                    ld h,a          ;  4    ;\n                    inc bc          ;  6    ; [ip++] -\u003e hl with xt\n                    jp (hl)         ;  4(38); jump to hl\n    .endif\n\nThe \"next routine\" cycles contribute to the overhead of DTC, which cannot be\nremoved to speed up DTC execution.  To improve speed by 10% on average, the\nfetch and execute routine is inlined with the `NEXT` macro for\nperformance-critical words.  When performance is not critical, a `JP_NEXT`\nmacro is used, which simply expands into `jp (iy)` with the IY register\npointing to the \"next routine\":\n\n    .macro          JP_NEXT\n                    jp (iy)         ;  8(46); jump to next routine\n    .endm\n\n### Colon call and return\n\nEach colon definition in memory starts with a `call docol`.  The `docol`\nroutine associated with the `(:)` word saves the instruction pointer in BC on\nthe return stack and pops the new instruction pointer from the parameter stack\n(since `call docol` leaves the address after the call on the stack.)  The\nroutine checks for ON/BREAK key and begins executing the colon definition with\nthe \"next routine\":\n\n    docol:          ld hl,(rp)      ; 16    ; [rp] -\u003e hl\n                    dec hl          ;  6    ;\n                    ld (hl),b       ;  7    ;\n                    dec hl          ;  6    ;\n                    ld (hl),c       ;  7    ; save bc -\u003e [--rp] with caller ip on the return stack\n                    ld (rp),hl      ; 16    ; ip - 2 -\u003e rp\n                    pop bc          ; 10(68); pop ip saved by call docol\n    ;               continue with ON/BREAK key check\n    cont:           in a,(0x1f)     ; 11    ; port 0x1f bit 7 is set if ON/BREAK is depressed\n                    add a           ;  4    ; test ON/BREAK key\n                    jr c,break      ;  7(22); if ON/BREAK pressed then break\n    ;               next\n    next:           ld a,(bc)       ;  7    ;\n                    ld l,a          ;  4    ;\n                    inc bc          ;  6    ;\n                    ld a,(bc)       ;  7    ;\n                    ld h,a          ;  4    ;\n                    inc bc          ;  6    ; [bc++] -\u003e hl with xt\n                    jp (hl)         ;  4(38); jump to hl\n\nA return from a colon definition with the `(;)` word pops the return\ninstruction pointer off the return stack to continue executing the caller's\nnext instruction.\n\n    doret:          ld hl,(rp)      ; 16    ; [rp] -\u003e hl\n                    ld c,(hl)       ;  7    ;\n                    inc hl          ;  6    ;\n                    ld b,(hl)       ;  7    ;\n                    inc hl          ;  6    ;\n                    ld (rp),hl      ; 16(58); restore [rp++] -\u003e bc with ip of the caller\n                    NEXT            ; 38    ; continue\n\nA colon call takes 145 cycles (17 + 68 + 22 + 38 cycles) and a colon return\ntakes 96 cycles (58 + 38 cycles.)  This includes the 38 cycle overhead of the\n\"next routine\" to fetch and execute the next token.\n\n### Variables\n\nA Forth variable leaves its address on the parameter stack.  A `call dovar` is\nused to push the address on the stack, which is then retrieved to set the new\nTOS:\n\n    dovar:          pop hl          ; 10    ; pop hl with pfa addr saved by call dovar\n                    push de         ; 11    ; save TOS\n                    ex de,hl        ;  4(25); set new TOS to hl with pfa addr\n                    NEXT            ; 38    ; continue\n\nExecuting a word defined as a variable takes 80 cycles (17 + 25 + 38 cycles),\nwhich includes the \"next routine\" overhead.\n\n### Constants and values\n\nA Forth constant or value leaves its value on the parameter stack.  A `call\ndoval` is used to push the address of the constant/value on the stack.  The\nconstant/value is then fetched:\n\n    doval:          pop hl          ; 10    ; pop hl with pfa addr saved by call doval\n                    push de         ; 11    ; save TOS\n                    ld e,(hl)       ;  7    ;\n                    inc hl          ;  6    ;\n                    ld d,(hl)       ;  7(41); set [hl] -\u003e de as new TOS\n                    NEXT            ; 38    ; continue\n\nExecuting a word defined as a constant or value takes 96 cycles (17 + 41 + 38\ncycles), which includes the \"next routine\" overhead.\n\n### Fetch and store\n\nThe `@` fetch and `!` store words make good use of `ex de,hl`:\n\n    fetch:          ex de,hl        ;  4    ; addr -\u003e hl\n                    ld e,(hl)       ;  7    ;\n                    inc hl          ;  6    ;\n                    ld d,(hl)       ;  7(24); set [hl] -\u003e de as new TOS\n                    NEXT            ; 38    ; continue\n\n    store:          pop hl          ; 10    ; pop addr -\u003e hl\n                    ex de,hl        ;  4    ; x -\u003e de, addr -\u003e hl\n                    ld (hl),e       ;  7    ;\n                    inc hl          ;  6    ;\n                    ld (hl),d       ;  7    ; de -\u003e [hl] with x\n                    pop de          ; 10(44); pop new TOS\n                    NEXT            ; 38    ; continue\n\n### CREATE with DOES\u003e\n\nA Forth definer word that uses CREATE with DOES\u003e to define new words is\ncompiled to execute the `(;CODE)` token with label `doscode`, followed by a\n`call dodoes` to start interpreting the DOES\u003e code:\n\n    doscode:        ld hl,(lastxt+3)        ; LASTXT -\u003e hl with last defined word xt\n                    inc hl                  ;\n                    ld (hl),c               ;\n                    inc hl                  ;\n                    ld (hl),b               ; ip -\u003e [LASTXT+1] overwrite call address\n                    jr doret                ; (;) return to caller\n\n    dodoes:         ld hl,(rp)      ; 16    ; [rp] -\u003e hl\n                    dec hl          ;  6    ;\n                    ld (hl),b       ;  7    ;\n                    dec hl          ;  6    ;\n                    ld (hl),c       ;  7    ;\n                    ld (rp),hl      ; 16    ; save bc -\u003e [--rp] with old ip on the return stack\n                    pop bc          ; 10    ; pop bc with new ip of the DOES\u003e routine saved by call dodoes\n                    pop hl          ; 10    ; pop pfa addr\n                    push de         ; 11    ; save TOS\n                    ex de,hl        ;  4(93); set new TOS to hl with pfa addr\n                    NEXT            ; 38    ; continue\n\nA word defined by a CREATE/DOES\u003e definer makes a `call` to the `call dodoes`\nroutine.  For example, suppose we define `CONSTANT` as follows:\n\n    : CONSTANT CREATE , DOES\u003e @ ;\n    123 CONSTANT X\n\nthen `CONSTANT` and `X` are compiled as:\n\n    CONSTANT:       call docol\n                    .dw create\n                    .dw comma\n                    .dw doscode\n    CONSTANT_does:  call dodoes\n                    .dw fetch\n                    .dw doret\n\n    X:              call CONSTANT_does\n                    .dw 123\n\nExecuting `X` takes 192 cycles (17 + 17 + 24 + 38 + 96 cycles.)  When \nmore optimally defined as a `CONSTANT` in code, this takes 96 cycles.\n\n### Parsing\n\nForth words are parsed with my new `CHOP` and `TRIM` words that efficiently\nparse and extract white-space-delimited words from the input.\n\nEntry:\n- DE with TOS: a char to truncate the string with\n- 2OS: string length u1\n- 3OS: string address c-addr\n\nExit:\n- DE with TOS: truncated string length u2\n- 2OS: string address c-addr\n\nPerformance:\n21 cycles per character for non-BL char to chop, 40 cycles per character for BL\nto chop white space\n\n    chop:           ld a,e                  ; char -\u003e a\n                    exx                     ; save bc with ip\n                    ex af,af'               ; save a with char\n                    pop bc                  ; pop u1 -\u003e bc\n                    ld e,c                  ;\n                    ld d,b                  ; u1 -\u003e de\n                    ld a,c                  ;\n                    or b                    ; test bc = 0, 0 -\u003e cf\n                    jr z,2$                 ; if bc = 0 then not found\n                    pop hl                  ;\n                    push hl                 ; c-addr -\u003e hl\n                    ex af,af'               ; restore a with char\n                    cp 0x20                 ;\n                    jr z,3$                 ; if a = 0x20 then find white space\n                    or a                    ; 0 -\u003e cf not found\n    ;               find char in string\n                    cpir            ; 21/16 ; repeat until a = [hl++] or --bc = 0\n                    jr nz,2$                ; if match then \n    1$:             ccf                     ;   complement to correct cpi bc--\n    2$:             ex de,hl                ; u1 -\u003e hl\n                    sbc hl,bc               ; u1 - bc - cf -\u003e hl\n                    push hl                 ; save hl as TOS\n                    exx                     ; restore bc with ip\n                    pop de                  ; pop new TOS\n                    JP_NEXT                 ; continue\n    ;               find white space in string\n    3$:             cp (hl)         ;  7    ; loop to compare a to [hl]\n                    cpi             ; 16    ;   hl++, bc--\n                    jr nc,1$        ;  7    ;   if [hl]\u003ca then found\n                    jp pe,3$        ; 10    ; until bc = 0\n                    jr 1$                   ; not found\n\nEntry:\n- DE with TOS: char to trim the string by removing them from its beginning\n- 2OS: string length u1\n- 3OS: string address c-addr1\n\nExit:\n- DE with TOS: updated string length u2\n- 2OS: updated string address c-addr2\n\nPerformance:\n33 cycles to trim non-BL char, 106 cycles to trim white space with BL char\n\n    trim:           ld a,e                  ; char -\u003e a\n                    exx                     ; save bc with ip\n                    pop bc                  ; u1 -\u003e bc\n                    pop hl                  ; c-addr1 -\u003e hl\n    1$:             ex af,af'       ;  4    ; save a\n                    ld a,c          ;  4    ;\n                    or b            ;  4    ;\n                    jr z,3$         ;  7    ; if bc \u003c\u003e 0 then\n                    ex af,af'       ;  4    ;   restore a\n    2$:             cpi             ; 16    ;   loop\n                    jr nz,4$        ;  7/12 ;     while a = [hl++], --bc\n                    jp pe,2$        ; 10    ;   until b = 0\n    3$:             push hl                 ; save hl as 2OS\n                    push bc                 ; save bc as TOS\n                    exx                     ; restore bc with ip\n                    pop de                  ; pop new TOS\n                    JP_NEXT                 ; continue\n    4$:             cp 0x20         ;  7    ;\n                    jr nz,5$        ;  7    ; if char = 0x20 then\n                    dec hl          ;  6    ;\n                    cp (hl)         ;  7    ;\n                    inc hl          ;  6    ;\n                    jr nc,1$        ; 12    ;   if [hl-1] \u003c= 0x20 then keep trimming\n    5$:             inc bc                  ; correct bc++ for cpi match\n                    dec hl                  ; correct hl-- for cpi match\n                    jr 3$                   ; finalize trimming\n\nTo parse a white-space-delimited word is efficiently performed with `BL PARSE`\nwhere the `PARSE` word is defined as:\n\n    : PARSE     ( char \"ccc\u003cchar\u003e\" -- c-addr u )\n      SOURCE\n      \u003eIN @ /STRING\n      ROT CHOP\n      DUP 1+ \u003eIN @ +\n      SOURCE NIP UMIN \u003eIN ! ;\n\nTo skip input until the next non-white-space character is efficiently performed\nwith `BL SKIPS`, where `SKIPS` is defined as:\n\n    : SKIPS     ( char \"\u003cchars\u003e\" -- )\n      SOURCE \u003eIN @ /STRING\n      ROT TRIM\n      DROP\n      SOURCE DROP - \u003eIN ! ;\n\n### Dictionary search with case insensitive string matching\n\nThe `FIND-WORD` word searches the dictionary starting with `CONTEXT` for a\nmatching word.  The search is case insensitive.  Smudged words are skipped.\n\nEntry:\n- DE with TOS: size of the string to search u\n- 2OS: address of the string to search c-addr\n\nExit:\n- DE with TOS: 0 = not found, 1 = found immediate, -1 = found (not immediate)\n- 2OS: string address if not found or execution token when found\n\nPerformance:\n95 cycles per dictionary entry,\n51 or 102 cycles per character comparison when characters match\n\n    findword:       ld a,d                  ;\n                    or a                    ; test d = 0 high order byte of u\n                    jp nz,zero_next         ; if u is too large then set new TOS to 0\n                    sla e                   ; shift u to compare w/o immediate bit\n                    jp c,zero_next          ; if u is too large then set new TOS to 0\n                    jp z,zero_next          ; if u = 0 then set new TOS to 0\n                    push de                 ; save de with 2*u\n                    exx                     ; save bc with ip\n                    pop bc                  ; pop 2 * u -\u003e bc\n                    pop de                  ; pop c-addr -\u003e de\n                    ld hl,(context+3)       ; CONTEXT -\u003e hl\n                    jr 3$                   ; start searching\n    ;               loop over dictionary\n    1$:             pop de                  ; restore de with c-addr\n    2$:             pop hl          ; 10    ; loop, restore hl with lfa\n    3$:             ld a,(hl)       ;  7    ;\n                    inc hl          ;  6    ;\n                    ld h,(hl)       ;  7    ;\n                    ld l,a          ;  4    ;   [hl] -\u003e hl follow link at hl = lfa\n                    or h            ;  4    ;\n                    jr z,6$         ;  7    ;   if hl = 0 then not found\n                    push hl         ; 11    ;   save hl with lfa\n                    inc hl          ;  6    ;\n                    inc hl          ;  6    ;   hl + 2 -\u003e hl with nt (nfa)\n                    ld a,(hl)       ;  7    ;   word length\n                    add a           ;  4    ;   shift away immediate bit\n                    cp c            ;  4    ;   test a = c word length match (both shifted)\n                    jr nz,2$        ; 12(95);   if lengths differ then continue searching\n    ;               compare string to word\n                    push de                 ;   save de with c-addr\n                    inc hl                  ;   hl++ point to nfa chars\n                    ld b,c                  ;   2 * u -\u003e b\n                    srl b                   ;   u -\u003e b word length (nonzero)\n    ;               loop over word chars\n    4$:             ld a,(de)       ;  7    ;   loop\n                    cp (hl)         ;  7    ;     compare [de] = [hl]\n                    jr z,5$         ; 12/7  ;     if mismatch then\n                    and 0xdf        ;    7  ;       make upper case\n                    cp 'A           ;    7  ;\n                    jr c,1$         ;    7  ;       if a\u003c'A' then continue search\n                    cp 'Z+1         ;    7  ;\n                    jr nc,1$        ;    7  ;       if a\u003e'Z' then continue search\n                    xor (hl)        ;    7  ;\n                    and 0xdf        ;    7  ;       case insensitive compare [de] = [hl]\n                    jr nz,1$        ;    7  ;       if mismatch then continue search\n    5$:             inc de          ;  6    ;     de++ point to next char of c-addr\n                    inc hl          ;  6    ;     hl++ point to next char of word\n                    djnz 4$         ; 13(51/102);until --b = 0\n    ;               found a matching word\n                    pop de                  ;   discard saved c-addr\n                    ex (sp),hl              ;   save hl with xt as 2OS, restore hl with lfa\n                    inc hl                  ;\n                    inc hl                  ;   hl + 2 -\u003e hl with nt (nfa)\n                    bit immediate_bit,(hl)  ;   test immediate bit of [hl] word length\n                    exx                     ;   restore bc with ip\n                    jp nz,one_next          ;   set new TOS to 1 if word is immediate\n                    jp mone_next            ;   set new TOS to -1\n    ;               not found\n    6$:             push de                 ; save de with c-addr as 2OS\n                    exx                     ; restore bc with ip\n                    jp zero_next            ; set new TOS to 0\n                    JP_NEXT                 ; continue\n\n## Z80 integer math routines\n\nI've written the following Z80 math routines.  My objective was to make them\nas efficient as possible.  The second objective was to keep the code size small\nby using tricks with CPU arithmetic and flags.\n\n### Fast signed/unsigned 16x16-\u003e16 bit multiplication\n\nEntry:\n- BC: signed or unsigned multiplier n1\n- DE: signed or unsigned multiplicand n2\n\nExit:\n- HL: signed product or unsigned product n3\n\nPerfomance:\nmax 51 cycles x 16 iterations = 816 cycles or\nmax 51 cycles x 8 iterations + 45 x 8 = 768 cycles, excluding entry/exit overhead\n\n    mult1616:       ld hl,0                 ; 0 -\u003e hl\n                    ld a,c                  ; c -\u003e a low order byte of n1\n                    ld c,b                  ; b -\u003e c save high order byte of n1\n                    ld b,8                  ; 8 -\u003e b loop counter\n    1$:             rra             ;  4    ; loop, a \u003e\u003e 1 -\u003e a set cf\n                    jr nc,2$        ;  7    ;   if cf = 1 then\n                    add hl,de       ; 11    ;     hl + de -\u003e hl\n    2$:             sla e           ;  8    ;\n                    rl d            ;  8    ;   de \u003c\u003c 1 -\u003e de\n                    djnz 1$         ; 13(51); until --b = 0\n                    ld a,c                  ; c -\u003e a high order byte of n1\n                    ld b,8                  ; 8 -\u003e b loop counter\n    3$:             rra             ;  4    ; loop, a \u003e\u003e 1 -\u003e a set cf\n                    jr nc,4$        ;  7    ;   if cf = 1 then\n                    add hl,de       ; 11    ;     hl + de -\u003e hl\n    4$:             sla e           ;  8    ;\n                    rl d            ;  8    ;   de \u003c\u003c 1 -\u003e de\n                    djnz 3$         ; 13(51); until --b = 0\n                    ret                     ; done\n\nWe can make an additional speed improvement, which only costs us one more\ninstruction byte.  To calculate the high order byte we do not need to iterate\nover all 8 bits of the high order multiplier stored in register c, but only\nover the nonzero bits.  We also can ignore the lower order result stored in\nregister e.  This reduces the max loop iteration cycle time to 32 and 33 per\nbit.  Furthermore, the second loop only runs until the last bit of register c\nis shifted out.  If register c is zero, the second loop does not execute\nthereby saving hundreds of cycles.  We also use jp instead of jr to improve and\nbalance the cycle time per bit:\n\n    mult1616:       ld hl,0                 ; 0 -\u003e hl\n                    ld a,c                  ; c -\u003e a low order byte of n1\n                    ld c,b                  ; b -\u003e c save high order byte of n1\n                    ld b,8                  ; 8 -\u003e b loop counter\n    1$:             rra             ;  4    ; loop, a \u003e\u003e 1 -\u003e a set cf\n                    jr nc,2$        ;  7    ;   if cf = 1 then\n                    add hl,de       ; 11    ;     hl + de -\u003e hl\n    2$:             sla e           ;  8    ;\n                    rl d            ;  8    ;   de \u003c\u003c 1 -\u003e de\n                    djnz 1$         ; 13(51); until --b = 0\n                    ld a,h                  ; h -\u003e a do high order, low order is done\n                    jr 5$                   ; jump to shift c and loop\n    3$:             add d           ;  4    ; loop, a + d -\u003e d\n    4$:             sla d           ;  8    ;   d \u003c\u003c 1 -\u003e d\n    5$:             srl c           ;  8    ;   c \u003e\u003e 1 -\u003e c set cf and z if no bits left\n                    jr c,3$         ; 12/7(32); until cf = 0 repeat with addition\n                    jp nz,4$        ;   10(33); until c = 0 repeat without addition\n                    ret                     ; done\n\nNote: unrolling the loops would improve the speed at the cost of a significant\ncode size increase, which is undesirable for small memory devices.\n\n### Fast unsigned 16x16-\u003e32 bit multiplication\n\nEntry:\n- DE: unsigned multiplicand u1\n- BC: unsigned multiplier u2\n\nExit:\n- DE: low order unsigned product u3\n- HL: high order unsigned product u3\n\nPerfomance:\nmax 64 cycles x 17 iterations = 1088 cycles, excluding entry/exit overhead\n\n    umult1632:      xor a                   ; 0 -\u003e cf\n                    ld l,a                  ;\n                    ld h,a                  ; 0 -\u003e hl\n                    ld a,17                 ; 17 -\u003e a loop counter\n    1$:             rr h            ;  8    ; loop\n                    rr l            ;  8    ;\n                    rr d            ;  8    ;\n                    rr e            ;  8    ;   hlde + cf \u003e\u003e 1 -\u003e hlde\n                    jr nc,2$        ;  7    ;   if cf = 1 then\n                    add hl,bc       ; 11    ;     hl + bc -\u003e hl\n    2$:             dec a           ;  4    ;\n                    jp nz,1$        ; 10(64); until --a = 0\n                    ret                     ; done\n\nNote: unrolling the loop would improve the speed at the cost of a significant\ncode size increase, which is undesirable for small memory devices.\n\n### Fast signed/unsigned 32x32-\u003e32 bit multiplication\n\nEntry:\n- BC': low order signed or unsigned multiplicand d1\n- DE: high order signed or unsigned multiplicand d1\n- DE': low order signed or unsigned multiplier d2\n- HL': high order signed or unsigned multiplier d2\n\nExit:\n- HL': low order signed product or unsigned product d3\n- HL: high order signed product or unsigned product d3\n\nPerfomance:\nmax 98 cycles x 32 iterations = 3136 cycles, excluding entry/exit overhead\n\n    mult3232:       ld hl,0                 ; 0 -\u003e hl high order d3, de with d2 high order\n                    exx                     ; save bc with ip\n                    ld a,h                  ;\n                    push af                 ; save d1 high order byte 3\n                    ld a,l                  ;\n                    push af                 ; save d1 high order byte 2\n                    ld a,b                  ;\n                    push af                 ; save d1 low order byte 1\n                    ld a,c                  ;\n                    push af                 ; save d1 low order byte 0\n                    ld hl,0                 ; 0 -\u003e hl' low order d3\n                    ld c,4                  ; 4 -\u003e c outer loop counter\n    1$:             pop af                  ; loop, [sp++] -\u003e a next d1 byte\n                    ld b,8                  ;   8 -\u003e b inner loop counter\n    2$:             rra             ;  4    ;   loop, a \u003e\u003e 1 -\u003e a set cf\n                    jr nc,3$        ;  7    ;     if cf = 1 then\n                    add hl,de       ; 11    ;       hl' + de' -\u003e hl add low order\n                    exx             ;  4    ;\n                    adc hl,de       ; 15    ;       hl + de + cf -\u003e hl add high order\n                    exx             ;  4    ;\n    3$:             sla e           ;  8    ;\n                    rl d            ;  8    ;     de' \u003c\u003c 1 -\u003e de' shift low order\n                    exx             ;  4    ;\n                    rl e            ;  8    ;\n                    rl d            ;  8    ;     de \u003c\u003c 1 + cf -\u003e de shift high order\n                    exx             ;  4    ;\n                    djnz 2$         ; 13(98);   until --b = 0\n                    dec c                   ;\n                    jr nz,1$                ; until --c = 0\n                    ret                     ; done\n\nThe same tricks as the 16x16-\u003e16 multiplication method can be used to reduce\nthe cycle time, but at a cost of increased code size.  We also assign different\nregisters to the low and high order parts:\n\nEntry:\n- BC': low order signed or unsigned multiplicand d1\n- DE: high order signed or unsigned multiplicand d1\n- HL': low order signed or unsigned multiplier d2\n- DE': high order signed or unsigned multiplier d2\n\nExit:\n- HL: low order signed product or unsigned product d3\n- HL': high order signed product or unsigned product d3\n\nPerfomance:\nmax 8 x (98+87+59+33) = 2216 cycles, excluding entry/exit overhead\n\n    mult3232:       ld hl,0                 ; 0 -\u003e hl low order d3, de with d2 low order\n                    exx                     ; save bc with ip\n                    ld a,h                  ;\n                    push af                 ; save d1 high order byte 3\n                    ld a,l                  ;\n                    push af                 ; save d1 high order byte 2\n                    ld a,b                  ;\n                    push af                 ; save d1 low order byte 1\n                    ld a,c                  ; d1 -\u003e a low order byte 0\n                    ld hl,0                 ; 0 -\u003e hl' high order d3\n                    ld b,8                  ; 8 -\u003e b loop counter\n    1$:             rra             ;  4    ; loop, a \u003e\u003e 1 -\u003e a set cf\n                    jr nc,2$        ;  7    ;   if cf = 1 then\n                    exx             ;  4    ;\n                    add hl,de       ; 11    ;     hl + de -\u003e hl add low order\n                    exx             ;  4    ;\n                    adc hl,de       ; 15    ;     hl' + de' + cf -\u003e hl add high order\n    2$:             exx             ;  4    ;\n                    sla e           ;  8    ;\n                    rl d            ;  8    ;   de \u003c\u003c 1 -\u003e de shift low order\n                    exx             ;  4    ;\n                    rl e            ;  8    ;\n                    rl d            ;  8    ;   de' \u003c\u003c 1 + cf -\u003e de' shift high order\n                    djnz 1$         ; 13(98); until --b = 0\n                    pop af                  ;\n                    ld c,a                  ; d1 -\u003e c low order byte1\n                    exx                     ;\n                    ld a,h                  ; h -\u003e a low order d3\n                    exx                     ;\n                    ld b,8                  ; 8 -\u003e b loop counter\n    3$:             rr c            ;  8    ; loop, c \u003e\u003e 1 -\u003e c set cf\n                    jr nc,4$        ;  7    ;   if cf = 1 then\n                    exx             ;  4    ;\n                    add d           ;  4    ;     a + d -\u003e a add low order\n                    exx             ;  4    ;\n                    adc hl,de       ; 15    ;     hl' + de' + cf -\u003e hl add high order\n    4$:             exx             ;  4    ;\n                    sla d           ;  8    ;   d \u003c\u003c 1 -\u003e d shift low order\n                    exx             ;  4    ;\n                    rl e            ;  8    ;\n                    rl d            ;  8    ;   de' \u003c\u003c 1 + cf -\u003e de' shift high order\n                    djnz 3$         ; 13(87); until --b = 0\n                    exx                     ;\n                    ld h,a                  ; a -\u003e h low order d3\n                    exx                     ;\n                    pop af                  ; d1 -\u003e a high order byte 2\n                    ld b,8                  ; 8 -\u003e b loop counter\n    5$:             rra             ;  8    ; loop, c \u003e\u003e 1 -\u003e c set cf\n                    jr nc,6$        ;  7    ;   if cf = 1 then\n                    add hl,de       ; 15    ;     hl' + de' + cf -\u003e hl add high order\n    6$:             rl e            ;  8    ;\n                    rl d            ;  8    ;   de' \u003c\u003c 1 + cf -\u003e de' shift high order\n                    djnz 5$         ; 13(59); until --b = 0\n                    pop af                  ;\n                    ld c,a                  ; d1 -\u003e c high order byte 3\n                    ld a,h                  ; h -\u003e a high order\n                    jr 9$                   ; jump to shift c and loop\n    7$:             add d           ;  4    ; loop, a + d -\u003e a\n    8$:             sla d           ;  8    ;   d \u003c\u003c 1 -\u003e d\n    9$:             srl c           ;  8    ;   c \u003e\u003e 1 -\u003e c set cf and z if no bits left\n                    jr c,7$         ; 12/7(32); until cf = 0 repeat with addition\n                    jp nz,8$        ;   10(33); until c = 0 repeat without addition\n                    ld h,a                  ; a -\u003e h high order\n                    exx                     ;\n                    ret                     ; done\n\nNote: unrolling the inner loop would improve the speed at the cost of a\nsignificant code size increase, which is undesirable for small memory devices.\n\n### Fast unsigned 32/16-\u003e16 bit division and remainder\n\nThis implementation is used by all division and remainder (modulo) Forth words\nby calling `UM/MOD`.  As such, it is an important and versatile algorithm.\n\nEntry:\n- HL: high order dividend ud\n- BC: low order dividend ud\n- DE: divisor u1\n\nExit:\n- HL: remainder u2\n- BC: quotient u3\n\nPerformance:\nmax 85 cycles x 16 iterations = 1360 cycles, excluding entry/exit overhead\n\n    udiv3216:       xor a                   ;\n                    sub e                   ;\n                    ld e,a                  ;\n                    sbc a                   ;\n                    sub d                   ;\n                    ld d,a                  ; -de -\u003e de with -u1\n                    ld a,b                  ; b -\u003e a low order dividend in ac\n                    ld b,16                 ; 16 -\u003e b loop counter\n                    sla c                   ;\n                    rla                     ; ac \u003c\u003c 1 -\u003e ac\n    1$:             adc hl,hl       ; 15    ; loop, hl \u003c\u003c 1 + cf -\u003e hl\n                    jr nc,2$        ; 12/ 7 ;   if cf = 1 then\n                    add hl,de       ;    11 ;     hl + -u1 -\u003e hl\n                    scf             ;     4 ;     1 -\u003e cf\n                    jr 3$           ;    12 ;   else\n    2$:             add hl,de       ; 11    ;     hl + -u1 -\u003e hl\n                    jr c,3$         ; 12/ 7 ;     if cf = 0 then\n                    sbc hl,de       ;    15 ;       hl - -u1 -\u003e hl to undo, no carry\n    3$:             rl c            ;  8    ;\n                    rla             ;  4    ;   ac \u003c\u003c 1 + cf -\u003e ac\n                    djnz 1$         ; 13(85); until --b = 0\n                    ld b,a                  ; a -\u003e b quotient bc, remainder in hl\n                    ret                     ; done\n\nThe algorithm negates the divisor first to speed up subtraction by adding the\nnegative of the divisor instead.  Another benefit of using the negated divisor\nis that this produces the right carry value to shift into the quotient,\notherwise the carry should be inverted or the resulting quotient must be\ninverted.\n\nBy moving the first conditional block out of the loop, we can save 5 CPU cycles\non the critical path (the most expensive path through the loop) to reduce to\nmax 80 cycles per iteration at the cost of making the code more cluttered.\n\nEntry:\n- HL: high order dividend ud\n- BC: low order dividend ud\n- DE: divisor u1\n\nExit:\n- HL: remainder u2\n- BC: quotient u3\n\nPerformance:\nmax 80 cycles x 16 iterations = 1280 cycles, excluding entry/exit overhead\n\n    udiv3216:       xor a                   ;\n                    sub e                   ;\n                    ld e,a                  ;\n                    sbc a                   ;\n                    sub d                   ;\n                    ld d,a                  ; -de -\u003e de with -u1\n                    ld a,b                  ; b -\u003e a low order dividend in ac\n                    ld b,16                 ; 16 -\u003e b loop counter\n                    sla c                   ;\n                    rla                     ; ac \u003c\u003c 1 -\u003e ac\n    1$:             adc hl,hl       ; 15    ; loop, hl \u003c\u003c 1 + cf -\u003e hl\n                    jr c,3$         ;  7/12 ;   if cf = 1 then hl + -u1 -\u003e hl, 1 -\u003e cf else\n                    add hl,de       ; 11    ;     hl + -u1 -\u003e hl\n                    jr c,2$         ; 12/ 7 ;     if cf = 0 then\n                    sbc hl,de       ;    15 ;       hl - -u1 -\u003e hl to undo, no carry\n    2$:             rl c            ;  8    ;\n                    rla             ;  4    ;   ac \u003c\u003c 1 + cf -\u003e ac\n                    djnz 1$         ; 13(80); until --b = 0\n                    ld b,a                  ; a -\u003e b quotient bc, remainder in hl\n                    ret                     ; done\n\n    3$:             add hl,de       ;    11 ; hl + -u1 -\u003e hl\n                    scf             ;     4 ; 1 -\u003e cf\n                    jr 2$           ;    12 ;\n\nBy comparison, the CamelForth Z80 code is also fast, but slower than my\nimplemenation with 90 cycles x 16 iterations = 1440 cycles, excluding\nentry/exit overhead:\n\n    udiv3216:       ld a,16                 ; 16 -\u003e a loop counter\n                    sla e                   ;\n                    rl d                    ; de \u003c\u003c 1 -\u003e de\n    1$:             adc hl,hl       ; 15    ; loop, hl \u003c\u003c 1 + cf -\u003e hl\n                    jr nc,2$        ; 12/ 7 ;   if cf = 1 then\n                    or a            ;     4 ;     0 -\u003e cf\n                    sbc hl,bc       ;    15 ;     hl - u1 -\u003e hl\n                    or a            ;     4 ;     0 -\u003e cf\n                    jp 3$           ;    10 ;   else\n    2$:             sbc hl,bc       ; 15    ;     hl - u1 -\u003e hl\n                    jr nc,3$        ; 12/ 7 ;     if cf = 1 then\n                    add hl,bc       ;    11 ;       hl + u1 -\u003e hl to undo sbc, sets cf\n    3$:             rl e            ;  8    ; \n                    rl d            ;  8    ;   de \u003c\u003c 1 + cf -\u003e de with inverse cf we'll need\n                    dec a           ;  4    ;\n                    jp nz,1$        ; 10(90); until --a = 0\n                    ld a,e                  ;\n                    cpl                     ;\n                    ld e,a                  ;\n                    ld a,d                  ;\n                    cpl                     ;\n                    ld d,a                  ; complement de, faster than ccf in loop\n                    ret                     ; done\n\nNote: unrolling the loop would improve the speed at the cost of a significant\ncode size increase, which is undesirable for small memory devices.\n\n### Fast unsigned 32/32-\u003e32 bit division and remainder\n\nThis algorithm uses the shadow registers BC', DE' and HL'.  Because of this\nregister pressure, there is little room for further optimization.  Registers\nIX and IY cannot be used since they lack the necessary adc and sbc\ninstructions.\n\nEntry:\n- BC: high order dividend ud1\n- BC': low order dividend ud1\n- DE': high order divisor ud2\n- DE: low order divisor ud2\n\nExit:\n- HL': high order remainder ud3\n- HL: low order remainder ud3\n- BC: high order quotient ud4\n- BC': low order quotient ud4\n\nPerformance:\nmax 162 cycles x 32 iterations = 5184 cycles, excluding entry/exit overhead\n\n    udiv3232:       exx                     ;\n                    xor a                   ;\n                    ld h,a                  ;\n                    ld l,a                  ; 0 -\u003e hl'\n                    rl c                    ;\n                    rl b                    ;\n                    exx                     ;\n                    ld h,a                  ;\n                    ld l,a                  ; 0 -\u003e hl\n                    ld a,b                  ; b -\u003e a\n                    rl c                    ;\n                    rla                     ; ac \u003c\u003c 1 -\u003e ac\n                    ld b,32                 ; 32 -\u003e b loop counter\n    1$:             adc hl,hl       ; 15    ;\n                    exx             ;  4    ;\n                    adc hl,hl       ; 15    ;\n                    exx             ;  4    ;   hl'.hl \u003c\u003c 1 + cf -\u003e hl'.hl no carry\n                    sbc hl,de       ; 15    ;\n                    exx             ;  4    ;\n                    sbc hl,de       ; 15    ;   hl'.hl - de'.de -\u003e hl'.hl\n                    jr nc,2$        ; 12/ 7 ;   if cf = 1 then\n                    exx             ;     4 ;\n                    add hl,de       ;    11 ;\n                    exx             ;     4 ;\n                    adc hl,de       ;    15 ;     hl'.hl + de'.de -\u003e hl'.hl to undo, sets carry\n    2$:             ccf             ;  4    ;   complement cf\n                    rl c            ;  8    ;\n                    rl b            ;  8    ;\n                    exx             ;  4    ;\n                    rl c            ;  8    ;\n                    rla             ;  4    ;   ac.bc' \u003c\u003c 1 + cf\n                    djnz 1$         ; 13(162); until --b = 0\n                    ld b,a                  ;\n                    ld e,c                  ;\n                    ret                     ;\n\n## Z80 floating point math routines\n\nI've written a collection of Z80 IEEE 754 single precision floating point math\nroutines:\n- [math.asm](math.asm) (960 bytes of code) a simple version with truncation\n- [mathr.asm](mathr.asm) (1085 bytes of code) includes three IEEE 754 rounding\nmodes, where the default rounding mode is to round to nearest, ties to even;\n- [mathri.asm](mathr.asm) (1296 bytes of code) includes the three IEEE 754\nrounding modes, and inf/nan and signed zero.  This version is not intended for\nForth850, because Forth850 raises floating point exceptions.\n\nMy objective was to make the floating point routines as efficient as possible,\nsuch as by using the shadow registers instead of memory.  No memory is used,\nexcept at most one push-pop pair to move a value between the (shadow)\nregisters.  The second objective was to keep the code size small by using\ntricks with CPU arithmetic and flags.  The floating point library is about 1KB.\n\nSingle precision floating point values are stored in registers BC (high order)\nand DE (low order) to form a 32 bit float `bcde` and shadow float `bcde'`.\n\n- `fadd`: float `bcde` + `bcde'` -\u003e `bcde`; cf set on overflow\n- `fsubx`: float `bcde` - `bcde'` -\u003e `bcde`; cf set on overflow\n- `fsuby`: float `bcde'` - `bcde` -\u003e `bcde`; cf set on overflow\n- `fneg`: float - `bcde` -\u003e `bcde`; no errors (cf reset)\n- `fabs`: float |`bcde`| -\u003e `bcde`; no errors (cf reset)\n- `fmul`: float `bcde` * `bcde'` -\u003e `bcde`; cf set on overflow\n- `fdivx`: float `bcde` / `bcde'` -\u003e `bcde`; cf set on overflow or when dividing by zero\n- `fdivy`: float `bcde'` / `bcde` -\u003e `bcde`; cf set on overflow or when dividing by zero\n- `ftoi`: float `bcde` -\u003e signed 32 bit integer `bcde` truncated towards zero; cf set when out of range\n- `itof`: signed 32 bit integer `bcde` -\u003e float `bcde`; no errors (cf reset)\n- `ftrunc`: float trunc(`bcde`) -\u003e `bcde`; no errors (cf reset)\n- `ffloor`: float floor(`bcde`) -\u003e `bcde`; cf set on overflow\n- `fround`: float round(`bcde`) -\u003e `bcde`; cf set on overflow\n- `fpow10`: 10^`a` * `bcde` -\u003e `bcde` for -128 \u003c= `a` \u003c 39; cf set on overflow\n- `atof`: string [`hl`..`hl`+`a`-1] -\u003e float `bcde`; cf set on parsing error and `hl` points after the char\n- `ftoa`: float `bcde` -\u003e [`hl`...`hl`+`a`-1] string of digits, exponent `e` and sign `d` bit 7; no errors (flags undefined)\n- `fzero`: set `bcde` to 0.0\n\n[mathri.asm](mathr.asm) includes inf/nan and signed zero.  In this version the\nroutines listed above may return signed inf or nan with cf set to indicate\noverflow and errors.  In addition, this version includes the following\nroutines:\n\n- `ftype`: float `bcde` -\u003e `bcde` unchanged; cf set if `bcde` is nan, cf reset and z set if `bcde` is +/-inf\n- `fnan`: set `bcde` to nan; cf set\n- `finf`: set `bcde` to inf with sign in register A bit 7 (negative when set); cf set\n\n## Z80 string routines\n\nI've written the following Z80 string routines.  My objective was to make them\nas efficient as possible, such as by making the obvious choice to use the `cpi`\nand `cpir` Z80 instructions to minimize cycle count.  The second objective was\nto keep the code size small by using tricks with CPU arithmetic and flags.\n\n### Fast string comparison\n\nEntry:\n- IX: address of the first string c-addr1\n- HL: size of the first string u1\n- DE: address of the second string c-addr2\n- BC: size of the second string u2\n\nExit:\n- A: -1 (less), 0 (equal), 1 (greater)\n- F: zero flag set when equal, sign flag set when less\n\nPerformance:\n46 cycles per character comparison when characters match\n\n    compare:        push ix                 ; save c-addr1\n                    push hl                 ; save u1\n                    xor a                   ; 0 -\u003e a flags u1 = u2, 0 -\u003e cf\n                    sbc hl,bc               ;\n                    jr z,1$                 ; if u1 \u003c\u003e u2 then\n                    inc a                   ;   1 -\u003e a flags u1 \u003e u2\n                    jr nc,1$                ;   if u1 \u003c u2 then\n                    pop bc                  ;     pop u1 -\u003e bc\n                    push bc                 ;     rebalance stack\n                    ld a,-1                 ;   -1 -\u003e a flags u1 \u003c u2\n    1$:             pop hl                  ; pop to discard u1\n                    pop hl                  ; pop c-addr1 -\u003e hl\n                    ex af,af'               ; save a with -1|0|1 flag\n                    ld a,c                  ;\n                    or b                    ;\n                    jr z,3$                 ; if bc \u003c\u003e 0 then\n    ;               compare chars\n    2$:             ld a,(de)       ;  7    ;   loop\n                    cpi             ; 16    ;     compare [hl++] to [de], --bc\n                    jr nz,4$        ;  7    ;     while characters [hl] and [de] are equal\n                    inc de          ;  6    ;     de++\n                    jp pe,2$        ; 10(46);   until bc = 0\n    ;               chars match, check lengths\n    3$:             ex af,af'               ; restore a with -1|0|1 flag\n                    ret                     ;\n    ;               strings differ\n    4$:             dec hl                  ; hl-- to correct cpi overshoot\n                    cp (hl)                 ; test a\u003c[hl]\n                    ccf                     ; complement cf, cf = 1 if [hl]\u003ca\n                    sbc a                   ; a = -1 if cf = 1 else 0\n                    add a                   ; a = -2 if cf = 1 else 0\n                    inc a                   ; a = -1 if cf = 1 else 1\n                    ret                     ; done\n\n### Fast string search\n\nNaive string search, i.e. not Knuth-Morris-Pratt which is faster but would\nrequire a table and more code.\n\nEntry:\n- HL: address of the string searched c-addr1\n- IX: size of the string searched u1\n- DE: address of the string to search c-addr2\n- BC: size of the string to search u2\n\nExit:\n-  F: carry set when no match found\n- HL: address of the string position found c-addr3\n- BC: size of the remaining characters after the match\n\nPerformance:\n21 cycles per character to search the first or next character match and 46\ncycles per character comparison when characters match\n\n    search:         or a                    ; 0 -\u003e cf\n                    sbc ix,bc               ; u1 - u2 -\u003e ix\n                    ret c                   ; if u2\u003eu1 then impossible search, cf = 1\n                    ld a,c                  ;\n                    or b                    ;\n                    ret z                   ; if u2 = 0 then done (found), cf = 0\n                    push ix                 ;\n                    push bc                 ;\n                    pop ix                  ; u2 -\u003e ix\n                    pop bc                  ; u1 - u2 -\u003e bc\n                    inc bc                  ; u1 - u2 + 1 -\u003e bc correct for cpir\n                    push hl                 ; save c-addr1 on the stack\n    ;               find char match\n    1$:             push de                 ; loop, save de with c-addr2\n                    ld a,(de)               ;   [de] -\u003e a\n                    cpir            ; 21/16 ;   repeat until a = [hl++] or --bc = 0\n                    jr nz,4$                ;   if no match then not found\n                    pop de                  ;   restore de with c-addr2\n                    push bc                 ;\n                    push de                 ;\n                    push hl                 ;   save bc,de,hl\n                    push ix                 ;\n                    pop bc                  ;   u2 -\u003e bc\n    ;               compare substrings\n                    dec bc                  ;   u2 - 1 -\u003e bc since u2 \u003e 0\n                    ld a,c                  ;\n                    or b                    ;\n                    jr z,3$                 ;   if bc\u003c\u003e 0 then\n                    inc de                  ;     de++ to start matching at c-addr2+1\n    2$:             ld a,(de)       ;  7    ;     loop\n                    cpi             ; 16    ;       compare [hl++] to [de], --bc\n                    jr nz,3$        ;  7    ;       while characters [hl] and [de] are equal\n                    inc de          ;  6    ;       de++\n                    jp pe,2$        ; 10(46);     until bc = 0\n    3$:             pop hl                  ;\n                    pop de                  ;\n                    pop bc                  ;   restore bc,de,hl\n                    jr nz,1$                ; repeat\n    ;               substrings match\n                    dec hl                  ; hl-- to correct cpir overshoot\n                    ret                     ; done, cf = 0\n    ;               not found\n    4$:             scf                     ; 1 -\u003e cf\n                    ret                     ; done, cf = 1\n\n## Sharp PC-G850(V)(S)\n\n- Manual: \u003chttp://basic.hopto.org/basic/manual/Sharp%20PC-G850V.pdf\u003e\n- HP Forum thread: \u003chttps://www.hpmuseum.org/forum/thread-10520.html\u003e\n- Sharp PC-G850(V)(S) software (Japanese site): \u003chttp://ver0.sakura.ne.jp/g800/index.html\u003e\n\n## Forth resources\n\n- Forth for the Sharp PC-E500(S): \u003chttps://github.com/Robert-van-Engelen/Forth500\u003e\n- Forth 2012 Standard: \u003chttps://forth-standard.org/standard/intro\u003e\n- Moving Forth: \u003chttps://www.bradrodriguez.com/papers/moving1.htm\u003e\n\nForth850 benefits from the work done by many others to offer inspiration, but\nthe system does not include licensed code of the following implementations or\nany other implementation not listed here.  Some parts of Forth850 are derived\nfrom freely available Forth resources listed above and the Z80 resources listed\nfurther below:\n\n- CamelForth for the Z80: \u003chttp://www.camelforth.com/page.php?5\u003e\n- eForth: \u003chttps://github.com/lispnik/eforth/blob/master/z80efort/EFZ80.ASM\u003e\n- Jupiter Ace ROM listing: \u003chttp://www.jupiter-ace.co.uk/romli","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobert-van-engelen%2Fforth850","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobert-van-engelen%2Fforth850","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobert-van-engelen%2Fforth850/lists"}