/* forth.4th - base postforth code, both high-level and low-level words Copyright 2004 John Comeau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or [at your option] any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* postforth v.1 is a direct-threaded forth which uses subroutine calls instead of 'jmp' instructions, for easier compatibility with C and, through C, with Python registers used: IP: esi [instruction pointer for high-level FORTH] SP: edi [stack pointer for data stack] RP: esp [shares Forth return stack with machine return stack] [SP grows upwards to avoid having to mess with D flag] memory map (under cygwin): 0x00000000 - 0x00230000: system stack 0x00230000 - 0x00400000: ??? 0x00400000 - 0x00401000: ??? guessing environment for this program 0x00401000 - 0x00402000: program space, including PE header and .text 0x00402000 - 0x00403000: .bss section [cleared area for data] inner interpreter: since this is direct-threaded, every word, whether high- or low-level, starts with machine language and ends with RET [except for rare cases]. therefore the inner interpreter loop looks like this: docol: popl %eax ;# return address will point to the highlevel forth code... pushl %esi ;# save any existing codestream address movl %eax,%esi ;# use new one ;# falls through to next - don't put anything in between! next: lodsl ;# of which we get the first word call *%eax ;# and call it... jmp next unnest: ;# ;s or semis, gets you out of the endless NEXT loop popl %esi ;# throwaway the return address to 'jmp next' popl %esi ;# get previous codestream address ret ;# to the previous 'jmp next' */ CREATE LIT /* ( -- num ) (P lit ) */ /* push compiled number onto stack */ /* unlike older FORTHs, this LIT is the runtime code [LIT] */ LODSL, STOSL, RET, : LIT, /* ( num -- ) (P lit-kom'-ma ) */ /* compile LIT and number into application */ ' LIT , ' , , ; CREATE NEXT /* ( -- ) (P nekst ) */ /* advance Instruction Pointer and exec next Forth word */ LODSL, DS:[EAX] CALL, ' NEXT JMPMI, CREATE DOCOL /* ( -- ) (P du:'-kol ) */ /* begin high-level Forth word definition using ' DOCOL CALL, */ EAX POPL, /* get address of highlevel code from stack */ ESI PUSHL, /* save any existing codestream address */ EAX ESI MOVLRR, /* load new forth IP */ ' NEXT JMPMI, CREATE ;S /* ( -- ) (P semi-es' ) */ /* unnest one level of Forth call stack */ ESI POPL, /* discard the return address from NEXT loop */ ESI POPL, /* restore previous codestream address if any */ RET, /* to previous 'jmp next' or to system */ CREATE EXECUTE /* ( word-address -- ) (P eks'-e-kiwt ) */ /* execute Forth word whose address [from tick "'"] is on the stack */ EBX POPL, /* address of operation to perform */ [DS:EBX] JMP, /* go do it */ CREATE >CODE /* ( -- ) (P tu:-co:d') */ /* go from high-level Forth back to machine code level */ DS:[ESI] EAX MOVMR, /* low-level code address pointed to by forth IP */ ESI POPL, /* discard the return address from NEXT loop */ ESI POPL, /* restore previous codestream address if any */ EAX JMPRI, /* jump right into low-level code */