diff options
| -rw-r--r-- | amd64.e | 49 | ||||
| -rw-r--r-- | elf.e | 2 | ||||
| -rw-r--r-- | execution.e | 14 | ||||
| -rw-r--r-- | transform.e | 117 |
4 files changed, 165 insertions, 17 deletions
diff --git a/amd64.e b/amd64.e index ba78bc4..1a5df04 100644 --- a/amd64.e +++ b/amd64.e @@ -518,10 +518,24 @@ s" :cc-greater" keyword 0 4 :rsp reg64 sib ~ : drop-subitem } if + ~ The displacement byte. + ~ + ~ When addressing-disp8-reg64 references a source, as in + ~ lea-reg64-disp8-reg64, the first two parameters of the assembly word + ~ calling it are the source register and the source displacement value. When + ~ it references a target or destination, as in mov-disp8-reg64-reg64, the + ~ LAST two parameters are the target registera and target displacement + ~ value. + ~ + ~ In both cases, the register comes first and the displacement value + ~ comes second. When these are used in a comment template without any other + ~ rearranging going on, whichever is on the top of the stack will be popped + ~ off first. So, we swap them here, thus putting the register on the top. swap ~ : 1 adjust-length ~ : provide-hex8 + ~ : swap-subitems pack8 ; ~ (output point, reg/op field value, reg/mem field register, @@ -538,10 +552,13 @@ s" :cc-greater" keyword 0 4 :rsp reg64 sib ~ : drop-subitem } if - ~ The displacement value. + + ~ The displacement value. This is subject to the same considerations as in + ~ addressing-disp8-reg64. swap ~ : 4 adjust-length ~ : provide-hex32 + ~ : swap-subitems pack32 ; ~ (output point, reg/op field value, @@ -552,7 +569,17 @@ s" :cc-greater" keyword 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 + reg64 3unroll reg64 3unroll scalefield 3unroll + ~ We want the scale factor to show after the registers, so we put it + ~ last on the stack. We also want the base register to show before the + ~ index register, so the base comes first. + ~ + ~ Importantly, we have to do this before the sib so it'll count as being + ~ inside the span the assembly word's description attaches to. + ~ + ~ : -3 roll-subitems + ~ : swap-subitems + sib exit } if ." Base parameter to addressing-indexed-reg64 is :rbp." 1 sys-exit ; @@ -568,7 +595,21 @@ s" :cc-greater" keyword swap ~ : 1 adjust-length ~ : provide-hex8 - pack8 ; + ~ + ~ We want the parameters to be shown in the order: + ~ + ~ base register, index register, index scale factor, displacement + ~ + ~ So, we put them on the stack in the opposite of that order. + ~ + ~ Importantly, we have to do this before the pack8 so it'll count as being + ~ inside the span the assembly word's description attaches to. + ~ + ~ : -4 roll-subitems + ~ : -3 roll-subitems + ~ : swap-subitems + pack8 + ; ~ Easy instructions @@ -687,7 +728,7 @@ s" :cc-greater" keyword ~ (output point, source register, target register -- output point) : mov-reg64-reg64 - ~ : 1 #swap # # mov-reg64-reg64 + ~ : 1 # # mov-reg64-reg64 3roll rex-w 0x89 pack8 3unroll swap reg64 swap addressing-reg64 ; : mov-indirect-reg64-reg64 diff --git a/elf.e b/elf.e index 82af2f5..5f0cfc4 100644 --- a/elf.e +++ b/elf.e @@ -118,7 +118,7 @@ L@' total-size pack64 ~ : -8 *size in memory ~ : blank-line - 0 pack64 ~ : 8 segment alignment + 0 pack64 ~ : -8 segment alignment ~ for relocation, but this doesn't apply to us ~ As with the file header, we use the label system to keep track of the diff --git a/execution.e b/execution.e index c6b6254..20064af 100644 --- a/execution.e +++ b/execution.e @@ -251,13 +251,13 @@ ~ and the kernel would trust us, but this gives us more options for ~ interoperating with other runtimes. ~ - 9 :rax mov-reg64-imm64 ~ mmap() - log-requested-address :rdi mov-reg64-imm64 ~ address (very arbitrary) - log-size :rsi mov-reg64-imm64 ~ size (one meg) - 0x07 :rdx mov-reg64-imm64 ~ protection (read+write+exec) - 0x22 :r10 mov-extrareg64-imm64 ~ flags (private+anonymous) - 0 :r8 mov-extrareg64-imm64 ~ file descriptor (ignored) - 0 :r9 mov-extrareg64-imm64 ~ offset (ignored) + 9 :rax mov-reg64-imm64 ~ : mmap() + log-requested-address :rdi mov-reg64-imm64 ~ : address (very arbitrary) + log-size :rsi mov-reg64-imm64 ~ : size (one meg) + 0x07 :rdx mov-reg64-imm64 ~ : protection (read+write+exec) + 0x22 :r10 mov-extrareg64-imm64 ~ : flags (private+anonymous) + 0 :r8 mov-extrareg64-imm64 ~ : file descriptor (ignored) + 0 :r9 mov-extrareg64-imm64 ~ : offset (ignored) syscall ~ The return value of the system call is in rax, we'll use it in a sec. diff --git a/transform.e b/transform.e index 9a2cb21..5bec117 100644 --- a/transform.e +++ b/transform.e @@ -2786,6 +2786,8 @@ allocate-transformation-state s" transformation-state" variable ~ ~ : provide-hex64 ~ ~ : provide-keyword ~ ~ : drop-subitem +~ ~ : swap-subitems +~ ~ : 3 roll-subitems ~ ~ By the way, the reason it's possible to write these examples is that ~ they're all embedded within an outer comment, the text you're reading, so @@ -3139,7 +3141,10 @@ allocate-transformation-state s" transformation-state" variable ~ With all this context, it likely doesn't need much explanation, but the ~ drop-subitem command will create an -entry-type-drop-substring entry. When ~ this entry is executed, it will discard an item from the substring entry -~ stack. +~ stack. Similarly, swap-subitems creates an -entry-type-swap-substrings, +~ which swaps the top two items, and roll-subitems creates an +~ -entry-type-roll-substrings, which rolls them, or unrolls them with a +~ negative parameter. ~ ~ One more thing: The order in which provide-* commands execute will most ~ likely match the order in which the corresponding bytes are output. This may @@ -3205,6 +3210,8 @@ allocate-transformation-state s" transformation-state" variable : hex-output-metadata-entry-type-push-substring-hex64 12 ; : hex-output-metadata-entry-type-push-substring-string 13 ; : hex-output-metadata-entry-type-drop-substring 14 ; +: hex-output-metadata-entry-type-swap-substrings 15 ; +: hex-output-metadata-entry-type-roll-substrings 16 ; ~ Initialize the contents of the output metadata to all zeroes. This is ~ called from hex-transform at its top level, at the very start, to make sure @@ -3252,7 +3259,8 @@ allocate-transformation-state s" transformation-state" variable || ; : is-push-substring-entry - ~ Notably, -entry-type-drop-substring is not a push-substring entry. + ~ Notably, the substring swap, drop, and roll entry types are not + ~ push-substring entries. hex-output-metadata-entry-type @ dup hex-output-metadata-entry-type-push-substring-decimal = swap dup hex-output-metadata-entry-type-push-substring-hex8 = 3roll || swap @@ -3994,6 +4002,94 @@ allocate-transformation-state s" transformation-state" variable hex-output-metadata-entry-type-drop-substring = { pop-substring-entry-stack drop } if + + dup hex-output-metadata-entry-type @ + hex-output-metadata-entry-type-swap-substrings = { + ~ Just like in hex-emit-template-string, we want to make sure to + ~ discard the zeroes we get if we underflow the stack. + pop-substring-entry-stack + pop-substring-entry-stack + swap + dup { push-substring-entry-stack } { drop } if-else + dup { push-substring-entry-stack } { drop } if-else + } if + + dup hex-output-metadata-entry-type @ + hex-output-metadata-entry-type-roll-substrings = { + ~ The parameter giving the amount to roll by is in the string field. + dup hex-output-metadata-entry-string @ + ~ (... amount to roll by) + + ~ We resist the temptation to do the memcopy math. We don't need + ~ to, since the regular Forth roll and unroll do it for us. + dup 0 < { + ~ In this scenario, we're rolling. The amount to roll by is also + ~ the number of items. + + 0 { 2dup < } { pop-substring-entry-stack 3unroll 1+ } while + drop + ~ (... items, amount to roll by / number of items) + + ~ Please notice that the items we have popped from the substring + ~ entry stack are now, on the value stack, in the reverse of the + ~ order they were on the entry stack. They will reverse again when + ~ we push them back. + ~ + ~ The amount to roll by is positive, so the desired operation is + ~ a regular roll. Since the entries are reversed right now, we + ~ unroll them, which will be what we want when we put them back. + ~ + ~ Try it out interactively with a toy example and convince + ~ yourself. It's hard to really model in your head, but it's true. + ~ The intuition is that, if you're looking at the value stack, + ~ the bottommost item there will be the topmost one after + ~ re-reversing, so we think of our operations as proceeding + ~ up-stack from there, instead of the usual operations that start + ~ at the topmost item and proceed downwards. + ~ + ~ Anyway, this little trick does a regular unroll, which acts as + ~ a roll, and keeps the value around. + dup 3unroll 1+ unroll + } if + ~ (... items if positive, amount to roll by) + + ~ We did the positive case first, which means the amount to roll + ~ by is still its original value, regardless of whether that case + ~ ran or not. So, we can check if it's negative to see if we need + ~ to do the negative case. + dup 0 > { + ~ In this scenario, we're unrolling. We negate the amount to + ~ roll by to get the amount to unroll by, which is also the number + ~ of items. + negate + + 0 { 2dup < } { pop-substring-entry-stack 3unroll 1+ } while + drop + ~ (... items, number of items) + + ~ The parameter was negative, so we were asked to do an unroll. + ~ This trick does a roll, and keeps the value. Notice that "roll" + ~ always needs a positive amount. + dup 1+ roll swap + } if + ~ (... items, number of items) + + ~ Now, regardless of which case ran, we have the items and we have + ~ a non-negative count of them. Notice that if we were asked to roll + ~ by zero, neither case ran, but that still describes what we have + ~ accurately. + + ~ Now we push them back. + 0 { 2dup < } { + 3roll + + ~ Again, if we underflowed we got zeros, and we discard them. + dup { push-substring-entry-stack } { drop } if-else + + 1+ + } while + drop drop + } if } if hex-output-metadata-next-entry } while @@ -4877,9 +4973,20 @@ allocate-transformation-state s" transformation-state" variable } if dup s" drop-subitem" stringcmp 0 = { ~ Create a new "drop substring" entry. - drop drop - 2 pick execution-token-to-entry entry-to-name - hex-output-metadata-entry-type-drop-substring swap + drop drop hex-output-metadata-entry-type-drop-substring 0 + add-hex-output-metadata-entry + exit + } if + dup s" swap-subitems" stringcmp 0 = { + ~ Create a new "swap substrings" entry. + drop drop hex-output-metadata-entry-type-swap-substrings 0 + add-hex-output-metadata-entry + exit + } if + dup s" roll-subitems" stringcmp 0 = { + ~ Create a new "roll substrings" entry. The parameter goes in the string + ~ field, and the length is always zero. + drop drop 0 swap hex-output-metadata-entry-type-roll-substrings swap add-hex-output-metadata-entry exit } if |