fix: simplify the whole logic
fix (z80): adjust README
fix (z80): link to right image path
10biForthOS = 10b(2) i(nstructions) Forth OS is a very primitive Forth with only two instructions:
It is heavily inspired by Frank Sergeant 3-Instruction Forth and is a strip down exercise following up SectorForth, SectorLisp, SectorC (the C compiler used here) and milliForth.
Here is the full OS code in 46 bytes of 8086 assembly opcodes:
50b8 8e00 31d8 e8ff 0017 003c 0575 00ea
5000 3c00 7401 eb02 e8ee 0005 0588 eb47
b8e6 0200 d231 14cd e480 7580 c3f4
When loaded after the boot, 10biForthOS listen on the serial port for instructions (a keyboard version, an x64 port in 217 bytes and a z80 CPC464 are also provided).
The 1 instuction should be followed by a byte of an assembly opcode to be compiled into a fixed memory location.
The 0 instruction launch the compiled program.
And that's it!
On a host computer you can then send commands to 10biForthOS.
As examples a subleq-eForth or even SectorC can be used to code in eForth or C.
Build 10biForthOS floppy
$ make 10biForthOS.flp
nasm -f bin 10biForthOS.asm -o 10biForthOS.com
wc -c 10biForthOS.com
46 10biForthOS.com
nasm -DPAD_ZERO -f bin 10biForthOS.asm -o 10biForthOS.com
dd status=noxfer conv=notrunc if=10biForthOS.com of=10biForthOS.flp
1+0 records in
1+0 records out
Boot the machine (here qemu) or simply type make to build and boot the machine:
$ ./boot &
The OS is waiting input on the serial port.
Send the hello world example on the serial port:
$ ./send examples/hello-world.asm
00000000 B8007E mov ax,0x7e00
00000003 8EC0 mov es,ax
00000005 30FF xor bh,bh
... ...
00000028 726C jc 0x96
0000002A 64210D and [fs:di],cx
0000002D 00 db 0x00
The send command translate the above opcodes in a sequence of 1 command followed with a byte of the opcode.
1 B8 1 00 1 7E 1 8E 1 C0 1 30 1 FF 1...
Formatted like the disassembled code it looks like this:
00000000 1 B8 1 00 1 7E mov ax,0x7e00
00000003 1 8E 1 C0 mov es,ax
00000005 1 30 1 FF xor bh,bh
... ...
Send a 0
$ ./send 0
Enjoy!

Test the keyboard:
$ ./send examples/echo-kbd.asm
00000000 E81200 call 0x15
00000003 3C0D cmp al,0xd
00000005 741A jz 0x21
... ...
00000026 B00A mov al,0xa
00000028 E8EFFF call 0x1a
0000002B EBD3 jmp short 0x0
Send a 0
$ ./send 0
Ok the keyboard is up:

Then send the subleq-eForth example to start programming in Forth:
$ ./send examples/subleq-eForth/subleq-eForth.asm
00000000 30E4 xor ah,ah
00000002 B002 mov al,0x2
00000004 CD10 int 0x10
... ...
00004FAC 94 xchg ax,sp
00004FAD 0B39 or di,[bx+di]
00004FAF 04 db 0x04
Send a 0
$ ./send 0
Write your code in Forth!!

Then send the fat :-) SectorC example to program in C:
./send examples/sectorc/sectorc.s
00000000 680030 push word 0x3000
00000003 1F pop ds
00000004 680020 push word 0x2000
... ...
000001E5 009EC099 add [bp-0x6640],bl
000001E9 009DC000 add [di+0xc0],bl
000001ED 00 db 0x00
Send a 0
$ ./send 0
Write your code in C!!!
$ ./cat_sectorc hello.c | ./to-serial

or (you have to reset the machine for now)
$ ./cat_sectorc sinwave.c | ./to-serial
Send a 0
$ ./send 0

Shutdown the machine:
$ ./send examples/shutdown.asm
00000000 B80010 mov ax,0x1000
00000003 8ED0 mov ss,ax
00000005 BC00F0 mov sp,0xf000
... ...
0000000B BB0100 mov bx,0x1
0000000E B90300 mov cx,0x3
00000011 CD15 int 0x15
Send a 0
$ ./send 0
The machine shutdown.
Pseudocode description:
bits 16
cpu 8086
[org 0x7c00] ; boot load address
CODE equ 0x7e00 ; address where code should be compiled / executed
;;;--------------------
;;; Init
;;;--------------------
init:
mov ax,CODE
mov ds,ax
xor di,di
;;;--------------------
;;; Interpreter loop
;;;--------------------
loop:
call getchar
cmp al,0
je execute
cmp al,1
je compile
jmp loop
;;;--------------------
;;; Execute
;;;--------------------
execute:
jmp CODE:0x0
;;;--------------------
;;; Compile
;;;--------------------
compile:
call getchar
mov byte [di],al
inc di
jmp loop
;;;--------------------
;;; Utils
;;;--------------------
getchar: ; get a char in al from serial (bios function)
mov ax,0x0200
xor dx,dx
int 0x14
and ah,0x80 ; check for failure and clear ah as a side-effect
jne getchar ; failed, try again
ret
;;;--------------------
;;; Zero padding
;;;--------------------
%ifdef PAD_ZERO
times 510-($-$$) db 0
dw 0xAA55 ; boot indicator
%endif
Boot the machine (here qemu)
$ ./boot &
The OS is waiting input from the keyboard.
Type the hello world example with the keyboard (1 [opcode] 1 [opcode] ...).
transl type it for you:
$ ./transl ../examples/hello-world.asm
00000000 B85000 mov ax,0x50
00000003 8EC0 mov es,ax
00000005 30FF xor bh,bh
... ...
00000028 726C jc 0x96
0000002A 64210D and [fs:di],cx
0000002D 00 db 0x00
1B815010018E1C01301FF1BD11F1001B41131B310A1B010118B10E12C1001B610B1B210A1CD1101EA10017C10010014816516C16C16F12C12015716F17216C16412110D100104104
Type a 0 with the keyboard and tada:

Is this a Forth?
It seems to be: it has an outer interpreter which understand assembly opcodes and an inner interpreter: the standard machine code. A minimal switch/case dictionnary.
You can load/redefine code at will.
Even if it lacks stacks, defining words, etc. It has the simplicity and hacky feeling of Forth.
Is this an OS?
Once booted you can interract with the screen, the keyboard, the file system and so on.
Once the Forth interpreter or the C compiler are loaded, you can program in Forth or C.
Doesn't it look like an OS?
Why?
Why not? More seriously. It's quite pleasant to be able to write an OS with so little code and be able to extend it while it is running.
Discussion or patches?