diff --git a/Makefile b/Makefile index 932851f1..f55aa36d 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ DOC = info _info_ sysdoc sysnet syshst kshack _teco_ emacs emacs1 c kcc \ kldcp libdoc lisp _mail_ midas quux scheme manual wp chess ms macdoc \ aplogo _temp_ pdp11 chsncp cbf rug bawden llogo eak clib teach pcnet \ combat pdl minits mits_s chaos hal -pics- imlac maint cent ksc klh \ - digest prs decus bsg madman hur lmdoc rrs danny netwrk + digest prs decus bsg madman hur lmdoc rrs danny netwrk klotz BIN = sys sys1 sys2 emacs _teco_ lisp liblsp alan inquir sail comlap \ c decsys graphs draw datdrw fonts fonts1 fonts2 games macsym \ maint _www_ gt40 llogo bawden sysbin -pics- lmman r shrdlu imlac \ diff --git a/build/timestamps.txt b/build/timestamps.txt index 25615f8c..8f4cf267 100644 --- a/build/timestamps.txt +++ b/build/timestamps.txt @@ -917,7 +917,20 @@ klh/dazdrt.122 197604190418.39 klh/macros.84 198511060813.49 klh/nuuos.205 198505090937.25 klh/outdoc.208 198504211929.03 +klotz/6502.54 198104211822.49 +klotz/all.4 198009220145.02 +klotz/armacs.20 198009250428.43 +klotz/assem.files 198104211732.28 +klotz/assem.ideas 198009100913.04 +klotz/commac.29 198104212048.12 +klotz/decpop.45 198009250818.38 +klotz/defdef.9 198104211919.41 +klotz/eval.3 198901080634.32 +klotz/eval.66 198104212115.32 klotz/logass.19 198105131252.34 +klotz/match.20 198009212317.21 +klotz/pass1.65 198104211743.29 +klotz/tokenz.59 198009250826.52 kmp/kmp.teco 198001190455.28 ksc/fidox.18 197901032240.30 ksc/ivory.12 198002090455.41 diff --git a/doc/klotz/assem.files b/doc/klotz/assem.files new file mode 100755 index 00000000..eea3fa38 --- /dev/null +++ b/doc/klotz/assem.files @@ -0,0 +1,43 @@ +Here is a list of all the files which make up the MacLisp Cross Assembler along +with a brief description of each. + +KLOTZ; PASS1 > +The main body of the assembler. Conses up a big list of +the code, one line at a time. If there is no undefined reference on a a +line, it is completely assembled at read-time. Otherwise, a notation is made of +undefined references. Later, when these references are resolved the code +with the undefined reference is completed. + +KLOTZ; TOKENZ > +The Tokenizer. The routines for reading in tokens and strings. + +KLOTZ; ARMACS > +Code for defining assembler read macros. +Definitions of assembler read macros. + +KLOTZ; DEFDEF > +The code for DEFINS (define instruction), DEFAMODE (define addessing +mode), and DEF-PSEUDO-OP. + +APLOGO;EVAL > +The routines for evaluating symbols and arithmatic expressions. + +APLOGO;MATCH > +Winning simple pattern matcher for parsing losing assembler expressions. +Used by DEFINS and PASS1 for determining addressing mode. + +KLOTZ; 6502 > +These are the definitions for the 6502 addressing modes and instructions. + +KLOTZ;DECPOP > +These are the assembler DEC compatability pseudo-op directives. + +KLOTZ;PASS1 IDEAS +Old documentation for how pass1 ought to work. Somewhat obsolete, but nonetheless +informative. + +KLOTZ;POPDEC DOC +Documentation for the assembler DEC compatability pseudo-op directives. + +KLOTZ;ASSEM FILES +? \ No newline at end of file diff --git a/doc/klotz/assem.ideas b/doc/klotz/assem.ideas new file mode 100755 index 00000000..43e59b0a --- /dev/null +++ b/doc/klotz/assem.ideas @@ -0,0 +1,42 @@ +This is my attempt at one-pass assembly. + +First get a token. If it has a PREFIX-OP (opcode or pseudo-op) property +then hand control to the function in that property. Do not try to +collect arguments for it, since it might want string arguments (e.g. +".TITLE APPLE-LOGO"). If it wants tokens, it will call gettok itself. + +If the first token on the line is has no PREFIX-OP property then it +still might be an infix-style assignment statement (i.e. "=", or ":"). +So we save the old token, get another token, and check to see if +that token has a INFIX-OP property. If so, then it is called and given +as an argument the first token on the line. This type of PSEUDO-OP would +also call gettok or whatever it feels like calling and finally return. +All of the functions described above should return whatever they want +put in the list as code. Of course, they may have appropriate side +efects. + +If any token is undefined, whatever worries about it should look on the +FORWARD-REF property of the token (This is a job for ASSEM-SYMEVAL for +symbols and ASSEMBLE-CODE for operators.) and put a FORWARD-REF property +on them. The forward ref property (which should be a list of the new +and all the old FORWARD-REF properties still unresolved) should contain +a pointer to the place where the code for this instruction will be +pushed onto the list of all code (The first element of the +whole-code-list when the reference was made.) as the cdr of the +succesive cadr's of the FORWARD-REF property and the name of the +undefined reference as the car of the cadr's. Totally unclear? + +;;;;; Stuff below has note been correced. Error checking, etc. (LDA +FOO), then FOO=> $2345. Either an error or (LDA 69.). Either way, it +requires some checking. Maybe we shouldn't partially assemble the +instruction when there is an undefined reference in it, and let the +undef-ref handler do it. It wouldn't be difficult at all; just let IT +call the various propertys of the symbol.) The undef-ref handler just +rplacd's the undefined reference (although it might be an expression. We +should do an NSUBLIS (Which is like SUBLIS except it bashes its argument +and isn't written. We'd have to write it. Big deal.). This +automatically changes the reference. It should also remove the pointer +from the FORWARD-REF property of the token and place the new value on +the LABEL-VALUE property. When theassembleris through, we do a MAPTAOMS +looking for FORWARD-REF (perhaps we should call it UNDEF-REF) and +LABEL-VALUE properties and print out our symbol table from there. diff --git a/doc/klotz/dec.pseudo b/doc/klotz/dec.pseudo new file mode 100755 index 00000000..6307beb8 --- /dev/null +++ b/doc/klotz/dec.pseudo @@ -0,0 +1,123 @@ +MacLisp Cross Assembler Documentation. KLOTZ. +DEC compatability pseudo-op stuff. + +; Begins comment field + +' Indicates single ASCII character as a term in expressions. + +^O Indicates octal number + +^B Indicates binary number + += Symbol assignment + +. Current location counter + +$ Indicates hexadecimal number + + +Default radix is decimal. + + +.ADDR Generates a 16-bit address with the bytes in the proper order, + i.e., .[TO DO] + +.ASCII ASCII string delimited by paired markers. + +.ASCIZ ASCIZ string delimited by paired markers and supplied with + terminal null byte. + +.BLKBA Allocate space for a number of bytes as specified by the operand. + +.BLKW Allocate space for a number of words as specified by the operand. + +.BYTE Each expression supplied will be stored in a single byte.[In + progress. Eval-Expression does what with commas, and best way to cons it + up.] + +.END End of program. + +.ISET Specifies the instruction set to be used, "6502" or "TI990". + +.PAGE Force a page break. Does nothing useful now. + +.PRINT Print the text which follows on the terminal. + +.RADIX Change the radix to the value specified to this directive. + +.SBTTL Specify subtitle text. Prints out TOC on screen if + SILENT-RUNNING isn't specified (via the /N switch.) + +.TITLE Specify title text. + +.WORD Allocate space for each value specified. [Same as .BYTE for 8 bit machines.] + + + +------------------------- +Things for the Future: + +.REPT (.REPT ) Repeat the body of this directive the indicated + number of times. + +.NTYPE sym,arg The symbol is equated to the addressing mode of the argument. +.NCHR sym, The symbol is equated to the number of characters in +.NARG sym The symbol is equated to the number of arguments to the macro +.MEXIT Leave macro expansion +Begin a macro definition + +.MCALL Call macros from the system library. +.IRPC sym,Indefinite repeat of body based upon number of characters in + . + +.MACRO name Immediate if; if condition is met, stmnt is emitted. + +.IRP sym, Indefinite repeat of body based upon number of parameters in + ..IIF condition, arg, stmnt +.IF Enter a conditional. +.IFDF .IFDF true if symbol is defined. See also .IFNDF. +.IFEQ .IFEQ true if expression evaluates to zero. +.IFF Begins body of conditional if enclosing condition was false. +.IFG .IFG true if expression greater than zero. +.IFGE .IFG true if expression greater than or equal to + zero. +.IFGT Same as .IFG +.IFL .IFL true if expression is negative. +.IFLE .IFLE true if expression is negative or zero. +.IFLT Same as .IFL. +.IFNDF .IFNDF true if symbol is undefined. +.IFNE .IFNE true if expression is non-zero. +.IFNZ Same as .IFNE +.IFT Begins body of conditional if enclosing condition was true. +.IFZ Same as .IFE +.IIF condition, arg, stmnt +Immediate if; if condition is met, stmnt is emitted. + +.IRP sym, Indefinite repeat of body based upon number of parameters in + . +[Nui mahope.] +.IRPC sym,Indefinite repeat of body based upon number of characters in + . + +.MACRO name +Begin a macro definition +.MEXIT Leave macro expansion +.NARG sym The symbol is equated to the number of arguments to the macro +.NCHR sym, The symbol is equated to the number of characters in +.NTYPE sym,arg The symbol is equated to the addressing mode of the argument. + +-------------------- +Assembler syntax quick summary + +<...> Encloses any expression (the assembler's equivalent of + parentheses). Expressions are evaluated left-to-right without + operator heirarchy. + ++ Addition or unary plus + +- Subtraction or unary minus + +* Multiplication + +/ Division + diff --git a/src/klotz/6502.54 b/src/klotz/6502.54 new file mode 100755 index 00000000..87e185a0 --- /dev/null +++ b/src/klotz/6502.54 @@ -0,0 +1,349 @@ +;;;-*-LISP-*- +; Here is the instruction set and addressing modes for the 6502. + +(herald /6502) + +(declare + (*expr eight-bits-p eight-bits-or-defined-p assem-defined-p)) + +(eval-when (compile eval) + (or (get 'defdef 'version) (load "klotz;defdef")) + (setq ibase-save ibase + ibase 16. + status-+-save (status +)) + (sstatus + t)) + +; These are the 6502 addressing modes. +; IMM - Immediate ZP - Zero page +; ZPX - Zero Page, X ZPY - Zero Page, Y +; ABS - Absolute ABX - Absolute, X +; ABY - Absolute, Y INDX - (Indirect, X) +; INDY - (Indirect) ,Y REL - Relative +; IMP - Implied ACC - Accumulator +; IND - Indirect +; Here is the format for DEFAMODE: +; (DEFAMODE +; +;
) +;During the evaluation of the form, the bindings specified in the pattern are +;in effect. In addition, OP is bound to the opcode for the current instruction. +;The form should return a list like this ( [ []]). + + +(defamode IMM 2 (/# (VAL BYTE-OR-UNDEFINED)) + (list op (eval-expression val))) + +(defamode ZP 2 ((VAL BYTE)) + (list op (eval-expression val))) + +(defamode ZPX 2 ((VAL BYTE) /, X) + (list op (eval-expression val))) + +(defamode ZPY 2 ((VAL BYTE) /, Y) + (list op (eval-expression val))) + +(defamode ABS 3 ((VAL ANY-EXPRESSION)) + (let ((evaled-val (eval-expression val))) + (list op (lo-8 evaled-val) (hi-8 evaled-val)))) + +(defamode ABX 3 ((VAL ANY-EXPRESSION) /, X) + (let ((evaled-val (eval-expression val))) + (list op (lo-8 evaled-val) (hi-8 evaled-val)))) + +(defamode ABY 3 ((VAL ANY-EXPRESSION) /, Y) + (list op (eval-expression val))) + +(defamode INDX 2 (/( (VAL BYTE) /, /X /) ) + (list op (eval-expression val))) + +(defamode INDY 2 (/( (VAL BYTE) /) /, /Y) + (list op (eval-expression val))) + +(defamode ACC 1 (A) + (list op)) + +(defamode IMP 1 () + (list op)) + +(defamode IND 3 (/( (VAL ANY-EXPRESSION) /) ) + (let ((evaled-val (eval-expression val))) + (list op (lo-8 evaled-val) (hi-8 evaled-val)))) + +(defamode REL 2 ((VAL ANY-EXPRESSION)) + (list op (eval-expression `(< ,(assem-symeval '/.) - ,val >)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Here are the instructions for the 6502. +; The format is +; (defins +; ( ) +; ( ) ...) +; The last form returns a fixnum representing the opcode of the instruction. +; [Actually, ther had best be only one form - an atom and a fixnum at that. +; When we need things to be evaluated, I'll fix it.] +; IMM IMP ACC INDX INDY ZPX ZPY ABX ABY ZP ABS IND REL. +(defins ADC 0 + (IMM +69) + (INDX +61) + (INDY +71) + (ZP +65) + (ZPX +75) + (ABS +6D) + (ABX +7D) + (ABY +79)) + +(defins AND 0 + (IMM +29) + (ZP +25) + (ZPX +35) + (ABS +2D) + (ABX +3D) + (INDX +21) + (INDY +31)) + +(defins ASL 0 + (ACC +0A) + (ZP +06) + (ZPX +16) + (ABS +0E) + (ABX +1E)) + +(defins BCC 0 + (REL +90)) + +(defins BCS 0 + (REL +B0)) + +(defins BEQ 0 + (REL +F0)) + +(defins BIT 0 + (ZP +24) + (ABS +2C)) + +(defins BMI 0 + (REL +30)) + +(defins BNE 0 + (REL +D0)) + +(defins BPL 0 + (REL +10)) + +(defins BRK 0 + (IMP +00)) + +(defins BVC 0 + (REL +50)) + +(defins BVS 0 + (REL +70)) + +(defins CLC 0 + (IMP +18)) + +(defins CLD 0 + (IMP +D8)) + +(defins CLI 0 + (IMP +58)) + +(defins CLV 0 + (IMP +B8)) + +(defins CMP 0 + (IMM +C9) + (ZP +C5) + (ZPX +D5) + (ABS +DD) + (ABX +DD) + (ABY +D9) + (INDX +C1) + (INDY +D1)) + +(defins CPX 0 + (IMM +C0) + (ZP +E4) + (ABS +EC)) + +(defins CPY 0 + (IMM +C0) + (ZP +C4) + (ABS +CC)) + +(defins DEC 0 + (ZP +C6) + (ZPX +D6) + (ABS +CE) + (ABX +DE)) + +(defins DEX 0 + (IMP +CA)) + +(defins DEY 0 + (IMP +88)) + +(defins EOR 0 + (IMM +E6) + (ZP +45) + (ZPX +55) + (ABS +4D) + (ABX +5D) + (ABY +59) + (INDX +41) + (INDY +51)) + +(defins INC 0 + (ZP +E6) + (ZPX +F6) + (ABS +EE) + (ABX +FE)) + +(defins INX 0 + (IMP +E8)) + +(defins INY 0 + (IMP +C8)) + +(defins JMP 0 + (ABS +4C) + (IND +6C)) + +(defins JSR 0 + (ABS +20)) + +(defins LDA 0 + (IMM +A9) + (ZP +A5) + (ZPX +B5) + (ABS +AD) + (ABX +BD) + (ABY +B9) + (INDX +A1) + (INDY +B1)) + +(defins LDX 0 + (IMM +A2) + (ZP +A6) + (ZPY +B6) + (ABS +AE) + (ABY +BE)) + +(defins LDY 0 + (IMM +A0) + (ZP +A4) + (ZPX +B4) + (ABS +AC) + (ABX +BC)) + +(defins LSR 0 + (ACC +4A) + (ZP +46) + (ZPX +56) + (ABS +4E) + (ABX +5E)) + +(defins NOP 0 + (IMP +EA)) + +(defins ORA 0 + (IMM +09) + (ZP +05) + (ZPX +15) + (ABS +0D) + (ABX +1D) + (ABY +19) + (INDX +01) + (INDY +11)) + +(defins PHA 0 + (IMP +48)) + +(defins PHP 0 + (IMP +08)) + +(defins PLA 0 + (IMP +68)) + +(defins PLP 0 + (IMP +28)) + +(defins ROL 0 + (ACC +2A) + (ZP +26) + (ZPX +36) + (ABS +2E) + (ABX +3E)) + +(defins ROR 0 + (ACC +6A) + (ZP +66) + (ZPX +76) + (ABS +6E) + (ABX +7E)) + +(defins RTI 0 + (IMP +40)) + +(defins RTS 0 + (IMP +60)) + +(defins SBC 0 + (IMM +E9) + (ZP +E5) + (ZPX +F5) + (ABS +ED) + (ABX +FD) + (ABY +F9) + (INDX +E1) + (INDY +F1)) + +(defins SEC 0 + (IMM +38)) + +(defins SED 0 + (IMM +F8)) + +(defins SEI 0 + (IMM +78)) + +(defins STA 0 + (ZP +85) + (ZPX +95) + (ABS +8D) + (ABX +9D) + (ABY +99) + (INDX +81) + (INDY +91)) + +(defins STX + (ZP +86) + (ZPY +96) + (ABS +8E)) + +(defins STY + (ZP +84) + (ZPX +94) + (ABS +8C)) + +(defins TAX 0 + (IMP +AA)) + +(defins TAY 0 + (IMP +A8)) + +(defins TSX 0 + (IMM +BA)) + +(defins TXA 0 + (IMM +8A)) + +(defins TXS 0 + (IMM +9A)) + +(defins TYA 0 + (IMM +98)) + +(eval-when (compile eval) + (setq ibase ibase-save) + (sstatus + status-+-save)) diff --git a/src/klotz/all.4 b/src/klotz/all.4 new file mode 100755 index 00000000..ae0db9b4 --- /dev/null +++ b/src/klotz/all.4 @@ -0,0 +1,13 @@ +;;;-*-LISP-*- +;Here it is, the assembler in MacLisp. +(load "klotz;tokenz") ; Input and tokeinzing routines. +(load "klotz;armacs") ; Read macros for input. +(load "klotz;pass1") ; Top-level routines for assembly. +(load "aplogo;eval") ; Arithmetic expression evaluator. +(load "aplogo;match") ; Pattern matcher for determining addressing mode. +(load "klotz;decpop") ; DEC compatability pseudo-ops. +(load "klotz;6502") ; 6502 instructions and addressing modes. +; Definitions for DEFINS (define instruction), DEFAMODE (define addressing mode), +; and DEF-PSEUDO-OP are in DEFDEF and loaded automatically. + +(defun flush () (setq *untyi-char* nil)) diff --git a/src/klotz/armacs.20 b/src/klotz/armacs.20 new file mode 100755 index 00000000..afcab235 --- /dev/null +++ b/src/klotz/armacs.20 @@ -0,0 +1,59 @@ +;;;-*-LISP-*- +; Code for defining assembler read macros. +; Definitions of assembler read macros. + +(herald armacs) + +(eval-when (compile) + (setq defmacros-for-compiling nil) + (special assem-ibase number-context-p) + (*expr gettok unagetchar agetchar) + (fixsw t)) + +(eval-when (compile eval) + (or (get 'defdef 'version) (load "klotz;defdef"))) + +(array *assem-read-table* t 200) + +(declare (array* (notype (*assem-read-table* 200)))) + +;Read macro definitions. + +(defarmac #/+ (ch) + (ascii ch)) ;self-delimiting character. + + +(mapc #'(lambda (c) (store (*assem-read-table* c) + (*assem-read-table* #/+))) + '(#/- #/* #// #/# #/: #/@ #/( #/) + #/[ #/] #/< #/> #/, #/= #/& #/!)) + + +(defarmac #/; (NIL) + (do () ((= (agetchar) #\cr) (gobble-lf) ()))) ;nil token means end of line + +(defun gobble-lf () + (let ((ch (agetchar))) + (cond ((not (= ch #\lf)) (unagetchar ch))))) + +(defarmac #\cr (NIL) + (gobble-lf) + (*throw 'END-OF-LINE ())) + +(defarmac #/$ (NIL) + (let ((assem-ibase 16.) + (number-context-p t)) + (gettok))) +(defarmac #/ (NIL) + (let ((assem-ibase 8.) + (number-context-p t)) + (gettok))) + +(defarmac #/ (NIL) + (let ((assem-ibase 2.) + (number-context-p t)) + (gettok))) + +(defarmac #/' (NIL) ;returns the ascii value of the character it quotes. + (agetchar)) + diff --git a/src/klotz/commac.29 b/src/klotz/commac.29 new file mode 100755 index 00000000..e25c47ff --- /dev/null +++ b/src/klotz/commac.29 @@ -0,0 +1,15 @@ +;-*-LISP-*- +;Here are various functions used throughout that don't have much +;to do with anything else. + +(herald commac) + +(defvar silent-running-p nil) + +(defun print-when-talkative (string) + (cond (silent-running-p) + (t (progn (terpri) (princ string) (terpri))))) + +(defun aerror (&rest args) + (mapc #'(lambda (u) (princ u) (princ " ")) args) + (break aerror)) diff --git a/src/klotz/decpop.45 b/src/klotz/decpop.45 new file mode 100755 index 00000000..e480a785 --- /dev/null +++ b/src/klotz/decpop.45 @@ -0,0 +1,112 @@ +;;;-*-LISP-*- +;; These are the assembler DEC compatability pseudo-op directives. If a +;; pseudo-op returns something non-nil, it is spliced into the code +;; the point where it occurs, just like any opcode (see aplogo;6502 > +;; for examples.) For prefix ops, the argument list is the list of either +;; (TOKLST) or STRING. During the evaluation of the forms, this atom will +;; be bound to the list of tokens or the string constructed from the +;; remainder of the line on which the pseudo-op was encountered. +;; (def-pseudo-op PREFIX () +;; ) +;; In the definition of infix ops, the (toklst) or string is the bound to the +;; remainder of line in the same manner; however, there is an additional +;; atom which is bound to the token which was the first token on the line. +;; In the case of ":" and "=", this is the label name. +;; (def-pseudo-op INFIX ( ) +;; ) + +(herald decpop) + +(defvar silent-running-p) +(defvar *last-subtitle*) + +(declare (fixsw t) + (special /. assem-ibase *assem-input-stream*) + (*expr assemble-code-line assem-symeval eval-expression) + (setq defmacros-for-compiling nil)) + +(eval-when (compile eval) + (or (get 'commac 'version) (load "klotz; commac")) + (or (get 'defdef 'version) (load "klotz;defdef"))) + +(defun do-ascii-string (string) ;| "HELLO" | => HELLO + (do ((i 1 (1+ i)) + (charlst) + (char (getcharn string 1) (getcharn string i))) + ((zerop char) (nreverse charlst)) + (push char charlst))) + +(def-pseudo-op /.ASCII PREFIX (STRING) + (do-ascii-string string)) + +(def-pseudo-op /.ASCIZ PREFIX (STRING) + (nconc (do-ascii-string string) (ncons 0))) + +(def-pseudo-op /.BLKB PREFIX (TOKLST) + (do ((i (eval-expression toklst) (1- i)) + (space () (cons 0 space))) + ((zerop i) space))) + +(def-pseudo-op /.BLKW PREFIX (TOKLST) + (do ((i (eval-expression toklst) (1- i)) + (space () (cons 0 space))) + ((zerop i) space))) + +;(def-pseudo-op /.BYTE PREFIX (TOKLST) +; (do ((lst (toklst) (comma-cdr toklst)) +; (.....)))) + +(def-pseudo-op /.PAGE PREFIX (STRING) + (print-when-talkative string) + nil) + +(def-pseudo-op /.TITLE PREFIX (STRING) + (print-when-talkative string) + nil) + +(def-pseudo-op /.PRINT PREFIX (STRING) + (princ string tyo) ;Must be important, so print it always. + (terpri tyo) + nil) + +(def-pseudo-op /.RADIX PREFIX (TOKLST) + (setq assem-ibase (eval-expression toklst)) + nil) + +(def-pseudo-op /.SUBTTL PREFIX (STRING) + (print-when-talkative string) + (setq *last-subtitle* string) + nil) + +(def-pseudo-op /.END PREFIX (STRING) + (print-when-talkative string) + (close *assem-input-stream*) ;This shouldn't really do this so directly. + '*EOF*) + +(def-pseudo-op /.ISET PREFIX (STRING) + (selectq string + (|6502| (load "klotz;6502") + nil) + (|TI9900| (load "tilogo;9900") + nil) + (otherwise (aerror "Foo I never heard of a " string + "machine.")))) + +; Eval-Expression bashes its argument. If there are undefined labels encountered +; in the expression, they are not changed in the expression; however, a pointer +; to the expression is consed onto the forward-ref property of the undefined +; symbol if it is not already there. + +(def-pseudo-op = INFIX (TOKLST label) + (cond ((eq label '/.) `(PC-IS-NOW ,(assem-set '/. (assem-symeval toklst)))) + ((assem-defined-p label) + (aerror "Multiply defined equivalence: " label) nil)) + (assem-set label (assem-symeval-or-eval TOKLST)) + (handle-forward-references-for label) + nil) + +(def-pseudo-op : INFIX (NIL label) + (cond ((assem-defined-p label) (aerror "Multiply defined label: " label) nil)) + (assem-set label (assem-symeval '/.)) + (handle-forward-references-for label) + nil) diff --git a/src/klotz/defdef.9 b/src/klotz/defdef.9 new file mode 100755 index 00000000..a642428d --- /dev/null +++ b/src/klotz/defdef.9 @@ -0,0 +1,134 @@ +;;;-*-LISP-*- +; The code for defining instructions, addressing modes, and pseudo-ops. + +(herald defdef) ;close enough. + +(declare (setq defmacro-for-compiling t) + (*lexpr aerror) + (*expr tokenize-rest-of-line read-in-delimited-string match) + (array* (notype (*assem-read-table* 200))) + (fixsw t)) + +(defmacro defins (name cycles &rest args) + `(progn 'compile + (putprop ',name ',args 'ADDRESS-MODE-ALIST) + (putprop ',name ',cycles 'CYCLES))) + +;Here is the code for defining read macros. +(defmacro defarmac (char argl &rest body) + (let ((name (implode (cons (ascii char) '#.(exploden "-ASSEM-READMACRO"))))) + `(progn 'compile + (defun (,name lexscan) ,argl ,@body) + (store (*assem-read-table* ,char) + (get ',name 'lexscan)) + t))) + +(defun process-pattern (pattern) + (do ((pattern pattern (cdr pattern)) + (new-pattern) + (element) + (dispatch)) + ((null pattern) (nreverse new-pattern)) + (push + (cond ((atom (setq element (car pattern))) element) ;a literal + ((setq dispatch (get (cadr element) 'PATTERN-MACRO)) + (funcall dispatch)) + (t (error "Unknown pattern element - process-pattern"))) + new-pattern))) + +(defmacro defpred (name &rest body) + `(putprop ',name + #'(lambda () ,@body) + 'PATTERN-MACRO)) + +(defpred BYTE-OR-WORD ;the arg. vip GETs it. + (list '* #'(lambda (exp) + (no-illegal-chars-in-expression-p exp)))) + +(defpred BYTE + (list '* #'(lambda (exp) + (and (no-illegal-chars-in-expression-p exp) + (eight-bits-p exp))))) + +(defpred TWO-BYTES + (list '* #'(lambda (exp) + (and (no-illegal-chars-in-expression-p exp) + (sixteen-bits-p exp))))) + +(defpred BYTE-OR-UNDEFINED + (list '* #'(lambda (exp) + (and (no-illegal-chars-in-expression-p exp) + (or (eight-bits-p exp) + (asem-unbound-p exp)))))) +(defpred ANY-EXPRESSION + (list '* #'(lambda (exp) + (no-illegal-chars-in-expression-p exp)))) + +(defpred UNDEFINED-EXPRESSION + (list '* #'(lambda (exp) + (and (no-illegal-chars-in-expression-p exp) + (assem-defined-p exp))))) + +(defpred REGISTER + (list '> #'(lambda (name) + (get name 'REGISTER)))) + +(defun no-illegal-chars-in-expression-p (exp) + (doesnt-contain '(/) /( /,) exp)) + +(defun doesnt-contain (atoms lst) + (*catch 'LOST + (do ((atoms atoms (cdr atoms))) + ((null atoms) t) + (do ((exprs lst (cdr exprs))) + ((null exprs) t) + (cond ((eq (car atoms) (car exprs)) + (*throw 'lost nil))))))) + +;This conses the variables used in the pattern up in reverse order from their occurence. +;A function is built up using this as a lambda-list and applied to the output of the +;pattern matcher. It, conses up the variables' values in reverse order. +; (vip ((> foo bar) $ (* baz))) => (baz bar) +; (vip ((REGISTER BAR) /, (BYTE BAZ))) => (baz bar) + +(defun variables-in-pattern (pattern) + (do ((pattern pattern (cdr pattern)) + (vars) (element)) + ((null pattern) vars) + (cond ((not (atom (setq element (car pattern)))) + (push (car element) vars))))))) + + +(defmacro defregister (name nbytes) + `(put ,name ,nbytes 'REGISTER)) + +(defmacro defamode (name nbytes pattern &rest forms) + `(progn 'compile + (declare (special ,name)) + (putprop ',name ,nbytes 'INSTRUCTION-LENGTH) + (putprop ',name ,(process-pattern pattern) 'ADDRESS-MODE-PATTERN) + (putprop ',name + #'(lambda (OP ,@(variables-in-pattern pattern)) + ,@forms) + 'AMODE-FORMS))) + +(defun let-read-in-function (type body) + (cond ((null type) body) + (t `((let ((,type + ,(cond ((eq type 'string) + '(read-in-delimited-string)) + ((eq type 'toklst) + '(tokenize-rest-of-line)) + (t + (error + "Type of argument not STRING, NIL, or TOKLST to DEF-PSEUDO-OP."))))) + ,@body))))) + +(defmacro def-pseudo-op (name p-or-i varlst &rest body) + (cond ((eq p-or-i 'prefix) + `(defun (,name ASSEM-PREFIX-OP) () + ,@(let-read-in-function (car varlst) body))) + ((eq p-or-i 'infix) + `(defun (,name ASSEM-INFIX-OP) ,(cdr varlst) + ,@(let-read-in-function (car varlst) body))) + (t (error "Bad syntax type in def-pseudo-op -- not prefix or infix")))) diff --git a/src/klotz/eval.3 b/src/klotz/eval.3 new file mode 100755 index 00000000..50813041 --- /dev/null +++ b/src/klotz/eval.3 @@ -0,0 +1,148 @@ +Date: Thu, 3 Sep 87 00:47:46 PDT +From: ehl%dewey.SOE.Berkeley.EDU at jade.berkeley.edu (Ed Lay) +To: boxer-port%dewey.soe.Berkeley.EDU at jade.berkeley.edu +Re: sprite graphics times + +I moved the recursive calls to modified outside of the eval and square +now takes 0.56 seconds (after which it pauses for another .5 second to +handle all of the queued upo calls to modified. + +ed + +Date: Wed, 2 Sep 87 20:59:14 PDT +From: disessa%cogsci.Berkeley.EDU at jade.berkeley.edu (Andy diSessa) +To: ehl%dewey.SOE.Berkeley.EDU at jade.berkeley.edu +cc: boxer-port%dewey.SOE.Berkeley.EDU at jade.berkeley.edu +Re: Sprite Graphics + +My toshiba runs Logo at about 50 turtle commands per second (fd 1 rt 1 +counts as two), and LCSI Logo on the mac runs about 40. Looks like we +have the usual order of magnitude to make up to get into reasonable +range. 30% via pcl code doesn't sound like much of a solution. Is +this still the interpreter, or is it really the t.g. commands that +are slow? It's hard to imagine its in the t.g. commands. + +--Andy + +Received: from jade.berkeley.edu (TCP 20010104011) by AI.AI.MIT.EDU 2 Sep 87 06:24:38 EDT +Received: from dewey.soe.berkeley.edu + by jade.berkeley.edu (5.54 (CFC 4.23)/1.16.16) + id AA27518; Wed, 2 Sep 87 03:19:22 PDT +Received: by dewey.soe.berkeley.edu (5.51/SMI-3.0DEV3.7) + id AA00937; Wed, 2 Sep 87 03:18:30 PDT +Date: Wed, 2 Sep 87 03:18:30 PDT +From: ehl%dewey.SOE.Berkeley.EDU@jade.berkeley.edu (Ed Lay) +Message-Id: <8709021018.AA00937@dewey.soe.berkeley.edu> +To: boxer-port%dewey.soe.Berkeley.EDU@jade.berkeley.edu +Subject: Sprite Graphics + + +I have sprite graphics working in the new boxer along with the trigger +stuff. It's still horribly slow but getting better (down from 3 seconds +for square 30 to 2 seconds (the old implementation takes .6 seconds)). + +Almost running on the Sun as well (some problems with the different bitmap +representations in the underlying window system). + +Stuff left to do includes: +get the shape box to work +speed up !! +Right now, the triggers are calling the evaluator recursively which is +breaking it. (May need to wait for Leigh to fix this one) + +I meter'd the square and found that it was spending close to 30 % of the +time inside of PCL stuff. Block Compile Meta Class may be of some use here. + +ed + +Date: Mon, 21 Nov 88 20:05:15 PST +From: ehl%SOE.Berkeley.EDU@jade.berkeley.edu (Edward Lay) +Message-Id: <8811220405.AA24546@dewey.soe.berkeley.edu> +To: disessa%cogsci.Berkeley.EDU@jade.berkeley.edu +Subject: Re: telling input parameters +Cc: klotz@ai.ai.mit.edu + +Leigh, the background is that Andy wants a procedure (called move) that +transfers the bindings of one box into another box and he can't because +we aren't looking up variables inside of virtual copies + + Date: Mon, 21 Nov 88 19:56:36 PST + From: disessa@cogsci.berkeley.edu (Andy diSessa) + Subject: telling input parameters + + you need port flavored input to move or else the "to" variable + referred to in the else clause of the "if" line will be + referring to a COPY of "to" and will refer to separate copies + for each frame in the recursion. + + Yes, but I don't care if it is a copy. The MUTATED copy gets passed + on to the next recursion of move, Right? The thing that gets returned + is a mutated copy of a mutate copy of ..., and that's what I intended. + I know the bug you mean (I think) and I don't think this is an + instance of it. I am not intending to mutate the thing that supplied + input, only the input that I get before I pass it on for more mutation. + +ok, I see. The problem is that binding information inside of +virtual copies is not implemented because we couldn;t figure out +a good way to do it without slowing other things down. Perhaps the +lisp error is something Leigh put in. We sent mail about another aspect +of this problem last week. The bug was that named subprocedures inside +a shape were not available. I think the easiest work around is to +mutate the original (it is likely to be much faster than the variable +lookup inside of virtual copy). The initial implementation (not curently +installed apparently) for looking up variables inside of VC's loops +through the items of each row of the VC looking for named boxes. + +ed + +Date: Thu, 5 Jan 89 07:46:39 EST +From: hal@murren.ai.mit.edu (Hal Abelson) +Message-Id: <8901051246.AA01672@murren.ai.mit.edu> +To: klotz@ORION.AI.MIT.EDU +Cc: adis@dewey.soe.berkeley.edu, ehl@dewey.soe.berkeley.edu +In-Reply-To: Leigh L. Klotz's message of Thu, 5 Jan 89 04:56:51 est <8901050956.AA08106@orion> +Subject: lexical dynamic boxer +Reply-To: hal@zurich.ai.mit.edu + + +I remember talking about this way to gewt lexical scoping long ago, +maybe even with you, certyainly with Andy. But, sure, go ahead and +attribute it to every one you can -- the more people you attribute an +idea to, the more true it is, right? + + +Date: Wed, 4 Jan 89 22:41:32 est +From: klotz@ORION.AI.MIT.EDU (Leigh L. Klotz) +Message-Id: <8901050341.AA07891@orion> +To: adis@dewey.soe.berkeley.edu, ehl@dewey.soe.berkeley.edu, + hal@ORION.AI.MIT.EDU +Cc: klotz@ai.ai.mit.edu +Subject: TOP SECRETE; DO NOT DISSEMINATE + +In talking to people about Boxer and in writing my thesis, I am +beginning to find choice of basing Boxer on Logo syntax harder and +harder to defend: + + A. It has problems. + 1. The user can't distinguish procedures from data by looking the syntax of a program. + + 2. The interpreter can't distinguish procedures from data by looking + at the syntax of a program. + + 3. The compiler packed up its bags and went home ages ago. + + B. We haven't given the syntax much though except for the following points: + 1. Prompts, which don't quite work. + 2. No parentheses (use boxes instead) + 3. No variable arguments (but maybe we need them). + +What would you guys think about thinking hard about some new Boxer syntax? +It needn't be something really different -- I would hope to be able +implement it in my current evaluator. But maybe with some concerted thinking +about the issues in B we can solve the problems in A. + +Does this seem to be a reasonable thing for me to spend at least a +couple of weeks on in Berkeley? + +Leigh. + diff --git a/src/klotz/eval.66 b/src/klotz/eval.66 new file mode 100755 index 00000000..9deff827 --- /dev/null +++ b/src/klotz/eval.66 @@ -0,0 +1,259 @@ +;-*-lisp-*- +;expression evaluator. + +;Reader beware - a lot of this code is pretty hacked to avoid consing. +;I am not a fanatic about this sort of thing, but one must be careful +;when a piece of code is going to be called many thousands of times. + +;idea is this: we take the list, and try to find a value for anything +;that is not defined as an infix pseudo-op. when we find such values, +;we rplaca them in; if we don't find a value for one or more of them, +;we do not attempt to evaluate the expression past this level; i.e., +;this: +;(eval-expression '(foo + bar)) +;if foo has no assem-symbol-value and bar's is 3, turns into: +;(foo + 3) +;whereas, if foo had an assem-symbol-value of 1, (4) would be +;returned. That is, when we can, we turn the entire expression into a +;number, and then listify it. +;Referencing an undefined symbol also has the effect of pushing a +;pointer to the list it is in on the FORWARD-REF property of that +;symbol, so that it can later be resolved when the symbol is defined. +;Some people believe that this sort of silly hair required by a +;one-pass assembler that allows forward referencing is worth the +;trouble. More sane people write two-pass assemblers. Most people write +;in COBOL. +;When the symbol is eventually defined, we mapc eval-expression over +;the FORWARD-REF list, thereby bashing all the previous references +;with the actual value of the symbol, or maybe of the entire +;expression. +;So, consider: if we do: +;(eval-expression '(foo + bar)) +;and both foo and bar are undefined, we put a pointer to the list +;'(foo + bar) in the FORWARD-REF property of both foo and bar. Later +;on, if foo is defined to be 3, we run eval-expression on (foo + bar) +;and get (3 + bar). Since eval-expression bashes its argument (like +;nreverse, and such), the code-list has this, and so does the +;FORWARD-REF property of bar. When we get a definition for bar, we +;win. Get the picture? + +;To define an infix operator, we must put it on the list of infix +;operators, make it self delimiting (by calling DEFARMAC) if we want +;it to be and it is a single character (multiple-character +;self-delimiters are not supported), and also specify what operation +;it should perform on its two arguments. The same is true of prefix +;operators; except they should be placed on our list of prefix +;operators. + +;the following macros can be used to define operators and cause them +;to point to their associated functions. + +(herald eval) + +(declare (fixsw t) + (special refers-forward) + (setq defmacro-for-compiling nil) + (*lexpr aerror)) + +(eval-when (compile eval) + (or (get 'commac 'version) + (load "klotz;commac"))) + +(defmacro def-infix-op (op opfunction) + `(putprop ,op #',opfunction 'ARITH-INFIX-OP)) + +(defmacro def-prefix-op (op opfunction) + `(putprop ,op #',opfunction 'ARITH-PREFIX-OP)) + +(defmacro get-prefix-op-function (op) + `(get ,op 'ARITH-PREFIX-OP)) + +(defmacro get-infix-op-function (op) + `(get ,op 'ARITH-INFIX-OP)) + +(defmacro infix-op-p (op) + `(get-infix-op-function ,op)) + +(defmacro prefix-op-p (op) + `(get-prefix-op-function ,op)) + +(defmacro assem-op-p (op) + `(or (eq ,op '>) + (eq ,op '<) + (infix-op-p ,op) + (prefix-op-p ,op))) + +;resets lst to itself, minus leading subexpression. returns value +;of leading subexpression. + +(defmacro next-value (lst) + `(cond ((eq (car ,lst) '<) + (let ((temp (get-subexpression ,lst))) + (prog2 nil + (car (eval-arith-expr (cdr ,lst))) + (setq ,lst temp)))) + (t (prog2 nil + (car ,lst) + (setq ,lst (cdr ,lst)))))) + +;prefix/infix-op-funcall calls the prefix/infix op op on the args rest +(defmacro infix-op-funcall (op firstarg secondarg) + `(funcall (get-infix-op-function ,op) + ,firstarg ,secondarg)) + +(defmacro prefix-op-funcall (op arg) + `(funcall (get-prefix-op-function ,op) ,arg)) + +;functions used by assembler + +;gets symbol values for symbols if they exist; sets their FORWARD-REF +;properties if not. +(defun replace-with-values (expr-list &aux (refers-forward nil)) + (do ((nextcdr expr-list (cdr nextcdr))) + ((null nextcdr) refers-forward) + (rplaca nextcdr (assem-symeval-check (car nextcdr) expr-list)))) + +(defun assem-symeval (symbol) + (cond ((numberp symbol) symbol) + ((get symbol 'ASSEM-SYMBOL-VALUE)))) + +(defmacro set-sym-forward-ref (symbol in-list) + `(let ((current (get ,symbol 'FORWARD-REF))) + (cond ((member ,in-list current)) + (t (putprop ,symbol (cons ,in-list current) + 'FORWARD-REF))) + ,in-list)) + +(defun assem-symeval-check (symbol list) + (cond ((assem-op-p symbol) symbol) + ((assem-symeval symbol)) ;if non-nil, returned + (t (set-sym-forward-ref symbol list) + (setq refers-forward t) symbol))) + +(defun eval-expression-for-pattern (expr) + (cond ((atom expr) + (cond ((assem-symeval expr)) + (t expr))) ;since this is atomic we can't forward-ref it. + ((replace-with-values expr) expr) ;if referred forward, this + (t (number-or-expression ;Converts (4) to 4. Leaves everything else as is. + (eval-arith-expr expr))))) + +(defun eval-expression (expr) + (cond ((replace-with-values expr) expr) ;if referred forward, this + (t (number-or-expression ;Converts (4) to 4. Leaves everything else as is. + (eval-arith-expr expr))))) ;is all. Else this. + +(defun number-or-expression (it) + (cond ((atom it) it) + ((or (null (cdr it)) (numberp (car it))) + (car it)) + (t it))) + +;eval-arith-expr also bashes its arg - rplacd's it with nil, and +;rplaca's it with the answer. So it returns a list of the number the +;expression evaluated to. +(defun eval-arith-expr (expr-list) + (let ((lst expr-list)) + (do ((current + (cond ((null lst) + (aerror "Null expression?") 0) + ((prefix-op-p (car lst)) + (prefix-op-funcall (pop lst) + (next-value lst))) + (t (next-value lst))) + (infix-op-funcall (pop lst) + current + (next-value lst)))) + ((null lst) + (rplacd expr-list nil) + (rplaca expr-list current))))) + + +;we don't cons when we can help it (we never cons). Truncates its arg +;to the subexpression (minus trailing >) and returns list after end of +;subexpression. +(defun get-subexpression (list) + (do ((nxtcdr list (cdr nxtcdr)) + (local-depth 1)) + ((and (= 1 local-depth) + (eq (cadr nxtcdr) '>)) + (prog2 nil + (cddr nxtcdr) + (rplacd nxtcdr nil))) + (cond ((eq (cadr nxtcdr) '>) + (setq local-depth (1- local-depth))) + ((eq (cadr nxtcdr) '<) + (setq local-depth (1+ local-depth)))) + (and (null (cdr nxtcdr)) + (prog2 + (aerror "unmatched angle brackets") + (return nil))))) + +;these are already self-delimiters, so I'm not calling DEFARMAC on +;them. If I did, however, the call would look like this: +;(defarmac #/+ +; (ascii #/+)) + +(def-prefix-op '+ +) + +(def-prefix-op '- -) + +(def-infix-op '+ +) + +(def-infix-op '- -) + +(def-infix-op '// //) + +(def-infix-op '* *) + +(def-infix-op '& + (lambda (val1 val2) + (logand val1 val2))) + +(def-infix-op '! + (lambda (val1 val2) + (logand val1 val2))) + +;;; Here are various predicates for use in the defamode patterns. + +(defun eight-bits-p (exp) + (let ((val (or (assem-symeval exp) (eval-expression exp)))) + (cond ((numberp val) (< val 256.)) + (t nil)))) + +(defun eight-bits-or-undefined-p (exp) + (let ((val (or (assem-symeval exp) (eval-expression exp)))) + (cond ((numberp val) (< val 256.)) + (t t)))) + +(defun assem-undefined-p (exp) + (not (assem-defined-p exp))) + +(defun assem-defined-p (exp) + (or (assem-symeval exp) + (and (not (atom exp)) (numberp (eval-expression exp))))) + +(defun lo-8 (expression) + (cond ((assem-undefined-p word) + (eval-expression `(< ,word & 255. >))) + ((eight-bits-p word) word) + (t (logand (assem-symeval word) 255.)))) + +(defun hi-8 (word) + (cond ((symbolp word) + (// (assem-symeval-and-put-fref word) 256.)) + (assem-undefined-p word) + (eval-expression `(< word // 256. >))) + (t (// (assem-symeval word) 256.)))) + +(defun assem-set (symbol value) + (putprop symbol value 'ASSEM-SYMBOL-VALUE)) + +(defun assem-symeval-or-eval (exp) + (or (assem-symeval exp) (eval-expression exp))) + +(defun increment-dot (amount) + (assem-set '/. (+ amount (get '/. 'ASSEM-SYMBOL-VALUE)))) + +(defmacro logand (&rest args) + `(boole 1 ,@args)) diff --git a/src/klotz/match.20 b/src/klotz/match.20 new file mode 100755 index 00000000..f30a35c0 --- /dev/null +++ b/src/klotz/match.20 @@ -0,0 +1,109 @@ +;-*-LISP-*- +;winning simple pattern matcher for parsing losing assembler +;expressions. + +;the idea is this. our syntax is roughly like that of winston's +;pattern matcher (the most obvious syntax possible). There is a target +;list and a candidate list. The candidate is the list that is expected +;to fit the pattern; the target is the pattern specification. We only +;match top-level for this application. + +;the output of the pattern matcher is T if both inputs were nil, a nil +;if the match fails, and an alist if a match with non-nil target +;succeeded. + +;a target is nil, or a list made up of lists and/or atoms. A null +;target will be matched only by a null candidate. Any other target +;will be matched only by a candidate whose members match the target's +;members. To determine if the first member of a candidate matches the +;first member of a target, we use the following rules: +;if the target's first member is an atom, it will match only an atom +;that is eq to it. +;if the target's first member is a two-member list beginning with the +;atom >, it will match any object that is the first member of the +;candidate. In addition, if the entire match succeeds, an alist is +;returned, one of whose members will be the cons of the first member +;of the target and the other the first member of the candidate. +;If the first member of the target is a two-member list beginning with +;the atom *, it will match the first object in the candidate, and all +;succeeding objects until the next member of the target successfully +;matches the next member of the candidate. The alist returned will +;have as one of its members the cons of the second member of the first +;member of the target and a list of all the objects it matched. +;If the target's first member is a list beginning with either the +;symbol > or *, and its second member is an atom, and its third member +;is a predicate that will return t or nil when given an argument of +;the match that was found for that target-atom and candidate, a match +;will first be attempted as above; if it succeeds, the predicate will +;be apply'ed to the target and candidate. If the predicate returns +;non-nil, the match will have succeeded. To determine if the entire +;match has succeeded, we repeat the above procedure on the target and +;candidate, each time removing the members of each that have matched, +;until the target is nil or the candidate is nil. If exactly one of +;the target or candidate is nil at this point, the match will lose and +;nil will be returned. If both are nil, and the alist is non-nil, it +;is returned. If both are nil and the alist is nil, T is returned. + +#M(herald match) + +(defmacro pred-lose? (match assoc) + `(cond ((null (cadr ,match))) + ((not (funcall (cadr ,match) + ,assoc)) + (*throw 'Lost nil)))) + +(defun match (target candidate) + (*catch 'Lost + (do ((match (car target) (car target)) + (result)) ;the list * saves its result in + ((or (null target) + (null candidate)) + (cond (target nil) ;if one of target, candidate + (candidate nil) ;non-nil, return nil + (t (or result t)))) + (*catch 'Win + (progn + (and (atom match) + (cond ((eq match (car candidate)) + (setq target (cdr target) + candidate (cdr candidate)) + (*throw 'Win nil)) + (t (*throw 'Lost nil)))) + (and (eq (car match) '>) + (progn + (pred-lose? match (car candidate)) + (push (car candidate) result) + (setq target (cdr target) + candidate (cdr candidate)) + (*throw 'Win nil))) + (and (eq (car match) '*) + (progn + (cond ((null (cadr target)) + (pred-lose? match candidate) + (push candidate result) + (setq target nil + candidate nil) + (*throw 'Win nil))) + (let ((foo (get-*-match (cadr target) + candidate))) + (and foo (progn + (pred-lose? match (car foo)) + (push (car foo)result ) + (setq target (cdr target) + candidate (cdr foo)) + (*throw 'Win nil))) + (*throw 'Lost nil))))))))) + +(defun next-matches-p (next candidate) + (cond ((null candidate)) + ((atom next) + (eq next (car candidate))) + (t t))) + +(defun get-*-match (next candidate) + (do ((*-list (ncons (car candidate)) + (push (car candidate) *-list)) + (candidate (cdr candidate) + (cdr candidate))) + ((next-matches-p next candidate) + (cons (nreverse *-list) candidate)))) diff --git a/src/klotz/pass1.65 b/src/klotz/pass1.65 new file mode 100755 index 00000000..627ef341 --- /dev/null +++ b/src/klotz/pass1.65 @@ -0,0 +1,137 @@ +;;;-*-LISP-*- +;;The main body of the assembler. Conses up a big list of +;;the code, one line at a time. If there is no undefined reference on a a +;;line, it is completely assembled at read-time. Otherwise, a notation is made of +;;undefined references. Later, when these references are resolved the code +;;with the undefined reference is completed. +;;See the file KLOTZ;PASS1 IDEAS for documentation. + +(herald pass1) + +(declare (special *input-filename* *assem-input-stream*) + (fixsw t) + (*lexpr aerror) + (*expr gettok tokenize-rest-of-line eval-expression match) + (setq defmacro-for-compiling nil)) + +(eval-when (compile eval) + (or (get 'commac 'version) + (load "commac"))) + +(defvar /.) ;Current address. +(defvar assem-obase 16.) + +(defun assemble (*input-filename*) + (assem-set '/. 0) ;default. + (let ((*assem-input-stream* (open *input-filename* '(in ascii dsk block)))) + (unwind-protect + (let ((the-code (assemble-code)) + (undef-ref-count (write-out-symbol-table))) + (setq *foo-code* the-code) + (write-out-code the-code) + (print (format nil "~A assembled. ~D undefined symbols." + *input-filename* undef-ref-count))) + (close *assem-input-stream*)))) + + +(defun assemble-code () + (let ((last-cons (ncons nil))) + (do ((line (assemble-code-line) (assemble-code-line)) + (assembled-code-list last-cons)) + ((eq line '*EOF*) (cdr assembled-code-list)) ;I think we can afford the extra cons. + (cond ((not (null line)) + (rplacd last-cons line) + (cond ((atom (cadr line)) (setq last-cons (last line))) + (t (setq last-cons (ncons line))))))))) + +(defun assemble-code-line (&aux (op? (gettok)) dispatch token-save) + (cond ((setq dispatch (get op? 'ADDRESS-MODE-ALIST)) ;LDA, etc. Mnemonics. + (handle-instruction op? dispatch)) + ((setq dispatch (get op? 'ASSEM-PREFIX-OP)) + (funcall dispatch)) + ((setq token-save op? + op? (gettok) + dispatch (get op? 'ASSEM-INFIX-OP)) + (funcall dispatch token-save)) + (t (aerror "Bad Op or Pseudo-Op" op? token-save (tokenize-rest-of-line)) + (increment-dot 2) ;Somebody goofed, but let's try to save the rest. + (list 0 0)))) ;Put this where the code would have gone. + + +(defun handle-instruction (name modes) + (do ((modes modes (cdr modes)) ;;((mode opcode) (mode opcode)...) + (argl (tokenize-rest-of-line)) + (values)) + ((null modes) (aerror "Addressing mode not recognized" name argl)) + (cond ((setq values (match + (get (caar modes) 'ADDRESS-MODE-PATTERN) + argl)) + (increment-dot (get (caar modes) 'INSTRUCTION-LENGTH)) + (return + (cond ((eq values t) (funcall (get (caar modes) 'AMODE-FORMS) + (cadar modes))) + (t (lexpr-funcall (get (caar modes) 'AMODE-FORMS) + (cadar modes) ;Opcode for this mode. + values)))))))) ;of expressions from pattern matching. + +(defun handle-forward-references-for (label) + (do ((refs (get label 'FORWARD-REF) (cdr refs))) + ((null refs) (remprop label 'FORWARD-REF)) + (eval-expression (car refs)))) ;works by side effect. + +;;; I/O +;From here on down is fucked and should be flushed as soon as possible. +(defun write-out-symbol-table (&aux property) + (let* ((filename (namelist *input-filename*)) + (stream (open (progn (rplaca (last filename) 'SYMTAB) filename) 'out)) + (base assem-obase) + (undefined-reference-count 0)) + (unwind-protect + (mapatoms ;One day the assembler symbols will be on their on obarray. + #'(lambda (sym) + (cond ((setq property (get sym 'ASSEM-SYMBOL-VALUE)) + (princ sym stream) + (tyo #\tab stream) + (princ property stream) + (terpri stream)) + ((setq property (get sym 'FORWARD-REF)) + (princ sym stream) + (tyo #\tab stream) + (princ (length property) stream) + (princ " undefined references." + stream) + (terpri stream) + (setq undefined-reference-count + (1+ undefined-reference-count))))))) + (close stream) + undefined-reference-count)) + + +(defun hex-digit (num) + (cond ((< num 10.) (+ num #/0)) + (t (+ num #.(- #/A 10.))))) + +(defun twos-complement-hex-byte (byte) + (cond ((minusp byte) (hex-byte (+ 256. byte))) + (t (hex-byte byte)))) + +(defun hex-byte (byte &aux (lst '(nil nil))) ;Constant list used for implode. + (rplaca lst (hex-digit (// byte 16.))) + (rplaca (cdr lst) (hex-digit (logand byte 15.))) + (implode lst)) + +;This never writes out the PC when it changes. +(defun write-out-code (it) + (let* ((filename (namelist *input-filename*)) + (stream (open (progn (rplaca (last filename) 'PAPERT) filename) 'out))) + (unwind-protect + (do ((it it (cdr it)) + (bytes 1 (1+ bytes))) + ((null it) t) + (princ (twos-complement-hex-byte (car it)) stream) + (tyo #\space stream) + (cond ((= 8. bytes) + (terpri stream) + (setq bytes 0))))) + (close stream)) + t) diff --git a/src/klotz/tokenz.59 b/src/klotz/tokenz.59 new file mode 100755 index 00000000..ea389692 --- /dev/null +++ b/src/klotz/tokenz.59 @@ -0,0 +1,147 @@ +;;;-*-LISP-*- +; MacLisp Cross Assembler +; The Tokenizer. The routines for reading in tokens and strings. + +(herald tokenz) +(defvar assem-ibase 10.) +(defvar number-context-p nil) +(defvar *untyi-char* nil) +(defvar *assem-input-stream* nil) + +(eval-when (compile eval) + (setq max-token-size 20)) + +(declare (special *token-char-buffer* *assem-read-table*) + (array* (fixnum (*token-char-buffer* #.max-token-size)) + (notype (*assem-read-table* 200))) + (*lexpr aerror) + (fixsw t) + (setq defmacro-for-compiling nil)) + +(array *token-char-buffer* fixnum #.max-token-size)) + +;;; characters read in are consed onto a stack, which costs +;;; much less than heap consing. Since the stack pointer will be +;;; always small it will be an interned fixnum, so we don't lose +;;; by causing fixnum consing there. On the lisp machine we might +;;; simply use cons-in-area, although stack allocation has advantages +;;; even over that. + +(defmacro popbuff (j) + `(prog1 (*token-char-buffer* ,j) + (setq ,j (1- ,j)))) + +(defmacro pushbuff (c j) + `(prog1 (setq ,j (1+ ,j)) + ; adding 1 first makes it look like a list stack. + (store (*token-char-buffer* ,j) ,c))) + +;;; GETTOK is not re-entrant with respect to interrupt calling. +;;; Note special variable for untyi. +(defun gettok () (let ((it (gettok1))) (cond ((null it) (terpri) it) + (t (princ it) (tyo #\sp) it)))) +(defun gettok1 (&AUX + (SP 0) (dispatch) + (CH 0)) + (declare (fixnum SP CH)) + (*catch 'END-OF-LINE + (do () (nil) + (setq ch (agetchar)) + (cond ((or (= ch #\sp) (= ch #\tab)) + (cond ((zerop SP)) ; whitespace. + (t (return (make-token SP))))) + ((setq dispatch (*assem-read-table* ch)) ;only readmacro chars + (cond ((zerop SP) ;have something here. + (return (funcall dispatch ch))) + (t (unagetchar ch) + (return (make-token SP))))) + (t (pushbuff ch SP)))))) ;regular character. + + +(defun decimal-charp (ch) (not (or (> ch #/9) (< ch #/0)))) + +(defun make-token (SP) + ; check to see if it can be a number. + (cond ((represents-a-numberp SP) + (do ((number 0 (+ number + (* factor (numeric-value (popbuff SP))))) + (factor 1 (* factor assem-ibase))) + ((zerop SP) number))) + (t + (make-symbol SP)))) + + +(defun make-symbol (SP + &AUX (list-buffer '#,(DO ((j 0 (1+ j)) + (l nil (cons nil l))) + ((= j #.max-token-size) l)))) + ; to call IMPLODE we must have a list, but we don't want to + ; cons. All the stuff above is to that the list will be consed only + ; once, but not be in pure storage (since we rplaca it). + (and (> SP #.max-token-size) + (aerror "Token too big." (listarray *token-char-buffer*))) + (let ((right-size-sublist (nthcdr (- #.max-token-size SP) list-buffer))) + (do ((l right-size-sublist (cdr l)) + (j 1 (1+ j))) + ((null l) (implode right-size-sublist)) + (rplaca l (*token-char-buffer* j))))) + + +(defun represents-a-numberp (SP) + (or number-context-p + ;if non-nil, we're in a recursive gettok because of a base-conversion readmacro + (and (decimal-charp (*token-char-buffer* 1)) + (do ((c)) + ((zerop SP) t) + (setq c (popbuff SP)) + (or (decimal-charp c) + (a-f-charp c) + (return nil)))))) + +(defun a-f-charp (c) (not (or (> c #/F) (< c #/A)))) + +(defun numeric-value (charn) + (cond ((decimal-charp charn) + (- charn #/0)) + (t (- charn #.(- #/A 10.))))) + +(defun agetchar () + (char-upcase + (cond (*untyi-char* (prog1 *untyi-char* (setq *untyi-char* ()))) + ((tyi *assem-input-stream* nil)) ;if something, return it. + (t (aerror "End of file without .END."))))) + +(defun unagetchar (char) + (setq *untyi-char* char)) + +(defun char-upcase (ch) + (cond ((and (> ch #.(1- #/a)) (< ch #.(1+ #/z))) + (- ch #.(- #/a #/A))) + (t ch))) + +;since this is REST of line, if we get an EOL token we return nil, as +;that is the rest of the line. +(defun tokenize-rest-of-line () ;For Pseudo-op's and Op's use. + (do ((token (gettok) (gettok)) + line) + ((null token) (nreverse line)) ;null token is end of line. + (or (null token) (push token line)))) + +(defun gobble-rest-of-line () + (do () ((= (agetchar) #\cr) (agetchar)))) ;eat up to cr, then lf. + + +(defun read-in-delimited-string () ;| "HELLO" | => HELLO + (do ((delimiter) + (charlst) + (char (agetchar) (agetchar))) + (nil) + (cond (delimiter + (cond ((= char delimiter) + (gobble-rest-of-line) + (return (implode (nreverse charlst)))) + (t (push char charlst)))) + ((or (= char #\sp) (= char #\tab))) + (t (setq delimiter char))))) + +