diff options
| author | Irene Knapp <ireneista@irenes.space> | 2026-05-22 05:32:50 -0700 |
|---|---|---|
| committer | Irene Knapp <ireneista@irenes.space> | 2026-05-22 05:34:11 -0700 |
| commit | 02a165b1e5ce2960207976e0c27b568ab2d33f8d (patch) | |
| tree | 008c533f786953c99d9a3e0700c4dd5430dd34b3 | |
| parent | 3f3bda4692b4017c7ad6ed1c434313b9dcc62cfb (diff) | |
de-branch'd all the internals
so it's all just high-level flow control now, which makes it load in itself the output isn't identical on the first self-compilation, but it converges on the second with this, it is officially self-hosting. WOW. Force-Push: yes Change-Id: Ic14de1dae209b200cada269b07c3826c86fa494f
| -rw-r--r-- | amd64.e | 156 | ||||
| -rw-r--r-- | input.e | 60 | ||||
| -rw-r--r-- | output.e | 221 |
3 files changed, 205 insertions, 232 deletions
diff --git a/amd64.e b/amd64.e index 4d3c734..8e53822 100644 --- a/amd64.e +++ b/amd64.e @@ -145,64 +145,62 @@ s" :cc-greater" keyword ~ (register -- 3-bit encoded value for register) : reg64 - ~ In counting the words for the branches, notice that each integer literal - ~ is two words. - dup :rax = 0branch [ 5 8 * , ] drop 0 exit - dup :rcx = 0branch [ 5 8 * , ] drop 1 exit - dup :rdx = 0branch [ 5 8 * , ] drop 2 exit - dup :rbx = 0branch [ 5 8 * , ] drop 3 exit - dup :rsp = 0branch [ 5 8 * , ] drop 4 exit - dup :rbp = 0branch [ 5 8 * , ] drop 5 exit - dup :rsi = 0branch [ 5 8 * , ] drop 6 exit - dup :rdi = 0branch [ 5 8 * , ] drop 7 exit + dup :rax = { drop 0 exit } if + dup :rcx = { drop 1 exit } if + dup :rdx = { drop 2 exit } if + dup :rbx = { drop 3 exit } if + dup :rsp = { drop 4 exit } if + dup :rbp = { drop 5 exit } if + dup :rsi = { drop 6 exit } if + dup :rdi = { drop 7 exit } if ." Parameter to reg64 is not a reg64." 1 sys-exit ; ~ (register -- 3-bit encoded value for register) : extrareg64 - dup :r8 = 0branch [ 5 8 * , ] drop 0 exit - dup :r9 = 0branch [ 5 8 * , ] drop 1 exit - dup :r10 = 0branch [ 5 8 * , ] drop 2 exit - dup :r11 = 0branch [ 5 8 * , ] drop 3 exit - dup :r12 = 0branch [ 5 8 * , ] drop 4 exit - dup :r13 = 0branch [ 5 8 * , ] drop 5 exit - dup :r14 = 0branch [ 5 8 * , ] drop 6 exit - dup :r15 = 0branch [ 5 8 * , ] drop 7 exit + dup :r8 = { drop 0 exit } if + dup :r9 = { drop 1 exit } if + dup :r10 = { drop 2 exit } if + dup :r11 = { drop 3 exit } if + dup :r12 = { drop 4 exit } if + dup :r13 = { drop 5 exit } if + dup :r14 = { drop 6 exit } if + dup :r15 = { drop 7 exit } if ." Parameter to extrareg64 is not an extrareg64." 1 sys-exit ; ~ (register -- 3-bit encoded value for register) : reg32 - dup :eax = 0branch [ 5 8 * , ] drop 0 exit - dup :ecx = 0branch [ 5 8 * , ] drop 0 exit - dup :edx = 0branch [ 5 8 * , ] drop 0 exit - dup :ebx = 0branch [ 5 8 * , ] drop 0 exit - dup :esp = 0branch [ 5 8 * , ] drop 0 exit - dup :ebp = 0branch [ 5 8 * , ] drop 0 exit - dup :esi = 0branch [ 5 8 * , ] drop 0 exit - dup :edi = 0branch [ 5 8 * , ] drop 0 exit + dup :eax = { drop 0 exit } if + dup :ecx = { drop 0 exit } if + dup :edx = { drop 0 exit } if + dup :ebx = { drop 0 exit } if + dup :esp = { drop 0 exit } if + dup :ebp = { drop 0 exit } if + dup :esi = { drop 0 exit } if + dup :edi = { drop 0 exit } if ." Parameter to reg32 is not a reg32." 1 sys-exit ; ~ (register -- 3-bit encoded value for register) : reg16 - dup :ax = 0branch [ 5 8 * , ] drop 0 exit - dup :cx = 0branch [ 5 8 * , ] drop 1 exit - dup :dx = 0branch [ 5 8 * , ] drop 2 exit - dup :bx = 0branch [ 5 8 * , ] drop 3 exit - dup :sp = 0branch [ 5 8 * , ] drop 4 exit - dup :bp = 0branch [ 5 8 * , ] drop 5 exit - dup :si = 0branch [ 5 8 * , ] drop 6 exit - dup :di = 0branch [ 5 8 * , ] drop 7 exit + dup :ax = { drop 0 exit } if + dup :cx = { drop 1 exit } if + dup :dx = { drop 2 exit } if + dup :bx = { drop 3 exit } if + dup :sp = { drop 4 exit } if + dup :bp = { drop 5 exit } if + dup :si = { drop 6 exit } if + dup :di = { drop 7 exit } if ." Parameter to reg16 is not a reg16." 1 sys-exit ; ~ (register -- 3-bit encoded value for register) : reg8 - dup :al = 0branch [ 5 8 * , ] drop 0 exit - dup :cl = 0branch [ 5 8 * , ] drop 1 exit - dup :dl = 0branch [ 5 8 * , ] drop 2 exit - dup :bl = 0branch [ 5 8 * , ] drop 3 exit - dup :ah = 0branch [ 5 8 * , ] drop 4 exit - dup :ch = 0branch [ 5 8 * , ] drop 5 exit - dup :dh = 0branch [ 5 8 * , ] drop 6 exit - dup :bh = 0branch [ 5 8 * , ] drop 7 exit + dup :al = { drop 0 exit } if + dup :cl = { drop 1 exit } if + dup :dl = { drop 2 exit } if + dup :bl = { drop 3 exit } if + dup :ah = { drop 4 exit } if + dup :ch = { drop 5 exit } if + dup :dh = { drop 6 exit } if + dup :bh = { drop 7 exit } if ." Parameter to reg8 is not a reg8." 1 sys-exit ; @@ -215,10 +213,10 @@ s" :cc-greater" keyword ~ ~ (scale factor -- 2-bit encoded value) : scalefield - dup 1 = 0branch [ 5 8 * , ] drop 0 exit - dup 2 = 0branch [ 5 8 * , ] drop 1 exit - dup 5 = 0branch [ 5 8 * , ] drop 2 exit - dup 8 = 0branch [ 5 8 * , ] drop 3 exit + dup 1 = { drop 0 exit } if + dup 2 = { drop 1 exit } if + dup 5 = { drop 2 exit } if + dup 8 = { drop 3 exit } if ." Parameter to scalefield is not 1, 2, 4, or 8." 1 sys-exit ; @@ -238,22 +236,22 @@ s" :cc-greater" keyword ~ ~ (condition -- 4-bit encoded value) : condition-code - dup :cc-overflow = 0branch [ 5 8 * , ] drop 0 exit - dup :cc-no-overflow = 0branch [ 5 8 * , ] drop 1 exit - dup :cc-below = 0branch [ 5 8 * , ] drop 2 exit - dup :cc-above-equal = 0branch [ 5 8 * , ] drop 3 exit - dup :cc-equal = 0branch [ 5 8 * , ] drop 4 exit - dup :cc-not-equal = 0branch [ 5 8 * , ] drop 5 exit - dup :cc-below-equal = 0branch [ 5 8 * , ] drop 6 exit - dup :cc-above = 0branch [ 5 8 * , ] drop 7 exit - dup :cc-sign = 0branch [ 5 8 * , ] drop 8 exit - dup :cc-not-sign = 0branch [ 5 8 * , ] drop 9 exit - dup :cc-even = 0branch [ 5 8 * , ] drop 10 exit - dup :cc-odd = 0branch [ 5 8 * , ] drop 11 exit - dup :cc-less = 0branch [ 5 8 * , ] drop 12 exit - dup :cc-greater-equal = 0branch [ 5 8 * , ] drop 13 exit - dup :cc-less-equal = 0branch [ 5 8 * , ] drop 14 exit - dup :cc-greater = 0branch [ 5 8 * , ] drop 15 exit + dup :cc-overflow = { drop 0 exit } if + dup :cc-no-overflow = { drop 1 exit } if + dup :cc-below = { drop 2 exit } if + dup :cc-above-equal = { drop 3 exit } if + dup :cc-equal = { drop 4 exit } if + dup :cc-not-equal = { drop 5 exit } if + dup :cc-below-equal = { drop 6 exit } if + dup :cc-above = { drop 7 exit } if + dup :cc-sign = { drop 8 exit } if + dup :cc-not-sign = { drop 9 exit } if + dup :cc-even = { drop 10 exit } if + dup :cc-odd = { drop 11 exit } if + dup :cc-less = { drop 12 exit } if + dup :cc-greater-equal = { drop 13 exit } if + dup :cc-less-equal = { drop 14 exit } if + dup :cc-greater = { drop 15 exit } if ." Parameter to condition-code is not a condition code." 1 sys-exit ; @@ -399,15 +397,16 @@ s" :cc-greater" keyword ~ -- output point) : addressing-indirect-reg64 ~ Exit with an error if the R/M register is :rbp. - dup :rbp != 0branch [ 23 8 * , ] - ~ Check whether the R/M register is :rsp. Save the test result for later. - dup :rsp = 4 unroll - ~ (equality result, output point, reg/op value, reg/mem name) - reg64 0 3unroll modrm - ~ (equality result, output point) - ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it. - swap 0branch [ 8 8 * , ] 0 4 :rsp reg64 sib - exit + dup :rbp != { + ~ Check whether the R/M register is :rsp. Save the test result for later. + dup :rsp = 4 unroll + ~ (equality result, output point, reg/op value, reg/mem name) + reg64 0 3unroll modrm + ~ (equality result, output point) + ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it. + swap { 0 4 :rsp reg64 sib } if + exit + } if ." R/M parameter to addressing-indirect-reg64 is :rbp." 1 sys-exit ; ~ (output point, reg/op field value, reg/mem field register, @@ -420,7 +419,7 @@ s" :cc-greater" keyword 4 unroll reg64 1 3unroll modrm ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it. - 3roll 0branch [ 8 8 * , ] 0 4 :rsp reg64 sib + 3roll { 0 4 :rsp reg64 sib } if ~ The displacement byte. swap pack8 ; @@ -434,7 +433,7 @@ s" :cc-greater" keyword 4 unroll reg64 2 3unroll modrm ~ If the R/M register was rsp, we need an SIB byte; otherwise, skip it. - 3roll 0branch [ 8 8 * , ] 0 4 :rsp reg64 sib + 3roll { 0 4 :rsp reg64 sib } if ~ The displacement value. swap pack32 ; @@ -443,11 +442,12 @@ s" :cc-greater" keyword ~ -- output point) : addressing-indexed-reg64 ~ Exit with an error if the base register is :rbp. - dup :rbp != 0branch [ 23 8 * , ] - ~ Reg/mem value 4 means to use an SIB byte (at least, with this mode). - 5 roll 0 6 roll 4 modrm 4 unroll - reg64 3unroll reg64 3unroll scalefield 3unroll sib - exit + dup :rbp != { + ~ Reg/mem value 4 means to use an SIB byte (at least, with this mode). + 5 roll 0 6 roll 4 modrm 4 unroll + reg64 3unroll reg64 3unroll scalefield 3unroll sib + exit + } if ." Base parameter to addressing-indexed-reg64 is :rbp." 1 sys-exit ; ~ (output point, reg/op field value, diff --git a/input.e b/input.e index 8203673..dd1da1c 100644 --- a/input.e +++ b/input.e @@ -97,7 +97,7 @@ ~ (metadata pointer --) : consume-from ~ If the length is zero, exit without doing anything. - dup buffer-logical-length @ 0 = 0branch [ 2 8 * , ] exit + dup buffer-logical-length @ 0 = { exit } if ~ Decrement the logical length. We do this now to get it over with, since ~ adjusting the start pointer is more complex. @@ -116,13 +116,13 @@ ~ Check whether the updated start is equal to the physical end. dup 4 roll = ~ (metadata pointer, updated start, updated start, physical end) - 0branch [ 5 8 * , ] - - ~ If the logical start pointer is now equal to the physical end pointer, - ~ we want to wrap to the physical start. That's what makes it a circular - ~ buffer. - ~ (metadata pointer, updated start) - drop dup buffer-physical-start @ + { + ~ If the logical start pointer is now equal to the physical end pointer, + ~ we want to wrap to the physical start. That's what makes it a circular + ~ buffer. + ~ (metadata pointer, updated start) + drop dup buffer-physical-start @ + } if ~ However we got here, save the updated logical start pointer. ~ (metadata pointer, updated start) @@ -131,28 +131,28 @@ ~ (metadata pointer -- byte or 0) : peek-from - dup buffer-logical-length @ 0 = 0branch [ 28 8 * , ] - - ~ If the length is zero, there is no input, but we can still try calling - ~ the "refill" word. - ~ (metadata pointer) - dup input-buffer-refill @ dup 0branch [ 17 8 * , ] - - ~ If the refill word is nonzero, call it. It expects a copy of the metadata - ~ pointer as its parameter, so set that up. - ~ (metadata pointer, refill word) - swap dup 3roll execute - ~ (metadata pointer) - ~ Now we check if the length is still zero. - dup buffer-logical-length @ 0 = 0branch [ 10 8 * , ] - - ~ The length is zero even after calling the refill word, so return null. - ~ (metadata pointer) - drop 0 exit - - ~ If the refill word is zero, we can't help, just return null. - ~ (metadata pointer, refill word) - drop drop 0 exit + dup buffer-logical-length @ { + ~ If the length is zero, there is no input, but we can still try calling + ~ the "refill" word. + ~ (metadata pointer) + dup input-buffer-refill @ dup { + ~ If the refill word is zero, we can't help, just return null. + ~ (metadata pointer, refill word) + drop drop 0 exit + } unless + + ~ If the refill word is nonzero, call it. It expects a copy of the metadata + ~ pointer as its parameter, so set that up. + ~ (metadata pointer, refill word) + swap dup 3roll execute + ~ (metadata pointer) + ~ Now we check if the length is still zero. + dup buffer-logical-length @ { + ~ The length is zero even after calling the refill word, so return null. + ~ (metadata pointer) + drop 0 exit + } unless + } unless ~ The buffer is non-empty, so read a byte from it. We might have reached ~ this point either from the original check, or from the second check after diff --git a/output.e b/output.e index 80f1577..5c4b31d 100644 --- a/output.e +++ b/output.e @@ -26,21 +26,20 @@ : pow 1 swap - ~ If the count of remaining powers is NOT equal to zero, the comparison will - ~ return 0, which will cause the zbranch to skip the rest of the line. - dup 0 = 0branch [ 5 8 * , ] drop swap drop exit - ~ (base, result so far, count of remaining powers) + { + ~ If the count of remaining powers is equal to zero, exit. + dup { drop swap drop exit } unless + ~ (base, result so far, count of remaining powers) - 1 - 3unroll - ~ (updated count of remaining powers, base, result so far) + 1 - 3unroll + ~ (updated count of remaining powers, base, result so far) - swap dup 4 unroll - ~ (base, updated count of remaining powers, result so far, base) + swap dup 4 unroll + ~ (base, updated count of remaining powers, result so far, base) - * swap - ~ (base, updated result so far, updated count of remaining powers) - - branch [ -22 8 * , ] ; + * swap + ~ (base, updated result so far, updated count of remaining powers) + } forever ; ~ (base, value -- floor of logarithm of value in base base) @@ -48,29 +47,28 @@ ~ Start with a product equal to the base, and a count of 0. swap dup 3unroll 0 - ~ This is the start of the loop body. - ~ (base, value, product so far, count of powers included so far) - 3unroll 2dup - ~ (base, count so far, value, product so far) - >unsigned 0branch [ 6 8 * , ] + { + ~ This is the start of the loop body. + ~ (base, value, product so far, count of powers included so far) + 3unroll 2dup + ~ (base, count so far, value, product so far) - ~ If we get here, we're done. - ~ (base, count so far, value, product so far) - drop drop swap drop exit + ~ If we get here, we're done. + ~ (base, count so far, value, product so far) + >unsigned { drop drop swap drop exit } if - ~ If we're here, we need to do another loop. - ~ (base, count so far, value, product so far) - 4 roll dup 5 unroll * 3roll 1 + - ~ (base, value, updated product so far, updated count so far) + ~ If we're here, we need to do another loop. + ~ (base, count so far, value, product so far) + 4 roll dup 5 unroll * 3roll 1 + + ~ (base, value, updated product so far, updated count so far) - ~ If the product is less than the base, we overflowed. In that case, the - ~ product-so-far is the maximum, so just return it. - 4 roll dup 5 unroll 3roll dup 4 unroll - < 0branch [ 8 8 * , ] - 4 unroll drop drop drop exit + ~ If the product is less than the base, we overflowed. In that case, the + ~ product-so-far is the maximum, so just return it. + 4 roll dup 5 unroll 3roll dup 4 unroll + < { 4 unroll drop drop drop exit } if - ~ Nothing else weird going on, so loop. - branch [ -45 8 * , ] ; + ~ Nothing else weird going on, so loop. + } forever ; ~ (base, value -- ceiling of logarithm of value in base base) @@ -78,29 +76,28 @@ ~ Start with a product of 1 and a count of 0. 1 0 - ~ This is the start of the loop body. - ~ (base, value, product so far, count of powers included so far) - 3unroll 2dup - ~ (base, count so far, value, product so far) - >=unsigned 0branch [ 6 8 * , ] + { + ~ This is the start of the loop body. + ~ (base, value, product so far, count of powers included so far) + 3unroll 2dup + ~ (base, count so far, value, product so far) - ~ If we get here, we're done. - ~ (base, count so far, value, product so far) - drop drop swap drop exit + ~ If we get here, we're done. + ~ (base, count so far, value, product so far) + >=unsigned { drop drop swap drop exit } if - ~ If we're here, we need to do another loop. - ~ (base, count so far, value, product so far) - 4 roll dup 5 unroll * 3roll 1 + - ~ (base, value, updated product so far, updated count so far) + ~ If we're here, we need to do another loop. + ~ (base, count so far, value, product so far) + 4 roll dup 5 unroll * 3roll 1 + + ~ (base, value, updated product so far, updated count so far) - ~ If the product is less than the base, we overflowed. In that case, the - ~ product-so-far is the maximum, so just return it. - 4 roll dup 5 unroll 3roll dup 4 unroll - < 0branch [ 8 8 * , ] - 4 unroll drop drop drop exit + ~ If the product is less than the base, we overflowed. In that case, the + ~ product-so-far is the maximum, so just return it. + 4 roll dup 5 unroll 3roll dup 4 unroll + < { 4 unroll drop drop drop exit } if - ~ Nothing else weird going on, so loop. - branch [ -45 8 * , ] ; + ~ Nothing else weird going on, so loop. + } forever ; ~ This is an extremely inefficient implementation, but on the plus side, @@ -115,47 +112,52 @@ ~ logic of always printing at least one digit is already handled for us. 3unroll swap 2dup logfloor 1 + ~ (width, base, input, number of digits if no padding) - 4 roll 2dup > 0branch [ 5 8 * , ] - ~ (base, input, number of digits if no padding, width) - ~ If we're here, we should use the padded width - swap drop branch [ 2 8 * , ] - ~ (base, input, number of digits if no padding, width) - ~ If we're here, we should use the unpadded width - drop - - ~ (base, input, number of digits remaining) - ~ This is the start of the loop. - 2dup 1 - - ~ (base, input, number of digits remaining, input, intermediate value) - 5 roll dup 6 unroll swap - ~ (base, input, number of digits remaining, input, base, intermediate value) - pow /% swap drop - ~ (base, input, number of digits remaining, - ~ input divided by base^x appropriately) - 4 roll dup 5 unroll - ~ (base, input, number of digits remaining, - ~ input divided by base^x appropriately, base) - /% drop - ~ (base, input, number of digits remaining, current digit) - - ~ We construct a one-character string on the stack, then use a pointer to - ~ it. It will always contain its own null-termination without us having to - ~ do anything special to that end. - dup 10 > 0branch [ 5 8 * , ] - 0x30 branch [ 6 8 * , ] ~ ASCII "0" - 10 - 0x61 ~ ASCII "a" - + - value@ emitstring - - ~ We deallocate the string by dropping it. - ~ - ~ Saying it like that makes it sound obvious; contemplate it until it feels - ~ surprising. - drop - - 1 - - dup 0branch [ 3 8 * , ] branch [ -51 8 * , ] - drop drop drop ; + 4 roll 2dup > { + ~ (base, input, number of digits if no padding, width) + ~ If we're here, we should use the padded width + swap drop + } { + ~ (base, input, number of digits if no padding, width) + ~ If we're here, we should use the unpadded width + drop + } if-else + + { + ~ (base, input, number of digits remaining) + ~ This is the start of the loop. + 2dup 1 - + ~ (base, input, number of digits remaining, input, intermediate value) + 5 roll dup 6 unroll swap + ~ (base, input, number of digits remaining, input, base, intermediate value) + pow /% swap drop + ~ (base, input, number of digits remaining, + ~ input divided by base^x appropriately) + 4 roll dup 5 unroll + ~ (base, input, number of digits remaining, + ~ input divided by base^x appropriately, base) + /% drop + ~ (base, input, number of digits remaining, current digit) + + ~ We construct a one-character string on the stack, then use a pointer to + ~ it. It will always contain its own null-termination without us having to + ~ do anything special to that end. + dup 10 > { + 0x30 ~ ASCII "0" + } { + 10 - 0x61 ~ ASCII "a" + } if-else + + + value@ emitstring + + ~ We deallocate the string by dropping it. + ~ + ~ Saying it like that makes it sound obvious; contemplate it until it feels + ~ surprising. + drop + + 1 - + dup { drop drop drop exit } unless + } forever ; ~ (integer to print, base to print in --) @@ -164,9 +166,10 @@ ~ (base, input) ~ Deal with negative numbers. - dup 0 > 0branch [ 7 8 * , ] - -1 * - s" -" emitstring + dup 0 > { + -1 * + s" -" emitstring + } if swap 0 .base-unsigned ; @@ -187,33 +190,3 @@ ~ (integer to print, width --) : .hexn 16 swap .base-unsigned ; - -~ Debugging tools -~ ~~~~~~~~~~~~~~~ - -~ TODO remove these altogether, they're in dynamic.e now -~ ~ ~ TODO this is a horrible, horrible hack -~ : s0-kludge 0x1000010008 ; -~ -~ ~ TODO replace these with the implementations that use proper flow-control -~ : stack -~ s0-kludge @ 8 - -~ -~ dup value@ 8 + != -~ 0branch [ 19 8 * , ] -~ dup s0-kludge @ 8 - != 0branch [ 2 8 * , ] space dup @ . 8 - -~ branch [ -25 8 * , ] -~ -~ drop newline ; -~ -~ -~ : stackhex -~ s0-kludge @ 8 - -~ -~ dup value@ 8 + != -~ 0branch [ 19 8 * , ] -~ dup s0-kludge @ 8 - != 0branch [ 2 8 * , ] space dup @ .hex64 8 - -~ branch [ -25 8 * , ] -~ -~ drop newline ; - |