commit e736981c7b65fa1b69905d0e7b162cd43668c3e3
parent c0038ab9cb149b99b147760338d3d06af2ea0688
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Sat, 23 Jul 2022 06:29:32 -0400
asm: add labels, making forward jumps more convenient
Diffstat:
6 files changed, 88 insertions(+), 7 deletions(-)
diff --git a/Makefile b/Makefile
@@ -37,4 +37,4 @@ test: dusk
.PHONY: clean
clean:
- rm -f $(TARGETS) dusk.o fatfs
+ rm -f $(TARGETS) dusk.o fatfs pc.bin
diff --git a/fs/asm/i386.fs b/fs/asm/i386.fs
@@ -1,6 +1,5 @@
\ i386 assembler
-\ This assembler implies that the code will run in protected mode with the D
-\ attribute set.
+?f<< /asm/label.fs
\ MOD/RM constants
0 const AX 1 const CX 2 const DX 3 const BX
@@ -168,7 +167,6 @@ $c3 op ret, $90 op nop, $fa op cli, $fc op cld,
\ of the size of its relative offset. To that end, we auto adjust that relative
\ address to the size of the op. Therefore, "0 jmp," is an infinite loop encoded
\ as EB FE.
-: abs>rel ( a -- rel32 ) here - ;
: rel, ( rel32-or-16 ) is16bit? if 3 - w, else 5 - , then ;
\ Conditional jumps
@@ -191,6 +189,10 @@ $04e9 op jmp, $02e8 op call,
: jmpfar, ( seg16 absaddr ) $ea c, dw, w, ;
: callfar, ( seg16 absaddr ) $9a c, dw, w, ;
+: forward! ( jmpaddr -- )
+ c@+ $0f = if 1+ then pc over - ( a rel )
+ is16bit? if 2 - swap w! else 4 - swap ! then ;
+
\ Single operand
\ opcode format 00000000 00000rrr mmmmmmmm mmmmmmmm
\ r = opreg override
diff --git a/fs/asm/label.fs b/fs/asm/label.fs
@@ -0,0 +1,11 @@
+\ Cross-arch labels and flow
+
+0 value org
+0 value binstart
+
+: pc here org - binstart + ;
+: pc>addr ( pc -- a ) org + binstart - ;
+
+: abs>rel ( a -- rel32 ) pc - ;
+
+: forward here 0 ;
diff --git a/fs/doc/asm.txt b/fs/doc/asm.txt
@@ -0,0 +1,63 @@
+# Assemblers
+
+## Labels and flow
+
+The labels system at /asm/label is arch-neutral and rests on top of
+arch-specific jump words, which have these properties:
+
+* They take their jump target directly from PS.
+* They can be either relative or absolute jumps.
+* In the case of a relative jump, "0" means an infinite loop. When the native
+ op doesn't have the same scheme, the op itself has to adjust the offset.
+
+For jumps to work, assemblers all need to have a point of reference, which is
+called "org". It's the address at which the binary you're assembling begins. If
+you're assembling for the live system, "org" is 0, its default value. If you're
+cross-compiling, you'll want to set "org" to "here" when you begin assembling.
+
+If the binary you're assembling isn't designed to run at address 0, then you
+also need to define "binstart". For example, a x86 PC boot sector will want to
+have "binstart" set to $7c00.
+
+These 2 values give us a new word, "pc" (for Program Counter), which will yield
+the address where the next assembled byte will be return in terms of the target
+machine.
+
+Jumps to locations in Dusk is made easier with the use of labels. Labels are
+simple "value" that you declare before beginning the code.
+
+ 0 value mylabel
+
+There are two possible types of jumps, backward or forward. Backward jumps are
+easy:
+
+ pc to mylabel
+ nop, nop,
+ mylabel jmp, \ assuming "jmp," takes an absolute address
+
+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
+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.
+
+ forward jmp, to mylabel
+ nop, nop,
+ mylabel forward!
+
+That simple interface hides a few complexities we'll explain right here. You're
+probably asking yourself: how does the "forward!" word knows about the
+properties of the "jmp," op? namely:
+
+* is it absolute or relative?
+* is the immediate 1b, 2b, 4b?
+* is its opcode part 1b or 2b in length?
+* if relative, what is it's "zero point"?
+
+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.
diff --git a/fs/tests/asm/i386.fs b/fs/tests/asm/i386.fs
@@ -20,16 +20,19 @@ code foo2
foo2 1234 #eq
\ test call in its different forms
+0 value mylabel
code foo3
' foo1 abs>rel call,
ax ' foo1 i) mov,
ax call,
+ forward call, to mylabel
ret,
-foo3 42 #eq 42 #eq
+\ we test foo3 later
\ test shr/shl
code foo4
+ mylabel forward!
ax 42 i) mov,
ax 3 i) shl,
cl 2 i) mov,
@@ -39,6 +42,7 @@ code foo4
ret,
foo4 84 #eq
+foo3 84 #eq 42 #eq 42 #eq
\ test single operands
code foo5
diff --git a/fs/xcomp/pc/mbr.fs b/fs/xcomp/pc/mbr.fs
@@ -3,10 +3,11 @@
\ Bootsector is loaded at address $7c00
$7c00 const BINSTART
BINSTART $1e0 + const GDTADDR
+0 value L1
1 to realmode
create ORG here $400 0 fill
-$26 jmp, \ bypass BPB
-$23 allot
+forward 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