commit e6e821d66191046a366dc005416d811743279882
parent e736981c7b65fa1b69905d0e7b162cd43668c3e3
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Sat, 23 Jul 2022 07:30:02 -0400
i386: begin structuring the boot binary
It's getting exciting!
Diffstat:
8 files changed, 71 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
@@ -195,6 +195,16 @@ and echoed twice. To avoid that, you can invoke it like this:
You might be interested in [this thread from the mailing list][5].
+### Building bare metal binaries
+
+`make run` builds and runs a binary designed to work on a POSIX platform, but
+if you want to build binaries designed to run on bare metal, it's another
+matter.
+
+For now, only the x86 PC platform is supported (work in progress in fact) and
+can be built with `make pc.bin`. If you have QEMU installed, you can run it
+right away with `make pcrun`.
+
[1]: http://collapseos.org
[2]: http://collapseos.org/why.html
[3]: https://sr.ht/~vdupras/duskos
diff --git a/buildpc.fs b/buildpc.fs
@@ -1,6 +1,6 @@
: spit ( a u -- ) >r begin c@+ stderr next ;
-f<< /asm/i386.fs
+1 to fecho
f<< /xcomp/pc/mbr.fs
-ORG $200 spit
+org $200 spit
f<< /xcomp/i386.fs
-ORG here ORG - spit bye
+org here org - spit bye
diff --git a/fs/asm/i386.fs b/fs/asm/i386.fs
@@ -190,7 +190,7 @@ $04e9 op jmp, $02e8 op call,
: callfar, ( seg16 absaddr ) $9a c, dw, w, ;
: forward! ( jmpaddr -- )
- c@+ $0f = if 1+ then pc over - ( a rel )
+ c@+ $0f = if 1+ then here over - ( a rel )
is16bit? if 2 - swap w! else 4 - swap ! then ;
\ Single operand
diff --git a/fs/asm/label.fs b/fs/asm/label.fs
@@ -8,4 +8,6 @@
: abs>rel ( a -- rel32 ) pc - ;
-: forward here 0 ;
+: forward here $10000 ;
+: forward16 here $100 ;
+: forward8 here 0 ;
diff --git a/fs/doc/asm.txt b/fs/doc/asm.txt
@@ -40,7 +40,7 @@ If the jump you want to use takes a relative address, you can use "abs>rel":
mylabel abs>rel jmp,
Forward jumps are something else. We don't know in advance what's the target pc,
-so what we need to do is to emit the jump with a dummy value (0) and save the
+so what we need to do is to emit the jump with a dummy value and save the
address where the jump was written in a label. Then, when we reach that forward
label location, we go back to the jump address saved and fiddle with its offset.
@@ -61,3 +61,12 @@ and you would be quite right to ask these questions, because it's a tricky one.
The answer is that the "forward!" word is implemented in an arch-specific manner
and that it itself reads the target address and decode its own opcodes to answer
those questions.
+
+Another question: how does "forward" know in advance the magnitude (4b? 2b? 1b?)
+of the jump? In most assemblers, jump words auto-detect their width based on the
+value they are passed.
+
+Answer: It doesn't. "forward" sends to jmp a value that intentionally doesn't
+fit in 2b so that, by default, forward jumps don't end have space problems (at
+the expense of binary space). If you want to create tighter forward jumps, use
+"forward16" or "forward8" instead.
diff --git a/fs/xcomp/i386.fs b/fs/xcomp/i386.fs
@@ -2,9 +2,17 @@
\ has finished loading this binary as well as the Forth boot code following it
\ in memory. We're in protected mode and all segments have been initialized.
\ ESP and EBP are uninitialized.
+?f<< /xcomp/tools.fs
-$8000 const BINSTART \ This code lives at $8000.
-create ORG
-ax $07690748 i) mov,
-$b8000 m) ax mov,
-0 jmp, \ infinite loop
+0 value L1
+$8000 to binstart \ This code lives at $8000.
+here to org
+forward8 jmp, to L1
+
+xcode bye 0 jmp,
+
+xcode mainloop
+ L1 forward!
+ ax $07690748 i) mov,
+ $b8000 m) ax mov,
+ xwordlbl bye abs>rel call,
diff --git a/fs/xcomp/pc/mbr.fs b/fs/xcomp/pc/mbr.fs
@@ -1,19 +1,20 @@
\ x86 bootloader
+?f<< /asm/i386.fs
\ Bootsector is loaded at address $7c00
-$7c00 const BINSTART
-BINSTART $1e0 + const GDTADDR
+$7c00 to binstart
+binstart $1e0 + const GDTADDR
0 value L1
1 to realmode
-create ORG here $400 0 fill
-forward jmp, to L1 \ bypass BPB
+here to org here $400 0 fill
+forward8 jmp, to L1 \ bypass BPB
$23 allot L1 forward!
cli, cld, GDTADDR m) lgdt,
ax $0003 i) mov, $10 int, \ video mode 80x25
\ read sector 2 from boot floppy in memory at address $8000
ax $0201 i) mov, bx $8000 i) mov, cx $0002 i) mov, dx $0000 i) mov, $13 int,
ax cr0 mov, ax 1 i) or, cr0 ax mov,
-$08 0 jmpfar, here ORG - BINSTART + here 4 - w! 0 to realmode
+$08 0 jmpfar, here org - binstart + here 4 - w! 0 to realmode
\ initialize all segments
ax 16 i) mov,
ds ax mov,
@@ -24,11 +25,11 @@ fs ax mov,
\ Jump to payload
$08 $8000 jmpfar,
\ Inspired by GNU grub at grub-core/kern/i386/realmode.S
-ORG $1e0 + to here \ GDT
+org $1e0 + to here \ GDT
\ First entry (the null entry) is a reference to itself
$17 w, GDTADDR , 0 w,
\ Code segment. base=0, limit=ffffff*4kb, present, execute/read, DPL=0
$ffff w, 0 w, 0 c, $9a c, $cf c, 0 c,
\ Data segment. base=0, limit=ffffff*4kb, present, read/write, DPL=0
$ffff w, 0 w, 0 c, $92 c, $cf c, 0 c,
-ORG $1fe + to here $55 c, $aa c,
+org $1fe + to here $55 c, $aa c,
diff --git a/fs/xcomp/tools.fs b/fs/xcomp/tools.fs
@@ -0,0 +1,23 @@
+\ Tools for cross-compiling binaries
+\ In Dusk, we don't support cross-compiling Forth. It's too hairy. Here, we're
+\ only talking about cross-compiling assembler code, with, optionally, a Forth
+\ dict in it.
+
+?f<< /lib/xdict.fs
+?f<< /asm/label.fs \ org and binstart are there
+
+newxdict xbindict
+
+: xoffset binstart org - ;
+: xcode word xbindict xentry ;
+
+\ Usage: xwordlbl foo call, "foo" being a word name in xbindict
+: xwordlbl ( "name" -- pc ) xbindict x' xoffset + ;
+
+\ Traverse xdict and change all its prev field values so that they are related
+\ to current org+binstart. Once this is done, the xdict can't be traversed
+\ anymore and becomes opaque binary contents.
+: orgifydict ( xdict -- )
+ begin ( a )
+ ?dup while ( a ) @ dup xoffset + over ! ( prev ) repeat ;
+