10biForthOS: A full 8086 OS in 46 bytes

1 day ago 3

#10biForthOS a full 8086 OS in 46 bytes

10biForthOS = 10b(2) i(nstructions) Forth OS is a very primitive Forth with only two instructions:

  • 1 is compile
  • 0 is execute

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

#Overview

When loaded after the boot, 10biForthOS listen on the serial port for instructions (a keyboard version and an x64 port in 217 bytes 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.

#Usage

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!

Hello world

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:

echo keyboard

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!!

subleq eForth

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

C hello world

or (you have to reset the machine for now)

$ ./cat_sectorc sinwave.c | ./to-serial

Send a 0

$ ./send 0

C sin wave

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.

#How this works

Pseudocode description:

  1. Initialize the processor.
  2. Initialize the communication channel (serial port, keyboard...).
  3. Initialize a position pointer to a fixed position.
  4. Repeat the following forever:
    Get a byte from the communication channel.
    If byte = 0 [execute]
         Jump to the subroutine at the fixed position.
    Else If byte = 1 [compile]
         A. Get a byte from the communication channel.
         B. Store the byte at the position pointer.
         C. Increment the position pointer.
    End If.

#Full code:

bits 16 cpu 8086 [org 0x7c00] ; boot load address EXECUTE equ 0x7e00 ; address where code should be compiled / executed ;;;-------------------- ;;; Init ;;;-------------------- init: mov ax,EXECUTE mov ds,ax xor di,di ;;;-------------------- ;;; Interpreter loop ;;;-------------------- loop: call getchar cmp al,0 jne .skip jmp EXECUTE:0x0 .skip: cmp al,1 je compile jmp loop ;;;-------------------- ;;; 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

#Keyboard version:

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:

Keyboard version

#Questions

  1. 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.
    You can load/redefine code at will.

    Even if it lacks stacks, dictionnary, defining words, etc. It has the simplicity and hacky feeling of Forth.

  2. 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?

  3. 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.

Read Entire Article