duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

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:
MMakefile | 2+-
Mfs/asm/i386.fs | 8+++++---
Afs/asm/label.fs | 11+++++++++++
Afs/doc/asm.txt | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfs/tests/asm/i386.fs | 6+++++-
Mfs/xcomp/pc/mbr.fs | 5+++--
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