X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=jonesforth.S;h=624eba46460c14f1e560a7eaeaa5072afc854a9c;hb=7bd36863d6d71c4e4f94ba745d37b21f47e2ea9c;hp=5c1249c13ebc5d472464427fdd88cd1b0acc83e1;hpb=f288aec7ec596e7da9a0390c66517bd03ad837fd;p=rrq%2Fjonesforth.git diff --git a/jonesforth.S b/jonesforth.S index 5c1249c..624eba4 100644 --- a/jonesforth.S +++ b/jonesforth.S @@ -1,11 +1,11 @@ /* A sometimes minimal FORTH compiler and tutorial for Linux / i386 systems. -*- asm -*- By Richard W.M. Jones http://annexia.org/forth This is PUBLIC DOMAIN (see public domain release statement below). - $Id: jonesforth.S,v 1.36 2007-09-27 23:09:39 rich Exp $ + $Id: jonesforth.S,v 1.40 2007-09-29 22:12:07 rich Exp $ gcc -m32 -nostdlib -static -Wl,-Ttext,0 -o jonesforth jonesforth.S */ - .set JONES_VERSION,36 + .set JONES_VERSION,39 /* INTRODUCTION ---------------------------------------------------------------------- @@ -757,6 +757,14 @@ code_\label : // assembler code follows push %ecx NEXT + defcode "?DUP",4,,QDUP // duplicate top of stack if non-zero + pop %eax + test %eax,%eax + jz 1f + push %eax +1: push %eax + NEXT + defcode "1+",2,,INCR incl (%esp) // increment top of stack NEXT @@ -1111,7 +1119,7 @@ var_\name : */ defvar "STATE",5,,STATE defvar "HERE",4,,HERE,user_defs_start - defvar "LATEST",6,,LATEST,name_SYSEXIT // SYSEXIT must be last in built-in dictionary + defvar "LATEST",6,,LATEST,name_SYSCALL3 // SYSCALL3 must be last in built-in dictionary defvar "_X",2,,TX defvar "_Y",2,,TY defvar "_Z",2,,TZ @@ -1132,8 +1140,13 @@ var_\name : F_IMMED The IMMEDIATE flag's actual value. F_HIDDEN The HIDDEN flag's actual value. F_LENMASK The length mask in the flags/len byte. + + SYS_* and the numeric codes of various Linux syscalls (from ) */ +//#include // you might need this instead +#include + .macro defconst name, namelen, flags=0, label, value defcode \name,\namelen,\flags,\label push $\value @@ -1147,6 +1160,13 @@ var_\name : defconst "F_HIDDEN",8,,__F_HIDDEN,F_HIDDEN defconst "F_LENMASK",9,,__F_LENMASK,F_LENMASK + defconst "SYS_EXIT",8,,SYS_EXIT,__NR_exit + defconst "SYS_OPEN",8,,SYS_OPEN,__NR_open + defconst "SYS_CLOSE",9,,SYS_CLOSE,__NR_close + defconst "SYS_READ",8,,SYS_READ,__NR_read + defconst "SYS_WRITE",9,,SYS_WRITE,__NR_write + defconst "SYS_CREAT",9,,SYS_CREAT,__NR_creat + /* RETURN STACK ---------------------------------------------------------------------- @@ -1218,8 +1238,6 @@ var_\name : exits the program, which is why when you hit ^D the FORTH system cleanly exits. */ -#include - defcode "KEY",3,,KEY call _KEY push %eax // push return value on stack @@ -1924,10 +1942,13 @@ _COMMA: NEXT /* - PRINTING STRINGS ---------------------------------------------------------------------- + LITERAL STRINGS ---------------------------------------------------------------------- - LITSTRING and EMITSTRING are primitives used to implement the ." and S" operators - (which are written in FORTH). See the definition of those operators below. + LITSTRING is a primitive used to implement the ." and S" operators (which are written in + FORTH). See the definition of those operators later. + + TELL just prints a string. It's more efficient to define this in assembly because we + can make it a single Linux syscall. */ defcode "LITSTRING",9,,LITSTRING @@ -1939,7 +1960,7 @@ _COMMA: andl $~3,%esi NEXT - defcode "EMITSTRING",10,,EMITSTRING + defcode "TELL",4,,TELL mov $1,%ebx // 1st param: stdout pop %edx // 3rd param: length of string pop %ecx // 2nd param: address of string @@ -1959,7 +1980,6 @@ _COMMA: // COLD must not return (ie. must not call EXIT). defword "COLD",4,,COLD .int INTERPRETER // call the interpreter loop (never returns) - .int LIT,1,SYSEXIT // hmmm, but in case it does, exit(1). /* This interpreter is pretty simple, but remember that in FORTH you can always override * it later with a more powerful one! @@ -2033,11 +2053,12 @@ interpret_is_lit: CHAR puts the ASCII code of the first character of the following word on the stack. For example CHAR A puts 65 on the stack. - SYSEXIT exits the process using Linux exit syscall. + SYSCALL3 makes a standard Linux system call. (See for a list of system call + numbers). This is the form to use when the function takes up to three parameters. - In this FORTH, SYSEXIT must be the last word in the built-in (assembler) dictionary because we + In this FORTH, SYSCALL3 must be the last word in the built-in (assembler) dictionary because we initialise the LATEST variable to point to it. This means that if you want to extend the assembler - part, you must put new words before SYSEXIT, or else change how LATEST is initialised. + part, you must put new words before SYSCALL3, or else change how LATEST is initialised. */ defcode "CHAR",4,,CHAR @@ -2047,11 +2068,14 @@ interpret_is_lit: push %eax // Push it onto the stack. NEXT - // NB: SYSEXIT must be the last entry in the built-in dictionary. - defcode SYSEXIT,7,,SYSEXIT - pop %ebx - mov $__NR_exit,%eax + defcode "SYSCALL3",8,,SYSCALL3 + pop %eax // System call number (see ) + pop %ebx // First parameter. + pop %ecx // Second parameter + pop %edx // Third parameter int $0x80 + push %eax // Result (negative for -errno) + NEXT /* START OF FORTH CODE ----------------------------------------------------------------------