~hocwp/10biForthOS

fix: use conventional memory address
fix: readme link to itself
Merge branch 'readme'

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~hocwp/10biForthOS
read/write
git@git.sr.ht:~hocwp/10biForthOS

You can also use your local clone with git send-email.

#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. [1]

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 is also provided).
The 1 instuction should be followed by 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 [2] or even sectorc [3] can be used to code in eForth or C.

#Usage

Boot the machine (here qemu)

$ ./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  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

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 [2] 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 [3] 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 0x0050              ; 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

#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 quit pleasant to be able to write an OS with so little code and be able to extend it while it is running.

[1] https://pygmy.utoh.org/3ins4th.html
[2] https://github.com/pbrochard/subleq-eForthOS
[3] https://github.com/xorvoid/sectorc

Do not follow this link