commit ddbe4c7fd773650c216c89c8d8de5358ab9bb856
parent d27c3be65ff94d7382ee81f21e05142a3e89e4ad
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Thu, 21 Jul 2022 08:16:01 -0400
asm/i386: make all jumps relative
Instead of trying to shoehorn absolute jumps into the jmp, op, keep them all
as raw as possible (see comment in asm/i386) and keep that shoehorning for
another layer.
Diffstat:
4 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/fs/asm/i386.fs b/fs/asm/i386.fs
@@ -24,7 +24,6 @@ SZ32 value opsz
0 value imm \ value of current immediate, if set
0 value disp \ displacement value
0 value sib \ value of the SIB byte
-0 value ORG \ base address for jmp, and call, rel32 computing
\ Utilities
: asm$ SZ32 to opsz 1 to opdirec 3 to opmod -1 to opreg -1 to oprm 0 to imm? ;
@@ -139,8 +138,18 @@ AL _ al BL _ bl CL _ cl DL _ dl AH _ ah BH _ bh CH _ ch DH _ dh
: op ( opcode -- ) doer c, does> ( a -- ) c@ c, asm$ ;
$c3 op ret,
+\ Jumps and relative addresses
+\ i386 jumps and calls in their immediate modes are relative. We keep it that
+\ way. However, those relative addresses are inconvenient to use because it's
+\ relative to the *end* of the op, which can be 2, 3 or 5 bytes in size. This
+\ makes it very inconvenient to use reliably because the caller has to be aware
+\ 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 - ;
+
\ Conditional jumps
-: op ( opcode -- ) doer , does> ( rel32 a -- ) @ op, , ;
+: op ( opcode -- ) doer , does> ( rel32 a -- ) @ op, 5 - , ;
$840f op jz, $850f op jnz,
\ JMP and CALL
@@ -150,7 +159,7 @@ $840f op jz, $850f op jnz,
\ the "opreg" for the modrm version.
: op ( opcode -- ) doer , does> @ ( rel32? opcode -- )
opreg 0< if ( rel32 opcode )
- c, here - ORG + 4 - , asm$
+ c, 5 - , asm$
else \ ( opcode )
8 rshift opreg! $ff opmodrm,
then ;
diff --git a/fs/cc/vm.fs b/fs/cc/vm.fs
@@ -218,7 +218,7 @@ operands value 'curop
\ pop it with vmpspop,
: vmcall, ( -- )
VM_*CONSTANT optype = if oparg VM_NONE optype! else opAsm then
- call, opdeinit 0 to callsz ;
+ abs>rel call, opdeinit 0 to callsz ;
\ Allocate a new register for active op and pop 4b from PS into it.
: vmpspop,
@@ -329,7 +329,7 @@ operands value 'curop
: ]vmjmp ( 'jump_addr -- ) here over - 4 - swap ! ;
: _ here 4 - ;
-: vmjmp, ( a -- ) jmp, ;
+: vmjmp, ( a -- ) abs>rel jmp, ;
: vmjmp[, ( -- a ) 0 vmjmp, _ ;
: vmjz, ( -- addr )
selop1 opAsm opAsm test, opdeinit
diff --git a/fs/tests/asm/i386.fs b/fs/tests/asm/i386.fs
@@ -21,7 +21,7 @@ foo2 1234 #eq
\ test call in its different forms
code foo3
- ' foo1 call,
+ ' foo1 abs>rel call,
ax ' foo1 i) mov,
ax call,
ret,
diff --git a/fs/xcomp/pc/mbr.fs b/fs/xcomp/pc/mbr.fs
@@ -1,10 +1,12 @@
\ x86 bootloader
?f<< /asm/i386.fs
-here to ORG here $200 0 fill
+\ TODO: we're in 16bit real address mode, get out of it.
+create ORG here $200 0 fill
$26 jmp, \ bypass BPB
$21 allot
ax $0e58 i) mov,
$10 int,
-here jmp,
+\ manually encoded 16bit infinite loop
+$e9 c, $fd c, $ff c,
$55 ORG $1fe + c!+ $aa swap c!