; ; Global constants ; const FALSE = 0 const TRUE = -1 ; ; Hardware constants ; const csw = $0036 const speaker = $C030 const showgraphics = $C050 const showtext = $C051 const showfull = $C052 const showmix = $C053 const showpage1 = $C054 const showpage2 = $C055 const showlores = $C056 const showhires = $C057 const pushbttn1 = $C061 const pushbttn2 = $C062 const pushbttn3 = $C063 const keyboard = $C000 const keystrobe = $C010 const keyenter = $8D const keyspace = $A0 const keyarrowup = $8B const keyarrowdown = $8A const keyarrowleft = $88 const keyarrowright = $95 const keyescape = $9B const keyctrla = $81 const keyctrlb = $82 const keyctrlc = $83 const keyctrld = $84 const keyctrle = $85 const keyctrli = $89 const keyctrlk = $8B const keyctrll = $8C const keyctrln = $8E const keyctrlo = $8F const keyctrlp = $90 const keyctrlq = $91 const keyctrlr = $92 const keyctrls = $93 const keyctrlt = $94 const keyctrlu = $95 const keyctrlv = $96 const keyctrlw = $97 const keyctrlx = $98 const keyctrlz = $9A const keydelete = $FF const getbuff = $01FF const argbuff = $2006 word txtscrn[] = $0400,$0480,$0500,$0580,$0600,$0680,$0700,$0780 word = $0428,$04A8,$0528,$05A8,$0628,$06A8,$0728,$07A8 word = $0450,$04D0,$0550,$05D0,$0650,$06D0,$0750,$07D0 ; ; Data and text buffer constants ; const machid = $BF98 const maxlines = 626 const maxfill = 640 const iobuffer = $0800 const databuff = $0C00 const strlinbuf = $1000 const strheapmap = $1500 const strheapmsz = $70 ; = memory@16 bytes per bit map, 128 bytes per 8 bit map, 1K bytes per 8 byte map const maxlnlen = 79 const strheap = $7000 const strheasz = $3800 const codebuff = $A800 const codebuffsz = $1000 const pgjmp = 16 const changed = 1 const insmode = 2 const showcurs = 4 const uppercase = 8 const shiftlock = 128 ; ; Editor variables ; byte nullstr[] = "" byte version[] = "PLASMA VERSION 0.3 " byte errorstr[] = "ERROR: $" byte okstr[] = "OK" byte perr byte outofmem[] = "OUT OF MEMORY!" byte losechng[] = "LOSE CHANGES TO FILE (Y/N)?" ;byte emiterr[] = "EMIT CODE/DATA MISMATCH" byte untitled[] = "UNTITLED" byte txtfile[64] = "UNTITLED.PLA" byte flags = 0 byte flash = 0 byte cursx, cursy, scrnleft, curscol, underchr, curschr word cursrow, scrntop, cursptr word numlines = 0 word cutbuf = 0 word keyin ; ; Predeclared functions ; func cmdmode ; ; Compiler variables ; ; ; Tokens ; const ID_TKN = $D6 ; V const CHR_TKN = $C3 ; C const INT_TKN = $C9 ; I const STR_TKN = $D3 ; S const EOL_TKN = $02 const EOF_TKN = $01 const ERR_TKN = $00 ; ; Binary operand operators ; const SET_TKN = $BD ; = const ADD_TKN = $AB ; + const SUB_TKN = $AD ; - const MUL_TKN = $AA ; * const DIV_TKN = $AF ; / const MOD_TKN = $A5 ; % const OR_TKN = $BF ; ? const EOR_TKN = $DE ; ^ const AND_TKN = $A6 ; & const SHR_TKN = $D2 ; R const SHL_TKN = $CC ; L const GT_TKN = $BE ; > const GE_TKN = $C8 ; H const LT_TKN = $BC ; < const LE_TKN = $C2 ; B const NE_TKN = $D5 ; U const EQ_TKN = $C5 ; E const LOGIC_AND_TKN = $CE ; N const LOGIC_OR_TKN = $CF ; O ; ; Unary operand operators ; const AT_TKN = $C0 ; @ const DOT_TKN = $AE ; . const COLON_TKN = $BA ; : const NEG_TKN = $AD ; - const COMP_TKN = $A3 ; # const LOGIC_NOT_TKN = $A1 ; ! const BPTR_TKN = $DE ; ^ const WPTR_TKN = $AA ; * const INC_TKN = $C1 ; A const DEC_TKN = $C4 ; D ; ; Enclosure tokens ; const OPEN_PAREN_TKN = $A8 ; ( const CLOSE_PAREN_TKN = $A9 ; ) const OPEN_BRACKET_TKN = $DB ; [ const CLOSE_BRACKET_TKN = $DD ; ] ; ; Misc. tokens ; const COMMA_TKN = $AC ; , const COMMENT_TKN = $BB ; ; ; ; Keyword tokens ; const CONST_TKN = $80 const BYTE_TKN = $81 const WORD_TKN = $82 const IF_TKN = $83 const ELSEIF_TKN = $84 const ELSE_TKN = $85 const FIN_TKN = $86 const END_TKN = $87 const WHILE_TKN = $88 const LOOP_TKN = $89 const CASE_TKN = $8A const OF_TKN = $8B const DEFAULT_TKN = $8C const ENDCASE_TKN = $8D const FOR_TKN = $8E const TO_TKN = $8F const DOWNTO_TKN = $90 const STEP_TKN = $91 const NEXT_TKN = $92 const REPEAT_TKN = $93 const UNTIL_TKN = $94 const IFUNC_TKN = $95 const TFUNC_TKN = $96 const NFUNC_TKN = $97 const DONE_TKN = $98 const RETURN_TKN = $99 const BREAK_TKN = $9A const START_TKN = $9B const EXIT_TKN = $9C const EVAL_TKN = $9D const FUNC_TKN = $9E ; ; Types ; const CONST_TYPE = $01 const BYTE_TYPE = $02 const WORD_TYPE = $04 const VAR_TYPE = $06 ; (WORD_TYPE | BYTE_TYPE) const FUNC_TYPE = $08 const FUNC_CONST_TYPE = $09 const ADDR_TYPE = $0E ; (VAR_TYPE | FUNC_TYPE) const LOCAL_TYPE = $10 const BPTR_TYPE = $20 const WPTR_TYPE = $40 const PTR_TYPE = $60 ; (BPTR_TYPE | WPTR_TYPE) const XBYTE_TYPE = $22 ; (BPTR_TYPE | BYTE_TYPE) const XWORD_TYPE = $44 ; (WPTR_TYPE | WORD_TYPE) const STR_TYPE = $80 ; ; Keywords ; byte keywrds[] byte = "IF", IF_TKN byte = "TO", TO_TKN byte = "IS", OF_TKN byte = "OR", LOGIC_OR_TKN byte = "FOR", FOR_TKN byte = "FIN", FIN_TKN byte = "DEF", IFUNC_TKN byte = "END", END_TKN byte = "AND", LOGIC_AND_TKN byte = "NOT", LOGIC_NOT_TKN byte = "BYTE", BYTE_TKN byte = "WORD", WORD_TKN byte = "DEFT", TFUNC_TKN byte = "DEFN", NFUNC_TKN byte = "ELSE", ELSE_TKN byte = "NEXT", NEXT_TKN byte = "WHEN", CASE_TKN byte = "LOOP", LOOP_TKN byte = "FUNC", FUNC_TKN byte = "STEP", STEP_TKN byte = "EXIT", EXIT_TKN byte = "DONE", DONE_TKN byte = "WEND", ENDCASE_TKN byte = "CONST", CONST_TKN byte = "ELSIF", ELSEIF_TKN byte = "WHILE", WHILE_TKN byte = "UNTIL", UNTIL_TKN byte = "BREAK", BREAK_TKN byte = "OTHER", DEFAULT_TKN byte = "DOWNTO",DOWNTO_TKN byte = "REPEAT",REPEAT_TKN byte = "RETURN",RETURN_TKN byte = $FF ; ; Mathematical ops ; const bops_tblsz = 17 ; minus 1 byte bops_tbl[] ; Highest precedence byte = MUL_TKN, DIV_TKN, MOD_TKN byte = ADD_TKN, SUB_TKN byte = SHR_TKN, SHL_TKN byte = AND_TKN byte = EOR_TKN byte = OR_TKN byte = GT_TKN, GE_TKN, LT_TKN, LE_TKN byte = EQ_TKN, NE_TKN byte = LOGIC_AND_TKN byte = LOGIC_OR_TKN ; Lowest precedence byte bops_prec[] ; Highest precedence byte = 1, 1, 1 byte = 2, 2 byte = 3, 3 byte = 4 byte = 5 byte = 6 byte = 7, 7, 7, 7 byte = 8, 8 byte = 9 byte = 10 ; Lowest precedence byte opstack[16] byte precstack[16] word opsp = -1 ; ; Symbol table variables ; const idglobal_tblsz = 2048 const idlocal_tblsz = 512 const idglobal_tbl = $1600 const idlocal_tbl = $1E00 const ctag_max = 640 const ctag_value = $800 const ctag_flags = $D80 const idval = 0 const idtype = 2 const idname = 3 const idrecsz = 4 word globals = 0 word datasize = 0 word lastglobal byte locals = 0 word framesize = 0 word lastlocal const resolved = 1 const is_ctag = $8000 const mask_ctag = $7FFF word codetag = -1 word codeptr, entrypoint = 0 byte lastop = $FF ; ; Scanner variables ; const inbuff = $0200 const instr = $01FF byte token, tknlen byte parserrpos, parserr = 0 word scanptr, tknptr, parserrln word constval word lineno = 0 ; ; Compiler output messages ; byte entrypt_str[] = "START: " byte comp_ok_msg[] = "COMPILATION COMPLETE" byte dup_id[] = "DUPLICATE IDENTIFIER" byte undecl_id[] = "UNDECLARED IDENTIFIER" byte bad_cnst[] = "BAD CONSTANT" byte bad_offset[] = "BAD STRUCT OFFSET" byte bad_decl[] = "BAD DECLARATION" byte bad_op[] = "BAD OPERATION" byte bad_stmnt[] = "BAD STATMENT" byte bad_expr[] = "BAD EXPRESSION" byte bad_syntax[] = "BAD SYNTAX" byte estk_overflw[] = "EVAL STACK OVERFLOW" byte estk_underflw[] = "EVAL STACK UNDERFLOW" byte local_overflw[] = "LOCAL FRAME OVERFLOW" byte global_sym_overflw[] = "GLOBAL SYMBOL TABLE OVERFLOW" byte local_sym_overflw[] = "LOCAL SYMBOL TABLE OVERFLOW" byte ctag_full[] = "CODE LABEL OVERFLOW" byte no_close_paren[] = "MISSING CLOSING PAREN" byte no_close_bracket[] = "MISSING CLOSING BRACKET" byte missing_op[] = "MISSING OPERAND" byte no_fin[] = "MISSING FIN" byte no_loop[] = "MISSING LOOP" byte no_until[] = "MISSING UNTIL" byte no_done[] = "MISSING DONE" byte no_local_init[] = "NO INITIALIZED LOCALS" ; ; Runtime functions ; byte runtime0[] = "romcall" byte RUNTIME0[] = "ROMCALL" byte runtime1[] = "syscall" byte RUNTIME1[] = "SYSCALL" byte runtime2[] = "memset" byte RUNTIME2[] = "MEMSET" byte runtime3[] = "memcpy" byte RUNTIME3[] = "MEMCPY" byte runtime4[] = "cout" byte RUNTIME4[] = "COUT" byte runtime5[] = "cin" byte RUNTIME5[] = "CIN" byte runtime6[] = "prstr" byte RUNTIME6[] = "PRSTR" byte runtime7[] = "rdstr" byte RUNTIME7[] = "RDSTR" ; ; Parser variables ; byte infunc = 0 byte stack_loop = 0 byte prevstmnt = 0 word retfunc_tag = 0 word break_tag = 0 func parse_expr, parse_module ; ; ProDOS routines ; def getpfx(path) byte params[3] ^path = 0 params.0 = 1 params:1 = path perr = syscall($C7, @params) return path end def setpfx(path) byte params[3] params.0 = 1 params:1 = path perr = syscall($C6, @params) return path end def open(path, buff) byte params[6] params.0 = 3 params:1 = path params:3 = buff perr = syscall($C8, @params) return params.5 end def close(refnum) byte params[2] params.0 = 1 params.1 = refnum perr = syscall($CC, @params) return perr end def read(refnum, buff, len) byte params[8] params.0 = 4 params.1 = refnum params:2 = buff params:4 = len params:6 = 0 perr = syscall($CA, @params) return params:6 end def write(refnum, buff, len) byte params[8] params.0 = 4 params.1 = refnum params:2 = buff params:4 = len params:6 = 0 perr = syscall($CB, @params) return params:6 end def create(path, access, type, aux) byte params[12] params.0 = 7 params:1 = path params.3 = access params.4 = type params:5 = aux params.7 = $1 params:8 = 0 params:10 = 0 perr = syscall($C0, @params) return perr end def destroy(path) byte params[12] params.0 = 1 params:1 = path perr = syscall($C1, @params) return perr end def newline(refnum, emask, nlchar) byte params[4] params.0 = 3 params.1 = refnum params.2 = emask params.3 = nlchar perr = syscall($C9, @params) return perr end ;===================================== ; ; Editor ; ;===================================== def crout return cout ($0D) end def bell return romcall(0, 0, 0, 0, $FBDD) end ; ; Memory management routines ; def strcpy(srcstr, dststr) byte strlen strlen = ^srcstr while (srcstr).[strlen] == $8D or (srcstr).[strlen] == $A0 strlen = strlen - 1 loop ^dststr = strlen return memcpy(srcstr + 1, dststr + 1, strlen) end def heapaddr(ofst, mask) word addr addr = (ofst << 7) + strheap while !(mask & 1) addr = addr + 16 mask = mask >> 1 loop return addr end def sizemask(size) if size <= 16 return $01 elsif size <= 32 return $03 elsif size <= 48 return $07 elsif size <= 64 return $0F elsif size <= 80 return $1F fin end deft heapalloc(size) byte szmask, i word mapmask szmask = sizemask(size) for i = strheapmsz - 1 downto 0 if strheapmap.[i] <> $FF mapmask = szmask repeat if strheapmap.[i] & mapmask mapmask = mapmask << 1 else strheapmap.[i] = strheapmap.[i] ? mapmask return heapaddr(i, mapmask) fin until mapmask & $100 fin next bell prstr(@outofmem) end def freestr(strptr) byte mask, ofst if strptr and strptr <> @nullstr mask = sizemask(^strptr + 1) ofst = (strptr - strheap) >> 4 mask = mask << (ofst & $07) ofst = ofst >> 3 strheapmap.[ofst] = strheapmap.[ofst] & #mask fin end def newstr(strptr) byte strlen word newptr strlen = ^strptr while (strptr).[strlen] == $8D or (strptr).[strlen] == $A0 strlen = strlen - 1 loop if strlen == 0 return @nullstr fin newptr = heapalloc(strlen + 1) if newptr memcpy(strptr, newptr, strlen + 1) ^newptr = strlen return newptr fin return @nullstr end def inittxtbuf word i memset(0, strheapmap, strheapmsz) memset(@nullstr, strlinbuf, maxfill * 2) entrypoint = 0 numlines = 0 cursrow = 0 curscol = 0 cursx = 0 cursy = 0 scrnleft = 0 scrntop = 0 cutbuf = 0 end ; ; Case conversion/printing routines ; def caseconv(chr) if flags & uppercase if chr & $E0 == $E0 chr = chr - $E0 fin fin return chr end def strupper(strptr) byte i, chr for i = ^strptr downto 1 chr = (strptr).[i] if chr & $E0 == $E0 (strptr).[i] = chr - $E0 fin next end def strlower(strptr) byte i, chr for i = ^strptr downto 1 chr = (strptr).[i] if chr & $E0 == $00 (strptr).[i] = chr + $E0 fin next end def txtupper word i, strptr flags = flags ? uppercase for i = numlines - 1 downto 0 strupper(strlinbuf:[i]) next end def txtlower word i, strptr flags = flags & #uppercase for i = numlines - 1 downto 0 strlower(strlinbuf:[i]) next end def prbyte(h) cout('$') romcall(h, 0, 0, 0, $FDDA) end def prword(h) cout('$') romcall(h >> 8, h, 0, 0, $F941) end def print(i) byte numstr[7] byte place, sign place = 6 if i < 0 sign = 1 i = -i else sign = 0 fin while i >= 10 numstr[place] = i % 10 + '0' i = i / 10 place = place - 1 loop numstr[place] = i + '0' place = place - 1 if sign numstr[place] = '-' place = place - 1 fin numstr[place] = 6 - place prstr(@numstr[place]) end def nametostr(namestr, len, strptr) ^strptr = len memcpy(namestr, strptr + 1, len) end ;def toupper(c) ; if c >= 'a' ; if c <= 'z' ; return c - $20 ; fin ; fin ; return c ;end asm toupper LDA ESTKL,X CMP #'a' BCC :+ CMP #'z'+1 BCS :+ SEC SBC #$20 STA ESTKL,X : RTS end asm clrhibit(strptr) LDY #$02 ; strptr LDA (FP),Y STA TMPL INY LDA (FP),Y STA TMPH LDY #$00 LDA (TMP),Y BEQ :+ TAY CLHILP: LDA (TMP),Y AND #$7F STA (TMP),Y DEY BNE CLHILP : end asm sethibit(strptr) LDY #$02 ; strptr LDA (FP),Y STA TMPL INY LDA (FP),Y STA TMPH LDY #$00 LDA (TMP),Y BEQ :+ TAY STHILP: LDA (TMP),Y ORA #$80 STA (TMP),Y DEY BNE STHILP : end asm cpyln(srcstr, dststr) LDY #$02 ; srcstr LDA (FP),Y STA TMPL INY LDA (FP),Y STA TMPH INY ; dststr LDA (FP),Y STA $06 INY LDA (FP),Y STA $07 LDY #$00 LDA (TMP),Y TAY LDA #$00 INY STA ($06),Y DEY BEQ :++ CPLNLP: LDA (TMP),Y CMP #$20 BCS :+ ADC #$60 : AND #$7F STA ($06),Y DEY BNE CPLNLP LDA (TMP),Y : STA ($06),Y end ; ; File routines ; def readtxt(filename) byte txtbuf[81], refnum, i, j refnum = open(filename, iobuffer) if refnum == 0 return 0 fin newline(refnum, $7F, $0D) repeat txtbuf = read(refnum, @txtbuf + 1, maxlnlen) if txtbuf sethibit(@txtbuf) if flags & uppercase strupper(@txtbuf) fin strlinbuf:[numlines] = newstr(@txtbuf) numlines = numlines + 1 fin if !(numlines & $0F) cout('.') fin until txtbuf == 0 or numlines == maxlines close(refnum) end def writetxt(filename) byte txtbuf[81], refnum byte j, chr word i, strptr destroy(filename) create(filename, $C3, $04, $00) ; full access, TXT file refnum = open(filename, iobuffer) if refnum == 0 return 0 fin for i = 0 to numlines - 1 cpyln(strlinbuf:[i], @txtbuf) txtbuf = txtbuf + 1 txtbuf[txtbuf] = $0D write(refnum, @txtbuf + 1, txtbuf) if !(i & $0F) cout('.') fin next close(refnum) end ; ; Screen routines ; def drawrow(row, ofst, strptr) byte numchars word scrnptr scrnptr = txtscrn[row] if ^strptr <= ofst numchars = 0 else numchars = ^strptr - ofst fin if numchars >= 40 numchars = 40 else memset($A0A0, scrnptr + numchars, 40 - numchars) fin memcpy(strptr + ofst + 1, scrnptr, numchars) end deft drawscrn(toprow, ofst) byte row, numchars word strptr, scrnptr for row = 0 to 23 strptr = strlinbuf:[toprow + row] scrnptr = txtscrn[row] if ^strptr <= ofst numchars = 0 else numchars = ^strptr - ofst fin if numchars >= 40 numchars = 40 else memset($A0A0, scrnptr + numchars, 40 - numchars) fin memcpy(strptr + ofst + 1, scrnptr, numchars) next end def cursoff if flags & showcurs ^cursptr = underchr flags = flags & #showcurs fin end def curson if !(flags & showcurs) cursptr = txtscrn[cursy] + cursx underchr = ^cursptr ^cursptr = curschr flags = flags ? showcurs fin end def cursflash if flags & showcurs if flash == 0 ^cursptr = curschr elsif flash == 128 ^cursptr = underchr fin flash = flash + 1 fin end def redraw cursoff drawscrn(scrntop, scrnleft) curson end def curshome cursoff cursrow = 0 curscol = 0 cursx = 0 cursy = 0 scrnleft = 0 scrntop = 0 drawscrn(scrntop, scrnleft) curson end def cursend cursoff if numlines > 23 cursrow = numlines - 1 cursy = 23 scrntop = cursrow - 23 else cursrow = numlines - 1 cursy = numlines - 1 scrntop = 0 fin curscol = 0 cursx = 0 scrnleft = 0 drawscrn(scrntop, scrnleft) curson end def cursup if cursrow > 0 cursoff cursrow = cursrow - 1 if cursy > 0 cursy = cursy - 1 else scrntop = cursrow drawscrn(scrntop, scrnleft) fin curson fin end def pgup byte i for i = pgjmp downto 0 cursup next end def cursdown if cursrow < numlines - 1 cursoff cursrow = cursrow + 1 if cursy < 23 cursy = cursy + 1 else scrntop = cursrow - 23 drawscrn(scrntop, scrnleft) fin curson fin end def pgdown byte i for i = pgjmp downto 0 cursdown next end def cursleft if curscol > 0 cursoff curscol = curscol - 1 if cursx > 0 cursx = cursx - 1 else scrnleft = curscol drawscrn(scrntop, scrnleft) fin curson fin end def pgleft byte i for i = 7 downto 0 cursleft next end def cursright if curscol < 80 cursoff curscol = curscol + 1 if cursx < 39 cursx = cursx + 1 else scrnleft = curscol - 39 drawscrn(scrntop, scrnleft) fin curson fin end def pgright byte i for i = 7 downto 0 cursright next end ; ; Keyboard routines ; def keyin2e repeat cursflash until ^keyboard >= 128 return ^keystrobe end def keyin2 byte key, flash repeat cursflash key = ^keyboard if key == keyctrll ^keystrobe flags = flags ^ shiftlock key = 0 fin until key >= 128 ^keystrobe if key == keyctrln key = $DB ; [ elsif key == keyctrlp key = $DF ; _ elsif key == keyctrlb key = $DC ; \ elsif key == keyarrowleft if ^pushbttn3 < 128 key = $FF fin elsif key >= $C0 and flags < shiftlock if ^pushbttn3 < 128 if key == $C0 key = $D0 ; P elsif key == $DD key = $CD ; M elsif key == $DE key = $CE ; N fin else key = key ? $E0 fin fin return key end ; ; Printer routines ; def printtxt(slot) byte txtbuf[80] word i, scrncsw scrncsw = *(csw) *(csw) = $C000 ? (slot << 8) for i = 0 to numlines - 1 cpyln(strlinbuf:[i], @txtbuf) prstr(@txtbuf) crout next *(csw) = scrncsw end def openline(row) if numlines < maxlines memcpy(@strlinbuf:[row], @strlinbuf:[row + 1], (numlines - row) * 2) strlinbuf:[row] = @nullstr numlines = numlines + 1 flags = flags ? changed return 1 fin bell return 0 end def cutline freestr(cutbuf) cutbuf = strlinbuf:[cursrow] memcpy(@strlinbuf:[cursrow + 1], @strlinbuf:[cursrow], (numlines - cursrow) * 2) if numlines > 1 numlines = numlines - 1 fin flags = flags ? changed if cursrow == numlines cursup fin redraw end def pasteline if cutbuf and numlines < maxlines memcpy(@strlinbuf:[cursrow], @strlinbuf:[cursrow + 1], (numlines - cursrow) * 2) strlinbuf:[cursrow] = newstr(cutbuf) numlines = numlines + 1 flags = flags ? changed redraw else bell fin end def joinline byte joinstr[80], joinlen if cursrow < numlines - 1 strcpy(strlinbuf:[cursrow], @joinstr) joinlen = joinstr + ^(strlinbuf:[cursrow + 1]) if joinlen < 80 memcpy(strlinbuf:[cursrow + 1] + 1, @joinstr + joinstr + 1, ^(strlinbuf:[cursrow + 1])) joinstr = joinlen freestr(strlinbuf:[cursrow]) strlinbuf:[cursrow] = newstr(@joinstr) freestr(strlinbuf:[cursrow + 1]) numlines = numlines - 1 memcpy(@strlinbuf:[cursrow + 2], @strlinbuf:[cursrow + 1], (numlines - cursrow) * 2) flags = flags ? changed redraw else bell fin fin end def splitline byte splitstr[80], splitlen if openline(cursrow + 1) if curscol splitlen = ^(strlinbuf:[cursrow]) if curscol < splitlen - 1 memcpy(strlinbuf:[cursrow] + curscol + 1, @splitstr + 1, splitlen - curscol) splitstr = splitlen - curscol strlinbuf:[cursrow + 1] = newstr(@splitstr) memcpy(strlinbuf:[cursrow] + 1, @splitstr + 1, curscol) splitstr = curscol freestr(strlinbuf:[cursrow]) strlinbuf:[cursrow] = newstr(@splitstr) fin else strlinbuf:[cursrow + 1] = strlinbuf:[cursrow] strlinbuf:[cursrow] = @nullstr fin curscol = 0 cursx = 0 scrnleft = 0 redraw cursdown fin end def editkey(key) if key >= keyspace return 1 elsif key == keydelete return 1 elsif key == keyctrld return 1 elsif key == keyctrlr return 1 fin end def editline(key) byte editstr[80] word undoline if (editkey(key)) flags = flags ? changed memset($A0A0, @editstr, 80) strcpy(strlinbuf:[cursrow], @editstr) undoline = strlinbuf:[cursrow] strlinbuf:[cursrow] = @editstr repeat if key >= keyspace if key == keydelete if curscol > 0 if curscol <= editstr memcpy(@editstr[curscol + 1], @editstr[curscol], editstr - curscol) editstr = editstr - 1 fin curscol = curscol - 1 cursoff if cursx > 0 cursx = cursx - 1 drawrow(cursy, scrnleft, @editstr) else scrnleft = scrnleft - 1 drawscrn(scrntop, scrnleft) fin curson fin elsif curscol < maxlnlen curscol = curscol + 1 cursx = cursx + 1 if flags & insmode if editstr < maxlnlen or editstr.maxlnlen == $A0 editstr = editstr + 1 if curscol >= editstr editstr = curscol else memcpy(@editstr[curscol], @editstr[curscol + 1], editstr - curscol) fin else curscol = curscol - 1 cursx = cursx - 1 key = editstr[curscol] bell fin else if curscol > editstr editstr = curscol fin fin editstr[curscol] = caseconv(key) cursoff if cursx <= 39 drawrow(cursy, scrnleft, @editstr) else scrnleft = scrnleft + 1 cursx = 39 drawscrn(scrntop, scrnleft) fin curson else bell fin elsif key == keyctrld if curscol < editstr memcpy(@editstr[curscol + 2], @editstr[curscol + 1], editstr - curscol) editstr = editstr - 1 cursoff drawrow(cursy, scrnleft, @editstr) curson fin elsif key == keyctrlr strcpy(undoline, @editstr) cursoff drawrow(cursy, scrnleft, @editstr) curson fin key = keyin() until !editkey(key) if editstr strlinbuf:[cursrow] = newstr(@editstr) else strlinbuf:[cursrow] = @nullstr fin freestr(undoline) fin return key end def editmode repeat when editline(keyin()) is keyarrowup cursup is keyarrowdown cursdown is keyarrowleft cursleft is keyarrowright cursright is keyctrlw pgup is keyctrlz pgdown is keyctrla pgleft is keyctrls pgright is keyctrlq curshome is keyctrle cursend is keyctrlx cutline is keyctrlv pasteline is keyctrlo openline(cursrow) redraw is keyenter if flags & insmode splitline else openline(cursrow + 1) cursdown redraw fin is keyctrlt joinline is keyctrli if flags & insmode flags = flags & #insmode curschr = ' ' else flags = flags ? insmode curschr = '+' fin is keyctrlc if flags & uppercase txtlower else txtupper fin redraw is keyescape cursoff cmdmode redraw otherwise bell wend until 0 end ; ; Command mode ; def prfiles(optpath) byte path[64] byte refnum byte firstblk byte entrylen, entriesblk byte i, type, len word entry, filecnt if ^optpath strcpy(optpath, @path) else getpfx(@path) prstr(@path) crout fin refnum = open(@path, iobuffer); if perr return perr fin firstblk = 1 repeat if read(refnum, databuff, 512) == 512 entry = databuff + 4 if firstblk entrylen = databuff.$23 entriesblk = databuff.$24 filecnt = databuff:$25 entry = entry + entrylen fin for i = firstblk to entriesblk type = ^entry if type <> 0 len = type & $0F ^entry = len prstr(entry) if type & $F0 == $D0 ; Is it a directory? cout('/') len = len + 1 fin for len = 20 - len downto 1 cout(' ') next filecnt = filecnt - 1 fin entry = entry + entrylen next firstblk = 0 else filecnt = 0 fin until filecnt == 0 close(refnum) crout return 0 end def striplead(strptr, chr) while ^strptr and ^(strptr + 1) == chr memcpy(strptr + 2, strptr + 1, ^strptr) ^strptr = ^strptr - 1 loop end def parsecmd(strptr) byte cmd cmd = 0 striplead(strptr, $A0) if ^strptr cmd = ^(strptr + 1) striplead(strptr, cmd) fin if ^strptr striplead(strptr, $A0) fin return cmd & $7F end def upcase(chr) if chr >= 'a' and chr <= 'z' chr = chr - 'a' + 'A' fin return chr end def chkchng if flags & changed prstr(@losechng) if upcase(keyin() & $7F) == 'N' crout return 0 fin crout fin return 1 end def quit if chkchng exit fin end def cmdmode byte slot word cmdptr romcall(0, 0, 0, 0, $FC58) crout while 1 prstr(@txtfile) cmdptr = rdstr($BA) when upcase(parsecmd(cmdptr)) is 'A' readtxt(cmdptr) flags = flags ? changed is 'R' if chkchng inittxtbuf strcpy(cmdptr, @txtfile) readtxt(@txtfile) entrypoint = 0 flags = flags & #changed fin is 'W' if ^cmdptr strcpy(cmdptr, @txtfile) fin writetxt(@txtfile) if flags & changed entrypoint = 0 fin flags = flags & #changed is 'Q' quit is 'C' prfiles(cmdptr) is 'P' setpfx(cmdptr) is 'H' if ^cmdptr slot = cmdptr.1 & $7F - '0' else slot = 1 fin printtxt(slot) is 'E' return is 0 return is 'N' if chkchng inittxtbuf numlines = 1 strcpy(@untitled, @txtfile) fin is 'X' if flags & changed or !entrypoint parse_module if parserr bell cursrow = parserrln scrntop = cursrow & $FFF8 cursy = cursrow - scrntop curscol = parserrpos scrnleft = curscol & $FFE0 cursx = curscol - scrnleft else crout (entrypoint)() fin else (entrypoint)() fin crout is 'V' prstr(@version) wend if perr prstr(@errorstr) romcall(perr, 0, 0, 0, $FDDA) else prstr(@okstr) fin crout loop end ;===================================== ; ; PLASMA Compiler ; ;===================================== ; ; Error handler ; def parse_err(err) if !parserr parserr = TRUE parserrln = lineno - 1 parserrpos = tknptr - inbuff print(lineno) cout(':') prstr(err) crout fin return ERR_TKN end ; ; Emit bytecode ; def ctag_new if codetag >= ctag_max return parse_err(@ctag_full) fin codetag = codetag + 1 ctag_value:[codetag] = 0 ctag_flags.[codetag] = 0 return codetag ? is_ctag end def ctag_resolve(tag, addr) word updtptr, nextptr tag = tag & mask_ctag if ctag_flags.[tag] & resolved return parse_err(@dup_id) fin updtptr = ctag_value:[tag] while updtptr ; ; Update list of addresses needing resolution ; nextptr = *updtptr *updtptr = addr updtptr = nextptr loop ctag_value:[tag] = addr ctag_flags.[tag] = ctag_flags.[tag] ? resolved end defn emit_byte(bval) ^codeptr = bval codeptr = codeptr + 1 end defn emit_word(wval) *codeptr = wval codeptr = codeptr + 2 end def emit_fill(size) memset(0, codeptr, size) codeptr = codeptr + size end def emit_codetag(tag) return ctag_resolve(tag, codeptr) end deft emit_op(op) lastop = op return emit_byte(op) end def emit_tag(tag) word updtptr if tag & is_ctag tag = tag & mask_ctag updtptr = ctag_value:[tag] if !(ctag_flags.[tag] & resolved) ; ; Add to list of tags needing resolution ; ctag_value:[tag] = codeptr fin emit_word(updtptr) else emit_word(tag + codebuff) fin end def emit_iddata(value, size, namestr) return emit_fill(size) end def emit_data(vartype, consttype, constval, constsize) byte i word size, chrptr if consttype == 0 size = constsize emit_fill(constsize) elsif consttype == STR_TYPE size = constsize chrptr = constval constsize = constsize - 1 emit_byte(constsize) while constsize > 0 emit_byte(^chrptr) chrptr = chrptr + 1 constsize = constsize - 1 loop else if vartype == WORD_TYPE size = 2 emit_word(constval) else size = 1 emit_byte(constval) fin fin return size end def emit_const(cval) if cval == 0 emit_op($00) elsif cval > 0 and cval < 256 emit_op($2A) emit_byte(cval) else emit_op($2C) emit_word(cval) fin end def emit_lb return emit_op($60) end def emit_lw return emit_op($62) end def emit_llb(index) emit_op($64) return emit_byte(index) end def emit_llw(index) emit_op($66) return emit_byte(index) end def emit_lab(tag) emit_op($68) return emit_tag(tag) end def emit_law(tag) emit_op($6A) return emit_tag(tag) end def emit_sb return emit_op($70) end def emit_sw return emit_op($72) end def emit_slb(index) emit_op($74) return emit_byte(index) end def emit_slw(index) emit_op($76) return emit_byte(index) end def emit_dlb(index) emit_op($6C) return emit_byte(index) end def emit_dlw(index) emit_op($6E) return emit_byte(index) end def emit_sab(tag) emit_op($78) return emit_tag(tag) end def emit_saw(tag) emit_op($7A) return emit_tag(tag) end def emit_dab(tag) emit_op($7C) return emit_tag(tag) end def emit_daw(tag) emit_op($7E) return emit_tag(tag) end def emit_call(tag, cparams) emit_op($54) return emit_tag(tag) end def emit_ical(cparams) emit_op($56) return emit_byte(cparams) end def emit_push emit_op($34) end def emit_pull ; ; Skip if last op was push ; if lastop == $34 codeptr = codeptr - 1 lastop = $FF else emit_op($36) fin end def emit_localaddr(index) emit_op($28) return emit_byte(index) end def emit_globaladdr(tag) emit_op($26) return emit_tag(tag) end def emit_indexbyte return emit_op($2E) end def emit_indexword return emit_op($1E) end def emit_unaryop(op) when op is NEG_TKN emit_op($10) is COMP_TKN emit_op($12) is LOGIC_NOT_TKN emit_op($20) is INC_TKN emit_op($0C) is DEC_TKN emit_op($0E) is BPTR_TKN emit_op($60) is WPTR_TKN emit_op($62) otherwise return FALSE wend return TRUE end def emit_binaryop(op) when op is MUL_TKN ; ; Replace MUL 2 with SHL 1 ; if lastop == $2A and ^(codeptr - 1) == 2 ; CB 2 codeptr = codeptr - 1 emit_byte(1) ; CB 1 emit_op($1A) ; SHL else emit_op($06) fin is DIV_TKN ; ; Replace DIV 2 with SHR 1 ; if lastop == $2A and ^(codeptr - 1) == 2 ; CB 2 codeptr = codeptr - 1 emit_byte(1) ; CB 1 emit_op($1C) ; SHR else emit_op($08) fin is MOD_TKN emit_op($0A) is ADD_TKN ; ; Replace ADD 1 with INCR ; if lastop == $2A and ^(codeptr - 1) == 1 ; CB 1 codeptr = codeptr - 2 emit_op($0C) ; INC_OP else emit_op($02) fin is SUB_TKN ; ; Replace SUB 1 with DECR ; if lastop == $2A and ^(codeptr - 1) == 1 ; CB 1 codeptr = codeptr - 2 emit_op($0E) ; DEC_OP else emit_op($04) fin is SHL_TKN emit_op($1A) is SHR_TKN emit_op($1C) is AND_TKN emit_op($14) is OR_TKN emit_op($16) is EOR_TKN emit_op($18) is EQ_TKN emit_op($40) is NE_TKN emit_op($42) is GE_TKN emit_op($48) is LT_TKN emit_op($46) is GT_TKN emit_op($44) is LE_TKN emit_op($4A) is LOGIC_OR_TKN emit_op($22) is LOGIC_AND_TKN emit_op($24) otherwise return FALSE wend return TRUE end def emit_brtru(tag) emit_op($4E) return emit_tag(tag) end def emit_brfls(tag) emit_op($4C) return emit_tag(tag) end def emit_brgt(tag) emit_op($3A) return emit_tag(tag) end def emit_brlt(tag) emit_op($38) return emit_tag(tag) end def emit_brne(tag) emit_op($3E) return emit_tag(tag) end def emit_jump(tag) emit_op($50) return emit_tag(tag) end def emit_drop return emit_op($30) end def emit_leave(framesize) if framesize > 2 emit_op($5A) else emit_op($5C) fin end def emit_enter(framesize, cparams) emit_byte(emit_enter.[0]) emit_byte(emit_enter.[1]) emit_byte(emit_enter.[2]) if framesize > 2 emit_op($58) emit_byte(framesize) emit_byte(cparams) fin end def emit_start ; ; Save address ; entrypoint = codeptr emit_byte(emit_start.[0]) emit_byte(emit_start.[1]) return emit_op(emit_start.[2]) end def emit_exit emit_op($00) return emit_op($5C) end ; ; Lexical anaylzer ; ;defn isalpha(c) ; if c >= 'A' and c <= 'Z' ; return TRUE ; elsif c >= 'a' and c <= 'z' ; return TRUE ; elsif c == '_' ; return TRUE ; fin ; return FALSE ;end asm isalpha LDY #$00 LDA ESTKL,X CMP #'A' BCC ISALRET CMP #'Z'+1 BCS :+ DEY BNE ISALRET : CMP #'a' BCC ISALRET CMP #'z'+1 BCS :+ DEY BNE ISALRET : CMP #'_' BNE ISALRET DEY ISALRET: STY ESTKL,X STY ESTKH,X RTS end ;defn isnum(c) ; if c >= '0' and c <= '9' ; return TRUE ; fin ; return FALSE ;end asm isnum LDY #$00 LDA ESTKL,X CMP #'0' BCC :+ CMP #'9'+1 BCS :+ DEY : STY ESTKL,X STY ESTKH,X RTS end ;defn isalphanum(c) ; if c >= 'A' and c <= 'Z' ; return TRUE ; elsif c >= '0' and c <= '9' ; return TRUE ; elsif c >= 'a' and c <= 'z' ; return TRUE ; elsif c == '_' ; return TRUE ; fin ; return FALSE ;end asm isalphanum LDY #$00 LDA ESTKL,X CMP #'0' BCC ISANRET CMP #'9'+1 BCS :+ DEY BNE ISANRET : CMP #'A' BCC ISANRET CMP #'Z'+1 BCS :+ DEY BNE ISANRET : CMP #'a' BCC ISANRET CMP #'z'+1 BCS :+ DEY BNE ISANRET : CMP #'_' BNE ISANRET DEY ISANRET: STY ESTKL,X STY ESTKH,X RTS end deft keymatch(chrptr, len) byte i, keypos keypos = 0 while keywrds[keypos] < len keypos = keypos + keywrds[keypos] + 2 loop while keywrds[keypos] == len for i = 1 to len if toupper((chrptr).[i - 1]) <> keywrds[keypos + i] break fin next if i > len return keywrds[keypos + keywrds[keypos] + 1] fin keypos = keypos + keywrds[keypos] + 2 loop return ID_TKN end def skipspace ; ; Skip whitespace ; while ^scanptr and ^scanptr <= ' ' scanptr = scanptr + 1 loop tknptr = scanptr return !^scanptr or ^scanptr == ';' end deft scan ; ; Scan for token based on first character ; if skipspace if token <> EOF_TKN token = EOL_TKN fin elsif isalpha(^scanptr) ; ; ID, either variable name or reserved word ; repeat scanptr = scanptr + 1 until !isalphanum(^scanptr) tknlen = scanptr - tknptr; token = keymatch(tknptr, tknlen) elsif isnum(^scanptr) ; ; Number constant ; token = INT_TKN constval = 0 repeat constval = constval * 10 + ^scanptr - '0' scanptr = scanptr + 1 until !isnum(^scanptr) elsif ^scanptr == '$' ; ; Hexadecimal constant ; token = INT_TKN; constval = 0 repeat scanptr = scanptr + 1 if ^scanptr >= '0' and ^scanptr <= '9' constval = (constval << 4) + ^scanptr - '0' elsif ^scanptr >= 'A' and ^scanptr <= 'F' constval = (constval << 4) + ^scanptr - '7'; 'A'-10 elsif ^scanptr >= 'a' and ^scanptr <= 'f' constval = (constval << 4) + ^scanptr - 'W'; 'a'-10 else break; fin until !^scanptr elsif ^scanptr == $27 ; ' ; ; Character constant ; token = CHR_TKN if ^(scanptr + 1) <> $5C ; \ constval = ^(scanptr + 1) if ^(scanptr + 2) <> $27 ; ' return parse_err(@bad_cnst) fin scanptr = scanptr + 3 else when ^(scanptr + 2) is 'n' constval = $0D is 'r' constval = $0A is 't' constval = $09 otherwise constval = ^(scanptr + 2) wend if ^(scanptr + 3) <> $27 ; ' return parse_err(@bad_cnst) fin scanptr = scanptr + 4 fin elsif ^scanptr == '"' ; ; String constant ; token = STR_TKN scanptr = scanptr + 1 constval = scanptr while ^scanptr and ^scanptr <> '"' scanptr = scanptr + 1 loop if !^scanptr return parse_err(@bad_cnst) fin scanptr = scanptr + 1 else ; ; Potential two and three character tokens ; when ^scanptr is '>' if ^(scanptr + 1) == '>' token = SHR_TKN scanptr = scanptr + 2 elsif ^(scanptr + 1) == '=' token = GE_TKN scanptr = scanptr + 2 else token = GT_TKN scanptr = scanptr + 1 fin is '<' if ^(scanptr + 1) == '<' token = SHL_TKN scanptr = scanptr + 2 elsif ^(scanptr + 1) == '=' token = LE_TKN scanptr = scanptr + 2 elsif ^(scanptr + 1) == '>' token = NE_TKN scanptr = scanptr + 2 else token = LT_TKN scanptr = scanptr + 1 fin is '=' if ^(scanptr + 1) == '=' token = EQ_TKN scanptr = scanptr + 2; else token = SET_TKN; scanptr = scanptr + 1 fin otherwise ; ; Simple single character tokens ; token = ^scanptr ? $80 scanptr = scanptr + 1 wend fin tknlen = scanptr - tknptr return token end def rewind(ptr) scanptr = ptr end ; ; Get next line of input ; def nextln ; if ^keyboard == $A0 ; ^keystrobe ; while ^keyboard < 128 ; loop ; ^keystrobe ; elsif ^keyboard == $82 ; lineno = numlines ; ^keystrobe ; fin scanptr = inbuff if lineno < numlines cpyln(strlinbuf:[lineno], instr) lineno = lineno + 1 if !(lineno & $0F) cout('.') fin ; cout('>') ; prstr(instr) ; crout scan else ^instr = 0 ^inbuff = $00 token = DONE_TKN fin return ^instr end ; ; Alebraic op to stack op ; def push_op(op, prec) opsp = opsp + 1 if opsp == 16 return parse_err(@estk_overflw) fin opstack[opsp] = op precstack[opsp] = prec end def pop_op if opsp < 0 return parse_err(@estk_underflw) fin opsp = opsp - 1 return opstack[opsp + 1] end def tos_op if opsp < 0 return 0 fin return opstack[opsp] end def tos_op_prec(tos) if opsp <= tos return 100 fin return precstack[opsp] end ; ; Symbol table ; deft idmatch(nameptr, len, idptr, idcnt) byte i while idcnt if len == (idptr).idname for i = 1 to len if (nameptr).[i - 1] <> (idptr).idname.[i] break fin next if i > len return idptr fin fin idptr = idptr + (idptr).idname + idrecsz idcnt = idcnt - 1 loop return 0 end def dumpsym(idptr, idcnt) while idcnt prword((idptr):idval) cout(' ') prbyte((idptr).idtype) cout(' ') prstr(@(idptr).idname) cout('=') if (idptr).idtype & ADDR_TYPE if (idptr):idval & is_ctag prword(ctag_value:[(idptr):idval & mask_ctag]) else prword((idptr):idval + codebuff) fin else prword((idptr):idval) fin crout idptr = idptr + (idptr).idname + idrecsz idcnt = idcnt - 1 loop end def id_lookup(nameptr, len) word idptr idptr = idmatch(nameptr, len, idlocal_tbl, locals) if idptr return idptr fin idptr = idmatch(nameptr, len, idglobal_tbl, globals) if idptr return idptr fin return parse_err(@undecl_id) end def idglobal_lookup(nameptr, len) return idmatch(nameptr, len, idglobal_tbl, globals) end def idlocal_add(namestr, len, type, size) if idmatch(namestr, len, @idlocal_tbl, locals) return parse_err(@dup_id) fin (lastlocal):idval = framesize (lastlocal).idtype = type ? LOCAL_TYPE nametostr(namestr, len, lastlocal + idname) locals = locals + 1 lastlocal = lastlocal + idrecsz + len if lastlocal > idlocal_tbl + idlocal_tblsz prstr(@local_sym_overflw) exit fin framesize = framesize + size if framesize > 255 prstr(@local_overflw) return FALSE fin return TRUE end def iddata_add(namestr, len, type, size) if idmatch(namestr, len, idglobal_tbl, globals) return parse_err(@dup_id) fin (lastglobal):idval = datasize (lastglobal).idtype = type nametostr(namestr, len, lastglobal + idname) emit_iddata(datasize, size, lastglobal + idname) globals = globals + 1 lastglobal = lastglobal + idrecsz + len if lastglobal > idglobal_tbl + idglobal_tblsz prstr(@global_sym_overflw) exit fin datasize = datasize + size return TRUE end def iddata_size(type, varsize, initsize) if varsize > initsize datasize = datasize + emit_data(0, 0, 0, varsize - initsize) else datasize = datasize + initsize fin ; if datasize <> codeptr - codebuff ; prstr(@emiterr) ; keyin() ; fin end def idglobal_add(namestr, len, type, value) if idmatch(namestr, len, idglobal_tbl, globals) return parse_err(@dup_id) fin (lastglobal):idval = value (lastglobal).idtype = type nametostr(namestr, len, lastglobal + idname) globals = globals + 1 lastglobal = lastglobal + idrecsz + len if lastglobal > idglobal_tbl + idglobal_tblsz prstr(@global_sym_overflw) exit fin return TRUE end def idfunc_add(namestr, len, tag) return idglobal_add(namestr, len, FUNC_TYPE, tag) end def idconst_add(namestr, len, value) return idglobal_add(namestr, len, CONST_TYPE, value) end def idglobal_init word ctag lineno = 0 parserr = 0 codeptr = codebuff lastop = $FF entrypoint = 0 datasize = 0 globals = 0 lastglobal = idglobal_tbl codetag = -1 ctag = ctag_new idfunc_add(@runtime0 + 1, runtime0, ctag) idfunc_add(@RUNTIME0 + 1, RUNTIME0, ctag) ctag_resolve(ctag, @romcall) ctag = ctag_new idfunc_add(@runtime1 + 1, runtime1, ctag) idfunc_add(@RUNTIME1 + 1, RUNTIME1, ctag) ctag_resolve(ctag, @syscall) ctag = ctag_new idfunc_add(@runtime2 + 1, runtime2, ctag) idfunc_add(@RUNTIME2 + 1, RUNTIME2, ctag) ctag_resolve(ctag, @memset) ctag = ctag_new idfunc_add(@runtime3 + 1, runtime3, ctag) idfunc_add(@RUNTIME3 + 1, RUNTIME3, ctag) ctag_resolve(ctag, @memcpy) ctag = ctag_new idfunc_add(@runtime4 + 1, runtime4, ctag) idfunc_add(@RUNTIME4 + 1, RUNTIME4, ctag) ctag_resolve(ctag, @cout) ctag = ctag_new idfunc_add(@runtime5 + 1, runtime5, ctag) idfunc_add(@RUNTIME5 + 1, RUNTIME5, ctag) ctag_resolve(ctag, @cin) ctag = ctag_new idfunc_add(@runtime6 + 1, runtime6, ctag) idfunc_add(@RUNTIME6 + 1, RUNTIME6, ctag) ctag_resolve(ctag, @prstr) ctag = ctag_new idfunc_add(@runtime7 + 1, runtime7, ctag) idfunc_add(@RUNTIME7 + 1, RUNTIME7, ctag) ctag_resolve(ctag, @rdstr) end def idlocal_init locals = 0 framesize = 2 lastlocal = idlocal_tbl end ; ; Parser ; def parse_term when scan is ID_TKN return TRUE is INT_TKN return TRUE is CHR_TKN return TRUE is STR_TKN return TRUE is OPEN_PAREN_TKN if !parse_expr return FALSE fin if token <> CLOSE_PAREN_TKN return parse_err(@no_close_paren) fin return TRUE wend return FALSE end def parse_constval(valptr, sizeptr) byte mod, type word idptr mod = 0 type = 0 *valptr = 0 while !parse_term when token is SUB_TKN mod = mod ? 1 is COMP_TKN mod = mod ? 2 is LOGIC_NOT_TKN mod = mod ? 4 is AT_TKN mod = mod ? 8 otherwise return 0 wend loop when token is STR_TKN *valptr = constval ^sizeptr = tknlen - 1 type = STR_TYPE if mod return parse_err(@bad_op) fin is CHR_TKN *valptr = constval ^sizeptr = 1 type = BYTE_TYPE is INT_TKN *valptr = constval ^sizeptr = 2 type = WORD_TYPE is ID_TKN ^sizeptr = 2 idptr = id_lookup(tknptr, tknlen) if !idptr return parse_err(@bad_cnst) fin type = (idptr).idtype *valptr = (idptr):idval if type & VAR_TYPE and !(mod & 8) return parse_err(@bad_cnst) fin otherwise return parse_err(@bad_cnst) wend if mod & 1 *valptr = -*valptr fin if mod & 2 *valptr = #*valptr fin if mod & 4 *valptr = !*valptr fin return type end deft ispostop scan when token is OPEN_PAREN_TKN return TRUE is OPEN_BRACKET_TKN return TRUE is DOT_TKN return TRUE is COLON_TKN return TRUE wend return FALSE end def parse_value(rvalue) byte cparams, deref, type, emit_val word optos, idptr, value byte elem_type, elem_size word elem_offset deref = rvalue optos = opsp type = 0 emit_val = FALSE value = 0 ; ; Parse pre-ops ; while !parse_term when token is ADD_TKN is BPTR_TKN if deref push_op(token, 0) else type = type ? BPTR_TYPE deref = deref + 1 fin is WPTR_TKN if deref push_op(token, 0) else type = type ? WPTR_TYPE deref = deref + 1 fin is AT_TKN deref = deref - 1 is SUB_TKN push_op(token, 0) is COMP_TKN push_op(token, 0) is LOGIC_NOT_TKN push_op(token, 0) otherwise return 0 wend loop ; ; Determine terminal type ; when token is INT_TKN type = type ? CONST_TYPE value = constval is CHR_TKN type = type ? CONST_TYPE value = constval is ID_TKN idptr = id_lookup(tknptr, tknlen) if !idptr return 0 fin if !(idptr).idtype return 0 fin type = type ? (idptr).idtype value = (idptr):idval is CLOSE_PAREN_TKN type = type ? WORD_TYPE emit_val = TRUE otherwise return 0 wend ; ; Constant optimizations ; if type & CONST_TYPE cparams = TRUE while optos < opsp and cparams when tos_op is NEG_TKN pop_op value = -value is COMP_TKN pop_op value = #value is LOGIC_NOT_TKN pop_op value = !value otherwise cparams = FALSE wend loop fin ; ; Parse post-ops ; while ispostop if token == OPEN_BRACKET_TKN ; ; Array ; if !emit_val if type & ADDR_TYPE if type & LOCAL_TYPE emit_localaddr(value) else emit_globaladdr(value) fin elsif type & CONST_TYPE emit_const(value) fin emit_val = TRUE fin ; !emit_val if type & PTR_TYPE emit_lw fin if !parse_expr return 0 fin if token <> CLOSE_BRACKET_TKN return parse_err(@no_close_bracket) fin if type & WORD_TYPE type = WPTR_TYPE emit_indexword else type = BPTR_TYPE emit_indexbyte fin elsif token == DOT_TKN or token == COLON_TKN ; ; Dot and Colon ; if token == DOT_TKN elem_type = BPTR_TYPE else elem_type = WPTR_TYPE fin if parse_constval(@elem_offset, @elem_size) ; ; Constant structure offset ; if !emit_val if type & VAR_TYPE if type & LOCAL_TYPE emit_localaddr(value + elem_offset) else ; emit_globaladdr(value + elem_offset) emit_globaladdr(value) emit_const(elem_offset) emit_binaryop(ADD_TKN) fin elsif type & CONST_TYPE value = value + elem_offset emit_const(value) else ; FUNC_TYPE emit_globaladdr(value) emit_const(elem_offset) emit_binaryop(ADD_TKN) fin emit_val = TRUE else if elem_offset <> 0 emit_const(elem_offset) emit_binaryop(ADD_TKN) fin fin ; !emit_val elsif token == OPEN_BRACKET_TKN ; ; Array of arrays ; if !emit_val if type & ADDR_TYPE if type & LOCAL_TYPE emit_localaddr(value) else emit_globaladdr(value) fin elsif type & CONST_TYPE emit_const(value) fin emit_val = TRUE fin ; !emit_val while parse_expr if token <> COMMA_TKN break fin emit_indexword emit_lw loop if token <> CLOSE_BRACKET_TKN return parse_err(@no_close_bracket) fin if elem_type & WPTR_TYPE emit_indexword else emit_indexbyte fin else return parse_err(@bad_offset) fin type = elem_type elsif token == OPEN_PAREN_TKN ; ; Function call ; if !emit_val and type & VAR_TYPE if type & LOCAL_TYPE emit_localaddr(value) else emit_globaladdr(value) fin fin if !(type & FUNC_CONST_TYPE) emit_push fin cparams = 0 while parse_expr cparams = cparams + 1 if token <> COMMA_TKN break fin loop if token <> CLOSE_PAREN_TKN return parse_err(@no_close_paren) fin if type & FUNC_CONST_TYPE emit_call(value, cparams) else emit_pull emit_ical(cparams) fin emit_val = TRUE type = WORD_TYPE fin loop if emit_val if rvalue if deref and type & PTR_TYPE if type & BPTR_TYPE emit_lb else emit_lw fin fin fin else ; emit_val if type & CONST_TYPE emit_const(value) elsif deref if type & FUNC_TYPE emit_call(value, 0) elsif type & VAR_TYPE if type & LOCAL_TYPE if type & BYTE_TYPE emit_llb(value) else emit_llw(value) fin else if type & BYTE_TYPE emit_lab(value) else emit_law(value) fin fin elsif type & PTR_TYPE if type & BPTR_TYPE emit_lb else emit_lw fin fin else if type & LOCAL_TYPE emit_localaddr(value) else emit_globaladdr(value) fin fin fin ; emit_val while optos < opsp if !emit_unaryop(pop_op) return parse_err(@bad_op) fin loop return type end def parse_constexpr(valptr, sizeptr) byte type, size1, size2 word val1, val2 type = parse_constval(@val1, @size1) if !type return 0 fin size2 = 0 when scan is ADD_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 + val2 is SUB_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 - val2 is MUL_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 * val2 is DIV_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 + val2 is MOD_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 % val2 is AND_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 & val2 is OR_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 ? val2 is EOR_TKN type = parse_constval(@val2, @size2) if !type return 0 fin *valptr = val1 ^ val2 otherwise *valptr = val1 wend if size1 > size2 ^sizeptr = size1 else ^sizeptr = size2 fin return type end def parse_expr byte prevmatch, matchop, i word optos matchop = 0 optos = opsp repeat prevmatch = matchop matchop = 0 if parse_value(1) matchop = 1 for i = 0 to bops_tblsz if token == bops_tbl[i] matchop = 2 if bops_prec[i] >= tos_op_prec(optos) if !emit_binaryop(pop_op) return parse_err(@bad_op) fin fin push_op(token, bops_prec[i]) break fin next fin until matchop <> 2 if matchop == 0 and prevmatch == 2 return parse_err(@missing_op) fin while optos < opsp if !emit_binaryop(pop_op) return parse_err(@bad_op) fin loop return matchop or prevmatch end def parse_stmnt byte type, i word tag_prevbrk, tag_else, tag_endif, tag_while, tag_wend word tag_repeat, tag_for, tag_choice, idptr, saveptr, addr, stepdir if token <> END_TKN and token <> DONE_TKN prevstmnt = token fin when token is IF_TKN if !parse_expr return 0 fin tag_else = ctag_new tag_endif = ctag_new emit_brfls(tag_else) scan repeat while parse_stmnt nextln loop if token <> ELSEIF_TKN break fin emit_jump(tag_endif) emit_codetag(tag_else) if !parse_expr return 0 fin tag_else = ctag_new emit_brfls(tag_else) until FALSE if token == ELSE_TKN emit_jump(tag_endif) emit_codetag(tag_else) scan while parse_stmnt nextln loop emit_codetag(tag_endif) else emit_codetag(tag_else) emit_codetag(tag_endif) fin if token <> FIN_TKN return parse_err(@no_fin) fin is FOR_TKN stack_loop = stack_loop + 1 tag_for = ctag_new tag_prevbrk = break_tag break_tag = ctag_new if scan <> ID_TKN return parse_err(@bad_stmnt) fin idptr = id_lookup(tknptr, tknlen) if idptr type = (idptr).idtype addr = (idptr):idval else return FALSE fin if scan <> SET_TKN return parse_err(@bad_stmnt) fin if !parse_expr return parse_err(@bad_stmnt) fin emit_codetag(tag_for) if type & LOCAL_TYPE if type & BYTE_TYPE emit_dlb(addr) else emit_dlw(addr) fin else if type & BYTE_TYPE emit_dab(addr) else emit_daw(addr) fin fin if token == TO_TKN stepdir = 1 elsif token == DOWNTO_TKN stepdir = -1 else return parse_err(@bad_stmnt) fin if !parse_expr return parse_err(@bad_stmnt) fin if stepdir > 0 emit_brgt(break_tag) else emit_brlt(break_tag) fin if token == STEP_TKN if !parse_expr return parse_err(@bad_stmnt) fin if stepdir > 0 emit_binaryop(ADD_TKN) else emit_binaryop(SUB_TKN) fin else if stepdir > 0 emit_unaryop(INC_TKN) else emit_unaryop(DEC_TKN) fin fin while parse_stmnt nextln loop if token <> NEXT_TKN return parse_err(@bad_stmnt) fin emit_jump(tag_for) emit_codetag(break_tag) emit_drop break_tag = tag_prevbrk stack_loop = stack_loop - 1 is WHILE_TKN tag_while = ctag_new tag_wend = ctag_new tag_prevbrk = break_tag break_tag = tag_wend emit_codetag(tag_while) if !parse_expr return 0 fin emit_brfls(tag_wend) while parse_stmnt nextln loop if token <> LOOP_TKN return parse_err(@no_loop) fin emit_jump(tag_while) emit_codetag(tag_wend) break_tag = tag_prevbrk is REPEAT_TKN tag_repeat = ctag_new tag_prevbrk = break_tag break_tag = ctag_new emit_codetag(tag_repeat) scan while parse_stmnt nextln loop if token <> UNTIL_TKN return parse_err(@no_until) fin if !parse_expr return 0 fin emit_brfls(tag_repeat) emit_codetag(break_tag) break_tag = tag_prevbrk is CASE_TKN stack_loop = stack_loop + 1 tag_choice = ctag_new tag_prevbrk = break_tag break_tag = ctag_new if !parse_expr return parse_err(@bad_stmnt) fin nextln while token <> ENDCASE_TKN when token is OF_TKN if !parse_expr return parse_err(@bad_stmnt) fin emit_brne(tag_choice) while parse_stmnt nextln loop emit_jump(break_tag) emit_codetag(tag_choice) tag_choice = ctag_new is DEFAULT_TKN scan while parse_stmnt nextln loop if token <> ENDCASE_TKN return parse_err(@bad_stmnt) fin otherwise return parse_err(@bad_stmnt) wend loop emit_codetag(break_tag) emit_drop break_tag = tag_prevbrk stack_loop = stack_loop - 1 is BREAK_TKN if break_tag emit_jump(break_tag) else return parse_err(@bad_stmnt) fin is RETURN_TKN if infunc for i = 1 to stack_loop emit_drop next if !parse_expr emit_const(0) fin emit_leave(framesize) else return parse_err(@bad_stmnt) fin is EXIT_TKN if !parse_expr emit_const(0) fin emit_exit is ELSE_TKN return FALSE is ELSEIF_TKN return FALSE is FIN_TKN return FALSE is LOOP_TKN return FALSE is UNTIL_TKN return FALSE is NEXT_TKN return FALSE is OF_TKN return FALSE is DEFAULT_TKN return FALSE is ENDCASE_TKN return FALSE is END_TKN return FALSE is DONE_TKN return FALSE is IFUNC_TKN return FALSE is TFUNC_TKN return FALSE is NFUNC_TKN return FALSE is EOF_TKN return FALSE is EOL_TKN return TRUE otherwise if token == ID_TKN saveptr = tknptr idptr = id_lookup(tknptr, tknlen) if !idptr return FALSE fin type = (idptr).idtype if type & ADDR_TYPE addr = (idptr):idval if scan == SET_TKN if type & VAR_TYPE if !parse_expr return parse_err(@bad_expr) fin if type & LOCAL_TYPE if type & BYTE_TYPE emit_slb(addr) else emit_slw(addr) fin else if type & BYTE_TYPE emit_sab(addr) else emit_saw(addr) fin fin return TRUE fin elsif token == EOL_TKN and type & FUNC_TYPE emit_call(addr, 0) emit_drop return TRUE fin fin tknptr = saveptr fin rewind(tknptr) type = parse_value(0) if type if token == SET_TKN if !parse_expr return parse_err(@bad_expr) fin if type & XBYTE_TYPE emit_sb else emit_sw fin else if type & BPTR_TYPE emit_lb elsif type & WPTR_TYPE emit_lw fin emit_drop fin else return parse_err(@bad_syntax) fin wend if scan <> EOL_TKN return parse_err(@bad_syntax) fin return TRUE end def parse_var(type) byte consttype, constsize, idlen word idptr, constval, arraysize, size idlen = 0 size = 1 if scan == ID_TKN idptr = tknptr idlen = tknlen if scan == OPEN_BRACKET_TKN size = 0 parse_constexpr(@size, @constsize) if token <> CLOSE_BRACKET_TKN return parse_err(@no_close_bracket) fin scan fin fin if type == WORD_TYPE size = size * 2 fin if token == SET_TKN if infunc return parse_err(@no_local_init) fin if idlen iddata_add(idptr, idlen, type, 0) fin consttype = parse_constexpr(@constval, @constsize) if consttype arraysize = emit_data(type, consttype, constval, constsize) while token == COMMA_TKN consttype = parse_constexpr(@constval, @constsize) if consttype arraysize = arraysize + emit_data(type, consttype, constval, constsize) else return parse_err(@bad_decl) fin loop if token <> EOL_TKN return parse_err(@no_close_bracket) fin iddata_size(PTR_TYPE, size, arraysize); else return parse_err(@bad_decl) fin elsif idlen if infunc idlocal_add(idptr, idlen, type, size) else iddata_add(idptr, idlen, type, size) fin fin return TRUE end def parse_vars byte idlen, type, size word value, idptr when token is CONST_TKN if scan <> ID_TKN return parse_err(@bad_cnst) fin idptr = tknptr; idlen = tknlen if scan <> SET_TKN return parse_err(@bad_cnst) fin if !parse_constexpr(@value, @size) return parse_err(@bad_cnst) fin idconst_add(idptr, idlen, value) is BYTE_TKN type = BYTE_TYPE repeat if !parse_var(type) return FALSE fin until token <> COMMA_TKN is WORD_TKN type = WORD_TYPE repeat if !parse_var(type) return FALSE fin until token <> COMMA_TKN is FUNC_TKN repeat if scan == ID_TKN idfunc_add(tknptr, tknlen, ctag_new) else return parse_err(@bad_decl) fin until scan <> COMMA_TKN is EOL_TKN return TRUE otherwise return FALSE wend return TRUE end def parse_func byte defopt, cfnparms word func_tag, idptr if token == IFUNC_TKN or token == TFUNC_TKN or token == NFUNC_TKN defopt = token - IFUNC_TKN if scan <> ID_TKN return parse_err(@bad_decl) fin cfnparms = 0 infunc = TRUE idptr = idglobal_lookup(tknptr, tknlen) if idptr func_tag = (idptr):idval else func_tag = ctag_new idfunc_add(tknptr, tknlen, func_tag) fin emit_codetag(func_tag) retfunc_tag = ctag_new idlocal_init if scan == OPEN_PAREN_TKN repeat if scan == ID_TKN cfnparms = cfnparms + 1 idlocal_add(tknptr, tknlen, WORD_TYPE, 2) scan fin until token <> COMMA_TKN if token <> CLOSE_PAREN_TKN return parse_err(@bad_decl) fin scan fin while parse_vars nextln loop emit_enter(framesize, cfnparms) prevstmnt = 0 while parse_stmnt nextln loop infunc = FALSE if token <> END_TKN return parse_err(@bad_syntax) fin if scan <> EOL_TKN return parse_err(@bad_syntax) fin if prevstmnt <> RETURN_TKN emit_const(0) emit_leave(framesize) fin return TRUE elsif token == EOL_TKN return TRUE fin return FALSE end def parse_module entrypoint = 0 idglobal_init idlocal_init if nextln while parse_vars nextln loop while parse_func nextln loop if token <> DONE_TKN emit_start prevstmnt = 0 while parse_stmnt nextln loop if token <> DONE_TKN parse_err(@no_done) fin if prevstmnt <> EXIT_TKN emit_const(0) emit_exit fin fin ; dumpsym(idglobal_tbl, globals) ; prstr(@entrypt_str) ; prword(entrypoint) ; crout ; keyin() return TRUE fin return FALSE end ; ; Init editor ; if !(^machid & $80) flags = uppercase ? shiftlock keyin = @keyin2 else keyin = @keyin2e fin inittxtbuf if ^argbuff strcpy(argbuff, @txtfile) prstr(@txtfile) readtxt(@txtfile) else numlines = 1 fin curschr = '+' flags = flags ? insmode drawscrn(scrntop, scrnleft) curson editmode done