~oriansj/bootstrap-seeds

dcc92458cd3b4c70e97b06d197d1d66f24be971c — rick-masters 9 months ago e4974af
Add documentation and builder-hex0-x86-stage1 seed.
A NATIVE/x86/README.md => NATIVE/x86/README.md +63 -0
@@ 0,0 1,63 @@
# x86 Native Programs

The programs in this directory run as the initial boot program for an x86 machine.

They do not require a pre-existing POSIX kernel. Instead, they use the "native" interfaces provided by the machine.

The two most common native interfaces for an x86 machine are BIOS and UEFI. The following programs use the BIOS interface.


# stage0\_monitor

This program reads hexadecimal values from the keyboard, converts them to binary and writes them to memory starting at address 0x10000. It also allow executing the resulting code.

The stage0\_monitor.img file can be used as a drive image.

For example, it be run using the Qemu emulator with this command:

```
qemu-system-i386 -nographic -drive file=stage0_monitor.img,format=raw
```


# builder-hex0-x86-stage1

The builder-hex0-x86-stage1 tool is a tiny (200 byte) bootable program which contains a hex0 compiler which loads and compiles source from disk(starting at sector 2), places it into memory starting at address 0x7E00 and jumps to the resulting executable. Note that the stage1 code is only 200 bytes but it must be padded to 512 bytes with the Master Boot Record identifier bytes (0x55 0xAA) placed at the end to form a proper boot sector.

Like stage0\_monitor, builder-hex0-x86-stage1 is not particularly useful by itself but is enough to be combined with more source code to form a bootstrapping path to much more functionality.

For example, builder-hex0-x86-stage1 can be used to compile and run builder-hex0-x86-stage2. Builder-hex0-x86-stage2 is composed of a tiny POSIX kernel, shell, and file system that can be used to build software. Specifically, it has enough functionality to support the [stage0-posix-x86](https://github.com/oriansj/stage0-posix-x86) bootstrap and to run [live-bootstrap](https://github.com/fosslinux/live-bootstrap) up to the tcc compiler. Builder-hex0-x86-stage2 is not bootable by itself and cannot be used as a binary seed and so it is not hosted here.

All builder-hex0 programs are hosted [here](https://github.com/ironmeld/builder-hex0) where you can find further information and helper scripts for building the programs and running them under Qemu.  

The following is an example of building the POSIX x86 hex0-seed using helper scripts from the builder-hex0 repository. You could adapt them to build kaem-optional-seed as well.


## Building hex0-seed using builder-hex0-x86-stage1 and builder-hex0-x86-stage2

```
git clone https://github.com/ironmeld/builder-hex0
cd builder-hex0

make clean
make

cd BUILD

git clone https://github.com/oriansj/bootstrap-seeds
SEED_DIR=bootstrap-seeds/POSIX/x86

# create dev directory so we can write to /dev/hda
echo "src 0 /dev" > hex0.src

cp $SEED_DIR/hex0_x86.hex0 .

# Create commands to load hex0_x86.hex0 source, compile, and write result to /dev/hda
../hex0-to-src.sh ./hex0_x86.hex0 >> hex0.src

# Create disk image from seed and source, boot it, and extract result from resulting disk image
../build-stages.sh builder-hex0-x86-stage1.bin ../builder-hex0-x86-stage2.hex0 hex0.src hex0-seed

# Verify the pre-built hex0-seed is the same as the hex0-seed we built
diff $SEED_DIR/hex0-seed hex0-seed
```

A NATIVE/x86/builder-hex0-x86-stage1.bin => NATIVE/x86/builder-hex0-x86-stage1.bin +0 -0
A NATIVE/x86/builder-hex0-x86-stage1.hex0 => NATIVE/x86/builder-hex0-x86-stage1.hex0 +209 -0
@@ 0,0 1,209 @@
# SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com>
# SPDX-License-Identifier: MIT
#
# Builder-Hex0 is a small bootable machine image which acts as
# a bootloader using a hex0 compiler. It compiles hex0 code starting
# at sector 2, placing the compiled code at address 0x7E00 and then
# and then jumps to the resulting binary.
#
# hex0 is a "language" for binary encoding in hexadecimal
# with support for comments.

# Functions:
# _start
# read_sector
# read
# compile

#------------------------------------------------------------
# Memory:
#  9FC00 -  FFFFF BIOS
#   7C00 -   7E00 MBR/code
#   7A00 -   7BFF sector read buffer
# < 7700          real mode stack
#------------------------------------------------------------


# ------------------------------------------------------------
# Stub Entry Point
#
# boot drive is in dl
#
#[7C00][15]
#:_start

# We cannot be sure the registers are initialized to zero so we
# do that first. We far jump to mbr_main in order to set CS.
31 C0           # xor ax, ax
8E D8           # mov ds, ax
8E C0           # mov es, ax
8E D0           # mov ss, ax
BC 00 77        # mov sp, 0x7700
FC              # cld ; clear direction flag

#----------------------------------------
# Compile hex0 to binary
# compile(dl=boot_drive):
#[7C0C]
BF 00 7E        # mov di, 0x7E00

# this flag is set after the first digit is seen
31 DB           # xor bx,bx

#:read_loop
9A 91 7C 00 00  # call read
84 C0           # test al, al
74 4D           # jz finish

3C 23           # cmp al, '#'
74 28           # jz skip_comment

3C 3B           # cmp ';'
74 24           # jz skip_comment

3C 66           # cmp al, 'f'
7F EB           # jg read_loop

3C 61           # cmp al, 'a'
7C 04           # jl maybe_upper

# Handle a to f
2C 57           # sub al, 'a'-10 == 87 = 0x57
EB 23           # jmp maybe_store

#:maybe_upper
3C 46           # cmp al, 'F'
7F DF           # jg read_loop

3C 41           # cmp al, 'A'
7C 04           # jl maybe_digit

# Handle A to F
2C 37           # sub al, 'A'-10 == 55 = x37
EB 17           # jmp maybe_store

#:maybe_digit
3C 39           # cmp al, '9'
7F D3           # jg read_loop

3C 30           # cmp al, '0'
7C CF           # jl read_loop

# Handle 0 to 9
2C 30           # sub al, '0' == x30
EB 0B           # jmp maybe_store

#:skip_comment
9A 91 7C 00 00  # call read
3C 0A           # cmp al, '\n'
75 F7           # jnz skip_comment
EB C0           # jmp read_loop

# only store on second digit
#:maybe_store
84 DB           # test bl, bl
75 09           # jnz second_digit

# If on first digit, record and keep going
#:first_digit
C0 E0 04        # shl al, 4
88 C7           # mov bh, al
FE C3           # inc bl
EB B3           # jmp read_loop

# If on second digit, store and clear state
#:second_digit
08 C7           # or bh, al
88 F8           # mov al, bh
AA              # stosb
31 DB           # xor bx, bx

EB AA           # jmp read_loop

#:finish
EA 00 7E 00 00  # ljmp $0000:7E00       ; jump to stage2


#[7C6C][  ]
#:read_sector(di = *dest_addr, cx=cylinder/sector, dh = head, dl=drive)
#
# returns: di - next byte to write to
#          cx,dh - next disk sector to read from
#
50              # push ax
53              # push bx

89 FB           # mov bx, di      ; int 13 writes to bx

#:read_one_loop
B4 02           # mov ah, 2        ; rw mode = 02 (read)
B0 01           # mov al, 1        ; num_sectors
CD 13           # int 0x13
72 F8           # jnc read_one_loop
3C 01           # cmp al, 1
75 F4           # jnz read_one_loop

80 F9 3F        # cmp cl, 0x3f        ; if sector_num == max_sector
74 04           # jz next_head        ;      goto next_head
FE C1           # inc cl              ; else sector_num++;
EB 04           # jmp cleanup

#next_head:
FE C6           # inc dh              ; else head_num++
B1 01           # mov cl, 1           ; sector = 1

5B              # pop bx
58              # pop ax
CB              # retf


#----------------------------------------
# last_read_location
#[7C8C]
02 00    ; last_cylinder/sector
00       ; last_head
FF 01    ; last_byte

#[7C91]
#:read()
53                    # push bx
51                    # push cx
52                    # push dx
56                    # push si
57                    # push di

# get current position
BB 8C 7C              # mov bx, last_read_location
8B 0F                 # mov cx, [bx]
8A 77 02              # mov dh, [bx+2]
8B 47 03              # mov ax, [bx+3]

#end of sector?
3D FF 01              # cmp ax, 0x01ff
74 03                 # je next sector

#nextchar:
40                    # inc ax
EB 0F                 # jmp getchar

#read next sector
BF 00 78              # mov di, 0x7800
9A 6C 7C 00 00        # call read_sector
# save new location and offset
89 0F                 # mov [bx], cx
88 77 02              # mov [bx+2], dh
31 C0                 # xor ax, ax

#getchar:
89 47 03              # mov [bx+3], ax
BE 00 78              # mov si, 0x7800
89 C3                 # mov bx, ax
8A 00                 # mov al, [si+bx]

#finish:
5F                    # pop di
5E                    # pop si
5A                    # pop dx
59                    # pop cx
5B                    # pop bx
CB                    # ret