mirror of
https://github.com/dschmenk/PLASMA.git
synced 2024-06-01 19:41:36 +00:00
4135 lines
102 KiB
Plaintext
4135 lines
102 KiB
Plaintext
//
|
|
// Global constants
|
|
//
|
|
const FALSE = 0
|
|
const TRUE = !FALSE
|
|
//
|
|
// 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 keyctrlf = $86
|
|
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 keyctrly = $99
|
|
const keyctrlz = $9A
|
|
const keydelete = $FF
|
|
const getbuff = $01FF
|
|
//
|
|
// 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 = $80 // = memory@16 bytes per bit map, 128 bytes per 8 bit map, 1K bytes per 8 byte map
|
|
const maxlnlen = 79
|
|
const strheap = $6900
|
|
const strheasz = $4000
|
|
const codebuff = $A900
|
|
const codebuffsz = $1000
|
|
const pgjmp = 16
|
|
const changed = 1
|
|
const insmode = 2
|
|
const showcurs = 4
|
|
const uppercase = 8
|
|
const shiftlock = 128
|
|
//
|
|
// Argument buffer (must be first declared variables)
|
|
//
|
|
word signature = $EEEE // buffer signature
|
|
byte = 32 // buffer length
|
|
byte[32] argbuff = "" // buffer
|
|
//
|
|
// Text screen row address array
|
|
//
|
|
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
|
|
//
|
|
// Editor variables
|
|
//
|
|
byte nullstr = ""
|
|
byte version = "PLASMA ][ SANDBOX VERSION 00.96"
|
|
byte errorstr = "ERROR: $"
|
|
byte okstr = "OK"
|
|
byte outofmem = "OUT OF MEMORY!"
|
|
byte losechng = "LOSE CHANGES TO FILE (Y/N)?"
|
|
byte untitled = "UNTITLED.PLA"
|
|
byte[64] txtfile = "UNTITLED.PLA"
|
|
byte flags = 0
|
|
byte flash = 0
|
|
word numlines = 0
|
|
word cutbuf = 0
|
|
byte perr, cursx, cursy, scrnleft, curscol, underchr, curschr
|
|
word keyin, cursrow, scrntop, cursptr
|
|
//
|
|
// Predeclared functions
|
|
//
|
|
predef cmdmode(clrscrn)#0
|
|
//
|
|
// 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 = $FC // |
|
|
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 ALT_COMP_TKN = $FE // ~
|
|
const LOGIC_NOT_TKN = $A1 // !
|
|
const BPTR_TKN = $DE // ^
|
|
const WPTR_TKN = $AA // *
|
|
const PTRB_TKN = $D8 // X
|
|
const PTRW_TKN = $D7 // W
|
|
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 DEF_TKN = $95
|
|
const STRUC_TKN = $96
|
|
const DONE_TKN = $98
|
|
const RETURN_TKN = $99
|
|
const BREAK_TKN = $9A
|
|
const CONT_TKN = $9B
|
|
const EXIT_TKN = $9C
|
|
const EVAL_TKN = $9D
|
|
const PREDEF_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 CONSTADDR_TYPE = $61 // (CONST_TYPE | PTR_TYPE)
|
|
const STR_TYPE = $80
|
|
//
|
|
// Keywords
|
|
//
|
|
byte keywrds = "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", DEF_TKN
|
|
byte = "END", END_TKN
|
|
byte = "AND", LOGIC_AND_TKN
|
|
byte = "NOT", LOGIC_NOT_TKN
|
|
byte = "BYTE", BYTE_TKN
|
|
byte = "WORD", WORD_TKN
|
|
byte = "ELSE", ELSE_TKN
|
|
byte = "NEXT", NEXT_TKN
|
|
byte = "WHEN", CASE_TKN
|
|
byte = "LOOP", LOOP_TKN
|
|
byte = "STEP", STEP_TKN
|
|
byte = "DONE", DONE_TKN
|
|
byte = "WEND", ENDCASE_TKN
|
|
byte = "CONST", CONST_TKN
|
|
byte = "STRUC", STRUC_TKN
|
|
byte = "ELSIF", ELSEIF_TKN
|
|
byte = "WHILE", WHILE_TKN
|
|
byte = "UNTIL", UNTIL_TKN
|
|
byte = "BREAK", BREAK_TKN
|
|
byte = "DOWNTO", DOWNTO_TKN
|
|
byte = "REPEAT", REPEAT_TKN
|
|
byte = "RETURN", RETURN_TKN
|
|
byte = "PREDEF", PREDEF_TKN
|
|
byte = "CONTINUE", CONT_TKN
|
|
byte = "OTHERWISE",DEFAULT_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[16] opstack
|
|
byte[16] precstack
|
|
word opsp = -1
|
|
word[16] valstack
|
|
byte[16] sizestack
|
|
byte[16] typestack
|
|
word valsp = -1
|
|
//
|
|
// Symbol table variables
|
|
//
|
|
const idglobal_tblsz = 2048
|
|
const idlocal_tblsz = 512
|
|
const idglobal_tbl = $1600
|
|
const idlocal_tbl = $1E00
|
|
const ctag_max = 1024
|
|
const ctag_tbl = $800
|
|
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 IS_RESOLVED = $8000
|
|
const IS_RELATIVE = $8000
|
|
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
|
|
word scanptr = @nullstr
|
|
byte scanchr, token, tknlen
|
|
byte parserrpos, parserr = 0
|
|
word tknptr, parserrln
|
|
word constval
|
|
word lineno = 0
|
|
//
|
|
// Compiler output messages
|
|
//
|
|
byte bytes_compiled_str[] = "\nBYTES COMPILED: "
|
|
byte dup_id[] = "DUPLICATE IDENTIFIER"
|
|
byte undecl_id[] = "UNDECLARED IDENTIFIER"
|
|
byte bad_cnst[] = "BAD CONSTANT"
|
|
byte bad_struc[] = "BAD STRUCTURE"
|
|
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_ctag_offst[] = "CODE OFFSET NOT SUPPORTED"
|
|
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[] = "call"
|
|
byte RUNTIME0[] = "CALL"
|
|
byte runtime1[] = "syscall"
|
|
byte RUNTIME1[] = "SYSCALL"
|
|
byte runtime2[] = "memset"
|
|
byte RUNTIME2[] = "MEMSET"
|
|
byte runtime3[] = "memcpy"
|
|
byte RUNTIME3[] = "MEMCPY"
|
|
byte runtime4[] = "putc"
|
|
byte RUNTIME4[] = "PUTC"
|
|
byte runtime5[] = "getc"
|
|
byte RUNTIME5[] = "GETC"
|
|
byte runtime6[] = "puts"
|
|
byte RUNTIME6[] = "PUTS"
|
|
byte runtime7[] = "gets"
|
|
byte RUNTIME7[] = "GETS"
|
|
byte runtime8[] = "puti"
|
|
byte RUNTIME8[] = "PUTI"
|
|
byte runtime9[] = "home"
|
|
byte RUNTIME9[] = "HOME"
|
|
byte runtime10[] = "gotoxy"
|
|
byte RUNTIME10[] = "GOTOXY"
|
|
//
|
|
// Parser variables
|
|
//
|
|
byte infunc = 0
|
|
byte stack_loop = 0
|
|
byte prevstmnt = 0
|
|
word retfunc_tag = 0
|
|
word break_tag = 0
|
|
word cont_tag = 0
|
|
predef parse_constexpr(str,val), parse_expr, parse_module
|
|
//
|
|
// ASM utility functions
|
|
//
|
|
// Defines for ASM routines
|
|
//
|
|
asm equates
|
|
INTERP = $03D0
|
|
LCRDEN = $C080
|
|
LCWTEN = $C081
|
|
ROMEN = $C082
|
|
LCRWEN = $C083
|
|
LCBNK2 = $00
|
|
LCBNK1 = $08
|
|
!SOURCE "vmsrc/plvmzp.inc"
|
|
end
|
|
//
|
|
// SAVE VM STATE
|
|
//
|
|
asm save_vmstate
|
|
LDA $03F2
|
|
STA VMRESET
|
|
LDA $03F3
|
|
STA VMRESET+1
|
|
LDA $03F4
|
|
STA VMRESET+2
|
|
LDA #<RESETENTRY
|
|
STA $03F2
|
|
LDA #>RESETENTRY
|
|
STA $03F3
|
|
EOR #$A5
|
|
STA $03F4
|
|
DEX
|
|
RTS
|
|
end
|
|
//
|
|
// RESTORE VM STATE
|
|
//
|
|
asm restore_vmstate
|
|
RESETENTRY
|
|
LDA VMRESET
|
|
STA $03F2
|
|
LDA VMRESET+1
|
|
STA $03F3
|
|
LDA VMRESET+2
|
|
STA $03F4
|
|
LDX #$00
|
|
STX IFPL
|
|
LDA #$BF
|
|
STA IFPH
|
|
LDX #$FE
|
|
TXS
|
|
LDX #ESTKSZ/2
|
|
BIT ROMEN
|
|
JMP $2000
|
|
VMRESET !FILL 3
|
|
end
|
|
//
|
|
// CALL 6502 ROUTINE
|
|
// CALL(ADDR, AREG, XREG, YREG, STATUS)
|
|
//
|
|
asm call(addr, areg, xreg, yreg, status)
|
|
REGVALS = SRC
|
|
PHP
|
|
LDA ESTKL+4,X
|
|
STA TMPL
|
|
LDA ESTKH+4,X
|
|
STA TMPH
|
|
LDA ESTKL,X
|
|
PHA
|
|
LDA ESTKL+1,X
|
|
TAY
|
|
LDA ESTKL+3,X
|
|
PHA
|
|
LDA ESTKL+2,X
|
|
INX
|
|
INX
|
|
INX
|
|
INX
|
|
STX ESP
|
|
TAX
|
|
PLA
|
|
BIT ROMEN
|
|
PLP
|
|
JSR JMPTMP
|
|
PHP
|
|
BIT LCRDEN+LCBNK2
|
|
STA REGVALS+0
|
|
STX REGVALS+1
|
|
STY REGVALS+2
|
|
PLA
|
|
STA REGVALS+3
|
|
LDX ESP
|
|
LDA #<REGVALS
|
|
LDY #>REGVALS
|
|
STA ESTKL,X
|
|
STY ESTKH,X
|
|
PLP
|
|
RTS
|
|
JMPTMP JMP (TMP)
|
|
end
|
|
//
|
|
// CALL PRODOS
|
|
// SYSCALL(CMD, PARAMS)
|
|
//
|
|
asm syscall(cmd, params)
|
|
LDA ESTKL,X
|
|
LDY ESTKH,X
|
|
STA PARAMS
|
|
STY PARAMS+1
|
|
INX
|
|
LDA ESTKL,X
|
|
STA CMD
|
|
JSR $BF00
|
|
CMD: !BYTE 00
|
|
PARAMS: !WORD 0000
|
|
LDY #$00
|
|
STA ESTKL,X
|
|
STY ESTKH,X
|
|
RTS
|
|
end
|
|
// SET MEMORY TO VALUE
|
|
// MEMSET(ADDR, VALUE, SIZE)
|
|
// With optimizations from Peter Ferrie
|
|
//
|
|
asm memset(addr, val, size)
|
|
LDA ESTKL+2,X
|
|
STA DSTL
|
|
LDA ESTKH+2,X
|
|
STA DSTH
|
|
LDY ESTKL,X
|
|
BEQ +
|
|
INC ESTKH,X
|
|
LDY #$00
|
|
+ LDA ESTKH,X
|
|
BEQ SETMEX
|
|
SETMLPL CLC
|
|
LDA ESTKL+1,X
|
|
SETMLPH STA (DST),Y
|
|
DEC ESTKL,X
|
|
BNE +
|
|
DEC ESTKH,X
|
|
BEQ SETMEX
|
|
+ INY
|
|
BNE +
|
|
INC DSTH
|
|
+ BCS SETMLPL
|
|
SEC
|
|
LDA ESTKH+1,X
|
|
BCS SETMLPH
|
|
SETMEX INX
|
|
INX
|
|
RTS
|
|
end
|
|
//
|
|
// COPY MEMORY
|
|
// MEMCPY(DSTADDR, SRCADDR, SIZE)
|
|
//
|
|
asm memcpy(dst, src, size)
|
|
INX
|
|
INX
|
|
LDA ESTKL-2,X
|
|
ORA ESTKH-2,X
|
|
BEQ CPYMEX
|
|
LDA ESTKL-1,X
|
|
CMP ESTKL,X
|
|
LDA ESTKH-1,X
|
|
SBC ESTKH,X
|
|
BCC REVCPY
|
|
;
|
|
; FORWARD COPY
|
|
;
|
|
LDA ESTKL,X
|
|
STA DSTL
|
|
LDA ESTKH,X
|
|
STA DSTH
|
|
LDA ESTKL-1,X
|
|
STA SRCL
|
|
LDA ESTKH-1,X
|
|
STA SRCH
|
|
LDY ESTKL-2,X
|
|
BEQ FORCPYLP
|
|
INC ESTKH-2,X
|
|
LDY #$00
|
|
FORCPYLP LDA (SRC),Y
|
|
STA (DST),Y
|
|
INY
|
|
BNE +
|
|
INC DSTH
|
|
INC SRCH
|
|
+ DEC ESTKL-2,X
|
|
BNE FORCPYLP
|
|
DEC ESTKH-2,X
|
|
BNE FORCPYLP
|
|
RTS
|
|
;
|
|
; REVERSE COPY
|
|
;
|
|
REVCPY ;CLC
|
|
LDA ESTKL-2,X
|
|
ADC ESTKL,X
|
|
STA DSTL
|
|
LDA ESTKH-2,X
|
|
ADC ESTKH,X
|
|
STA DSTH
|
|
CLC
|
|
LDA ESTKL-2,X
|
|
ADC ESTKL-1,X
|
|
STA SRCL
|
|
LDA ESTKH-2,X
|
|
ADC ESTKH-1,X
|
|
STA SRCH
|
|
DEC DSTH
|
|
DEC SRCH
|
|
LDY #$FF
|
|
LDA ESTKL-2,X
|
|
BEQ REVCPYLP
|
|
INC ESTKH-2,X
|
|
REVCPYLP LDA (SRC),Y
|
|
STA (DST),Y
|
|
DEY
|
|
CPY #$FF
|
|
BNE +
|
|
DEC DSTH
|
|
DEC SRCH
|
|
+ DEC ESTKL-2,X
|
|
BNE REVCPYLP
|
|
DEC ESTKH-2,X
|
|
BNE REVCPYLP
|
|
CPYMEX RTS
|
|
end
|
|
//
|
|
// CHAR OUT
|
|
// COUT(CHAR)
|
|
//
|
|
asm cout(char)
|
|
LDA ESTKL,X
|
|
COUT1 BIT $BF98
|
|
BMI +
|
|
JSR TOUPR
|
|
+ ORA #$80
|
|
BIT ROMEN
|
|
JSR $FDED
|
|
BIT LCRDEN+LCBNK2
|
|
RTS
|
|
end
|
|
//
|
|
// CHAR IN
|
|
// RDKEY()
|
|
//
|
|
asm cin()
|
|
BIT ROMEN
|
|
JSR $FD0C
|
|
BIT LCRDEN+LCBNK2
|
|
DEX
|
|
LDY #$00
|
|
AND #$7F
|
|
STA ESTKL,X
|
|
STY ESTKH,X
|
|
RTS
|
|
end
|
|
//
|
|
// PRINT STRING
|
|
// PRSTR(STR)
|
|
//
|
|
asm prstr(pstr)
|
|
LDY #$00
|
|
LDA ESTKL,X
|
|
STA SRCL
|
|
LDA ESTKH,X
|
|
STA SRCH
|
|
LDA (SRC),Y
|
|
STA TMP
|
|
BEQ ++
|
|
- INY
|
|
LDA (SRC),Y
|
|
JSR COUT1
|
|
CPY TMP
|
|
BNE -
|
|
++ RTS
|
|
end
|
|
//
|
|
// READ STRING
|
|
// STR = RDSTR(PROMPTCHAR)
|
|
//
|
|
asm rdstr(prompt)
|
|
LDA ESTKL,X
|
|
STA $33
|
|
STX ESP
|
|
BIT ROMEN
|
|
JSR $FD6A
|
|
BIT LCRDEN+LCBNK2
|
|
STX $01FF
|
|
- LDA $01FF,X
|
|
AND #$7F
|
|
STA $01FF,X
|
|
DEX
|
|
BPL -
|
|
LDX ESP
|
|
LDA #$FF
|
|
STA ESTKL,X
|
|
LDA #$01
|
|
STA ESTKH,X
|
|
RTS
|
|
end
|
|
//
|
|
// EXIT
|
|
//
|
|
asm exit#0
|
|
JSR $BF00
|
|
!BYTE $65
|
|
!WORD EXITTBL
|
|
EXITTBL:
|
|
!BYTE 4
|
|
!BYTE 0
|
|
end
|
|
//def toupper_11(c)
|
|
// if c >= 'a'
|
|
// if c <= 'z'
|
|
// return c - $20
|
|
// fin
|
|
// fin
|
|
// return c
|
|
//end
|
|
asm toupper(char)
|
|
LDA ESTKL,X
|
|
TOUPR AND #$7F
|
|
CMP #'z'+1
|
|
BCS +
|
|
CMP #'a'
|
|
BCC +
|
|
SBC #$20
|
|
+ STA ESTKL,X
|
|
RTS
|
|
end
|
|
asm clrhibit(strptr)#0
|
|
LDA ESTKL,X
|
|
STA SRCL
|
|
LDA ESTKH,X
|
|
STA SRCH
|
|
INX
|
|
LDY #$00
|
|
LDA (SRC),Y
|
|
BEQ +
|
|
TAY
|
|
CLHILP LDA (SRC),Y
|
|
AND #$7F
|
|
STA (SRC),Y
|
|
DEY
|
|
BNE CLHILP
|
|
+ RTS
|
|
end
|
|
asm sethibit(strptr)#0
|
|
LDA ESTKL,X
|
|
STA SRCL
|
|
LDA ESTKH,X
|
|
STA SRCH
|
|
INX
|
|
LDY #$00
|
|
LDA (SRC),Y
|
|
BEQ +
|
|
TAY
|
|
STHILP LDA (SRC),Y
|
|
ORA #$80
|
|
STA (SRC),Y
|
|
DEY
|
|
BNE STHILP
|
|
+ RTS
|
|
end
|
|
asm cpyln(srcstr, dststr)#0
|
|
LDA ESTKL,X
|
|
STA DSTL
|
|
LDA ESTKH,X
|
|
STA DSTH
|
|
INX
|
|
LDA ESTKL,X
|
|
STA SRCL
|
|
LDA ESTKH,X
|
|
STA SRCH
|
|
INX
|
|
LDY #$00
|
|
LDA (SRC),Y
|
|
TAY
|
|
LDA #$00
|
|
INY
|
|
STA (DST),Y
|
|
DEY
|
|
BEQ ++
|
|
CPLNLP LDA (SRC),Y
|
|
CMP #$20
|
|
BCS +
|
|
ADC #$60
|
|
+ AND #$7F
|
|
STA (DST),Y
|
|
DEY
|
|
BNE CPLNLP
|
|
LDA (SRC),Y
|
|
++ STA (DST),Y
|
|
RTS
|
|
end
|
|
//
|
|
//def skipspace(scanptr)
|
|
// while ^scanptr == ' '
|
|
// scanptr = scanptr + 1
|
|
// loop
|
|
// return scanptr
|
|
//end
|
|
asm skipspace(scanptr)
|
|
LDA #$00
|
|
STA SRCL
|
|
LDA ESTKH,X
|
|
STA SRCH
|
|
LDY ESTKL,X
|
|
- LDA (SRC),Y
|
|
CMP #' '
|
|
BNE +
|
|
INY
|
|
BNE -
|
|
INC SRCH
|
|
BNE -
|
|
+ STY ESTKL,X
|
|
LDA SRCH
|
|
STA ESTKH,X
|
|
RTS
|
|
end
|
|
//def 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(char)
|
|
LDY #$00
|
|
LDA ESTKL,X
|
|
CMP #'_'
|
|
BEQ ISALTRU
|
|
CMP #'A'
|
|
BCC ISALRET
|
|
CMP #'Z'+1
|
|
BCC ISALTRU
|
|
CMP #'a'
|
|
BCC ISALRET
|
|
CMP #'z'+1
|
|
BCS ISALRET
|
|
ISALTRU DEY
|
|
ISALRET STY ESTKL,X
|
|
STY ESTKH,X
|
|
RTS
|
|
end
|
|
//def isnum(c)
|
|
// if c >= '0' and c <= '9'
|
|
// return TRUE
|
|
// fin
|
|
// return FALSE
|
|
//end
|
|
asm isnum(char)
|
|
LDY #$00
|
|
LDA ESTKL,X
|
|
CMP #'0'
|
|
BCC +
|
|
CMP #'9'+1
|
|
BCS +
|
|
DEY
|
|
+ STY ESTKL,X
|
|
STY ESTKH,X
|
|
RTS
|
|
end
|
|
//def 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(char)
|
|
LDY #$00
|
|
LDA ESTKL,X
|
|
CMP #'_'
|
|
BEQ ISANTRU
|
|
CMP #'0'
|
|
BCC ISANRET
|
|
CMP #'9'+1
|
|
BCC ISANTRU
|
|
CMP #'A'
|
|
BCC ISANRET
|
|
CMP #'Z'+1
|
|
BCC ISANTRU
|
|
CMP #'a'
|
|
BCC ISANRET
|
|
CMP #'z'+1
|
|
BCS ISANRET
|
|
ISANTRU DEY
|
|
ISANRET STY ESTKL,X
|
|
STY ESTKH,X
|
|
RTS
|
|
end
|
|
//
|
|
// Runtime routines
|
|
//
|
|
def home
|
|
return call($FC58, 0, 0, 0, 0)
|
|
end
|
|
def gotoxy(x, y)
|
|
^$24 = x + ^$20
|
|
return call($FB5B, y + ^$22, 0, 0, 0)
|
|
end
|
|
//
|
|
// 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
|
|
params.5 = 0
|
|
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[3]
|
|
|
|
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#0
|
|
cout($0D)
|
|
end
|
|
def bell#0
|
|
call($FBDD, 0, 0, 0, 0)
|
|
end
|
|
//
|
|
// Memory management routines
|
|
//
|
|
def strcpy(dststr, srcstr)#0
|
|
byte strlen
|
|
|
|
strlen = ^srcstr
|
|
while ^(srcstr + strlen) == $8D or ^(srcstr + strlen) == $A0
|
|
strlen = strlen - 1
|
|
loop
|
|
^dststr = strlen
|
|
memcpy(dststr + 1, srcstr + 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
|
|
return 0
|
|
end
|
|
def 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)
|
|
return 0
|
|
end
|
|
def freestr(strptr)#0
|
|
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(newptr, strptr, strlen + 1)
|
|
^newptr = strlen
|
|
return newptr
|
|
fin
|
|
return @nullstr
|
|
end
|
|
def inittxtbuf#0
|
|
memset(strheapmap, 0, strheapmsz)
|
|
memset(strlinbuf, @nullstr, maxfill * 2)
|
|
numlines = 1
|
|
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)#0
|
|
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)#0
|
|
byte i, chr
|
|
|
|
for i = ^strptr downto 1
|
|
chr = ^(strptr + i)
|
|
if chr & $E0 == $00
|
|
^(strptr + i) = chr + $E0
|
|
fin
|
|
next
|
|
end
|
|
def txtupper#0
|
|
word i, strptr
|
|
|
|
flags = flags | uppercase
|
|
for i = numlines - 1 downto 0
|
|
strupper(strlinbuf:[i])
|
|
next
|
|
end
|
|
def txtlower#0
|
|
word i, strptr
|
|
|
|
flags = flags & ~uppercase
|
|
for i = numlines - 1 downto 0
|
|
strlower(strlinbuf:[i])
|
|
next
|
|
end
|
|
def prbyte(h)#0
|
|
cout('$')
|
|
call($FDDA, h, 0, 0, 0)
|
|
end
|
|
def prword(h)#0
|
|
cout('$')
|
|
call($F941, h >> 8, h, 0, 0)
|
|
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--
|
|
loop
|
|
numstr[place] = i + '0'
|
|
place--
|
|
if sign
|
|
numstr[place] = '-'
|
|
place--
|
|
fin
|
|
numstr[place] = 6 - place
|
|
return prstr(@numstr[place])
|
|
end
|
|
def nametostr(namestr, len, strptr)#0
|
|
^strptr = len
|
|
memcpy(strptr + 1, namestr, len)
|
|
end
|
|
//
|
|
// File routines
|
|
//
|
|
def readtxt(filename)#0
|
|
byte txtbuf[81], refnum, i, j
|
|
|
|
refnum = open(filename, iobuffer)
|
|
if refnum and not perr
|
|
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
|
|
//
|
|
// Make sure there is a blank line at the end of the buffer
|
|
//
|
|
if numlines < maxlines and strlinbuf:[numlines - 1] <> @nullstr
|
|
strlinbuf:[numlines] = @nullstr
|
|
numlines++
|
|
fin
|
|
fin
|
|
if refnum; close(refnum); fin
|
|
end
|
|
def writetxt(filename)#0
|
|
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
|
|
fin
|
|
//
|
|
// Remove blank lines at end of text.
|
|
//
|
|
while numlines > 1 and strlinbuf:[numlines - 1] == @nullstr; numlines = numlines - 1; loop
|
|
//
|
|
// Write all the text line to the file.
|
|
//
|
|
for i = 0 to numlines - 1
|
|
cpyln(strlinbuf:[i], @txtbuf)
|
|
txtbuf++
|
|
txtbuf[txtbuf] = $0D
|
|
write(refnum, @txtbuf + 1, txtbuf)
|
|
if !(i & $0F); cout('.'); fin
|
|
next
|
|
close(refnum)
|
|
end
|
|
//
|
|
// Screen routines
|
|
//
|
|
def clrscrn#0
|
|
call($FC58, 0, 0, 0, 0)
|
|
end
|
|
def drawrow(row, ofst, strptr)#0
|
|
byte numchars
|
|
word scrnptr
|
|
|
|
scrnptr = txtscrn[row]
|
|
if ofst >= ^strptr
|
|
numchars = 0
|
|
else
|
|
numchars = ^strptr - ofst
|
|
fin
|
|
if numchars >= 40
|
|
numchars = 40
|
|
else
|
|
memset(scrnptr + numchars, $A0A0, 40 - numchars)
|
|
fin
|
|
memcpy(scrnptr, strptr + ofst + 1, numchars)
|
|
end
|
|
def drawscrn(toprow, ofst)#0
|
|
byte row, numchars
|
|
word strptr, scrnptr
|
|
|
|
for row = 0 to 23
|
|
strptr = strlinbuf:[toprow + row]
|
|
scrnptr = txtscrn[row]
|
|
numchars = ofst >= ^strptr ?? 0 :: ^strptr - ofst
|
|
if numchars >= 40
|
|
numchars = 40
|
|
else
|
|
memset(scrnptr + numchars, $A0A0, 40 - numchars)
|
|
fin
|
|
memcpy(scrnptr, strptr + ofst + 1, numchars)
|
|
next
|
|
end
|
|
def cursoff#0
|
|
if flags & showcurs
|
|
^cursptr = underchr
|
|
flags = flags & ~showcurs
|
|
fin
|
|
end
|
|
def curson#0
|
|
if !(flags & showcurs)
|
|
cursptr = txtscrn[cursy] + cursx
|
|
underchr = ^cursptr
|
|
^cursptr = curschr
|
|
flags = flags | showcurs
|
|
fin
|
|
end
|
|
def cursflash#0
|
|
if flags & showcurs
|
|
if flash == 0
|
|
^cursptr = curschr
|
|
elsif flash == 128
|
|
^cursptr = underchr
|
|
fin
|
|
flash++
|
|
fin
|
|
end
|
|
def redraw#0
|
|
cursoff
|
|
drawscrn(scrntop, scrnleft)
|
|
curson
|
|
end
|
|
def curshome#0
|
|
cursoff
|
|
cursrow = 0
|
|
curscol = 0
|
|
cursx = 0
|
|
cursy = 0
|
|
scrnleft = 0
|
|
scrntop = 0
|
|
drawscrn(scrntop, scrnleft)
|
|
curson
|
|
end
|
|
def cursend#0
|
|
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#0
|
|
if cursrow > 0
|
|
cursoff
|
|
cursrow--
|
|
if cursy > 0
|
|
cursy--
|
|
else
|
|
scrntop = cursrow
|
|
drawscrn(scrntop, scrnleft)
|
|
fin
|
|
curson
|
|
fin
|
|
end
|
|
def pgup#0
|
|
byte i
|
|
|
|
for i = pgjmp downto 0
|
|
cursup
|
|
next
|
|
end
|
|
def cursdown#0
|
|
if cursrow < numlines - 1
|
|
cursoff
|
|
cursrow++
|
|
if cursy < 23
|
|
cursy++
|
|
else
|
|
scrntop = cursrow - 23
|
|
drawscrn(scrntop, scrnleft)
|
|
fin
|
|
curson
|
|
fin
|
|
end
|
|
def pgdown#0
|
|
byte i
|
|
|
|
for i = pgjmp downto 0
|
|
cursdown
|
|
next
|
|
end
|
|
def cursleft#0
|
|
if curscol > 0
|
|
cursoff
|
|
curscol--
|
|
if cursx > 0
|
|
cursx--
|
|
else
|
|
scrnleft = curscol
|
|
drawscrn(scrntop, scrnleft)
|
|
fin
|
|
curson
|
|
fin
|
|
end
|
|
def pgleft#0
|
|
byte i
|
|
|
|
for i = 7 downto 0
|
|
cursleft
|
|
next
|
|
end
|
|
def cursright#0
|
|
if curscol < 80
|
|
cursoff
|
|
curscol++
|
|
if cursx < 39
|
|
cursx++
|
|
else
|
|
scrnleft = curscol - 39
|
|
drawscrn(scrntop, scrnleft)
|
|
fin
|
|
curson
|
|
fin
|
|
end
|
|
def pgright#0
|
|
byte i
|
|
|
|
for i = 7 downto 0
|
|
cursright
|
|
next
|
|
end
|
|
//
|
|
// Keyboard routines
|
|
//
|
|
def keyin2e
|
|
byte key
|
|
repeat
|
|
cursflash
|
|
key = ^keyboard
|
|
until key >= 128
|
|
^keystrobe
|
|
if ^$C062 & 128 // Closed Apple pressed
|
|
when key
|
|
is keyarrowleft
|
|
key = keyctrla; break
|
|
is keyarrowright
|
|
key = keyctrls; break
|
|
is keyarrowup
|
|
key = keyctrlw; break
|
|
is keyarrowdown
|
|
key = keyctrlz; break
|
|
is keyenter
|
|
key = keyctrlf; break
|
|
wend
|
|
fin
|
|
return key
|
|
end
|
|
def keyin2
|
|
byte key
|
|
|
|
repeat
|
|
cursflash
|
|
key = ^keyboard
|
|
if key == keyctrll
|
|
^keystrobe
|
|
flags = flags ^ shiftlock
|
|
key = 0
|
|
fin
|
|
until key >= 128
|
|
^keystrobe
|
|
when key
|
|
is keyctrln
|
|
key = $DB; break // [
|
|
is keyctrlp
|
|
key = $DF; break // _
|
|
is keyctrlb
|
|
key = $FC; break // |
|
|
is keyctrly
|
|
key = $FE; break // ~
|
|
// is keyarrowleft
|
|
// if ^pushbttn3 < 128
|
|
// key = $FF
|
|
// fin
|
|
// break
|
|
otherwise
|
|
if key >= $C0 and flags < shiftlock
|
|
if ^pushbttn3 < 128
|
|
when key
|
|
is $C0
|
|
key = $D0; break // P
|
|
is $DD
|
|
key = $CD; break // M
|
|
is $DE
|
|
key = $CE; break // N
|
|
wend
|
|
else
|
|
key = key | $E0
|
|
fin
|
|
fin
|
|
wend
|
|
return key
|
|
end
|
|
//
|
|
// Printer routines
|
|
//
|
|
def printtxt(slot)#0
|
|
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 + 1], @strlinbuf:[row], (numlines - row) * 2)
|
|
strlinbuf:[row] = @nullstr
|
|
numlines++
|
|
flags = flags | changed
|
|
return 1
|
|
fin
|
|
bell
|
|
return 0
|
|
end
|
|
def cutline#0
|
|
freestr(cutbuf)
|
|
cutbuf = strlinbuf:[cursrow]
|
|
memcpy(@strlinbuf:[cursrow], @strlinbuf:[cursrow + 1], (numlines - cursrow) * 2)
|
|
if numlines > 1
|
|
numlines--
|
|
fin
|
|
flags = flags | changed
|
|
if cursrow == numlines
|
|
cursup
|
|
fin
|
|
redraw
|
|
end
|
|
def pasteline#0
|
|
if cutbuf and numlines < maxlines
|
|
memcpy(@strlinbuf:[cursrow + 1], @strlinbuf:[cursrow], (numlines - cursrow) * 2)
|
|
strlinbuf:[cursrow] = newstr(cutbuf)
|
|
numlines++
|
|
flags = flags | changed
|
|
redraw
|
|
else
|
|
bell
|
|
fin
|
|
end
|
|
def joinline#0
|
|
byte joinstr[80], joinlen
|
|
|
|
if cursrow < numlines - 1
|
|
strcpy(@joinstr, strlinbuf:[cursrow])
|
|
joinlen = joinstr + ^(strlinbuf:[cursrow + 1])
|
|
if joinlen < 80
|
|
memcpy(@joinstr + joinstr + 1, strlinbuf:[cursrow + 1] + 1, ^(strlinbuf:[cursrow + 1]))
|
|
joinstr = joinlen
|
|
freestr(strlinbuf:[cursrow])
|
|
strlinbuf:[cursrow] = newstr(@joinstr)
|
|
freestr(strlinbuf:[cursrow + 1])
|
|
numlines--
|
|
memcpy(@strlinbuf:[cursrow + 1], @strlinbuf:[cursrow + 2], (numlines - cursrow) * 2)
|
|
flags = flags | changed
|
|
redraw
|
|
else
|
|
bell
|
|
fin
|
|
fin
|
|
end
|
|
def splitline#0
|
|
byte splitstr[80], splitlen
|
|
|
|
if openline(cursrow + 1)
|
|
if curscol
|
|
splitlen = ^(strlinbuf:[cursrow])
|
|
if curscol < splitlen - 1
|
|
memcpy(@splitstr + 1, strlinbuf:[cursrow] + curscol + 1, splitlen - curscol)
|
|
splitstr = splitlen - curscol
|
|
strlinbuf:[cursrow + 1] = newstr(@splitstr)
|
|
memcpy(@splitstr + 1, strlinbuf:[cursrow] + 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 TRUE
|
|
elsif key == keydelete
|
|
return TRUE
|
|
elsif key == keyctrld
|
|
return TRUE
|
|
elsif key == keyctrlr
|
|
return TRUE
|
|
fin
|
|
return FALSE
|
|
end
|
|
def editline(key)
|
|
byte editstr[80]
|
|
word undoline
|
|
|
|
if (editkey(key))
|
|
flags = flags | changed
|
|
memset(@editstr, $A0A0, 80)
|
|
strcpy(@editstr, strlinbuf:[cursrow])
|
|
undoline = strlinbuf:[cursrow]
|
|
strlinbuf:[cursrow] = @editstr
|
|
repeat
|
|
if key >= keyspace
|
|
if key == keydelete
|
|
if curscol > 0
|
|
if curscol <= editstr
|
|
memcpy(@editstr[curscol], @editstr[curscol + 1], editstr - curscol)
|
|
editstr--
|
|
fin
|
|
curscol--
|
|
cursoff
|
|
if cursx > 0
|
|
cursx--
|
|
drawrow(cursy, scrnleft, @editstr)
|
|
else
|
|
scrnleft--
|
|
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 + 1], @editstr[curscol], editstr - curscol)
|
|
fin
|
|
else
|
|
curscol--
|
|
cursx--
|
|
key = editstr[curscol]
|
|
bell
|
|
fin
|
|
elsif curscol > editstr
|
|
editstr = curscol
|
|
fin
|
|
editstr[curscol] = caseconv(key)
|
|
cursoff
|
|
if cursx <= 39
|
|
drawrow(cursy, scrnleft, @editstr)
|
|
else
|
|
scrnleft++
|
|
cursx = 39
|
|
drawscrn(scrntop, scrnleft)
|
|
fin
|
|
curson
|
|
else
|
|
bell
|
|
fin
|
|
elsif key == keyctrld
|
|
if curscol < editstr
|
|
strcpy(undoline, @editstr)
|
|
memcpy(@editstr[curscol + 1], @editstr[curscol + 2], editstr - curscol)
|
|
editstr--
|
|
cursoff
|
|
drawrow(cursy, scrnleft, @editstr)
|
|
curson
|
|
fin
|
|
elsif key == keyctrlr
|
|
strcpy(@editstr, undoline)
|
|
cursoff
|
|
drawrow(cursy, scrnleft, @editstr)
|
|
curson
|
|
fin
|
|
key = keyin()
|
|
until not editkey(key)
|
|
if editstr
|
|
strlinbuf:[cursrow] = newstr(@editstr)
|
|
else
|
|
strlinbuf:[cursrow] = @nullstr
|
|
fin
|
|
freestr(undoline)
|
|
fin
|
|
return key
|
|
end
|
|
def editmode#0
|
|
repeat
|
|
when editline(keyin())
|
|
is keyarrowup
|
|
cursup; break
|
|
is keyarrowdown
|
|
cursdown; break
|
|
is keyarrowleft
|
|
cursleft; break
|
|
is keyarrowright
|
|
cursright; break
|
|
is keyctrlw
|
|
pgup; break
|
|
is keyctrlz
|
|
pgdown; break
|
|
is keyctrla
|
|
pgleft; break
|
|
is keyctrls
|
|
pgright; break
|
|
is keyctrlq
|
|
curshome; break
|
|
is keyctrle
|
|
cursend; break
|
|
is keyctrlx
|
|
cutline; break
|
|
is keyctrlv
|
|
pasteline; break
|
|
is keyctrlf
|
|
if numlines < maxlines and cursrow == numlines - 1
|
|
strlinbuf:[numlines] = @nullstr
|
|
numlines++
|
|
fin
|
|
cursdown
|
|
is keyctrlo
|
|
openline(cursrow)
|
|
curscol = 0
|
|
cursx = 0
|
|
scrnleft = 0
|
|
redraw
|
|
break
|
|
is keyenter
|
|
if flags & insmode
|
|
splitline
|
|
else
|
|
cursdown
|
|
curscol = 0
|
|
cursx = 0
|
|
scrnleft = 0
|
|
redraw
|
|
fin
|
|
break
|
|
is keyctrlt
|
|
joinline; break
|
|
is keyctrli
|
|
if flags & insmode
|
|
flags = flags & ~insmode
|
|
curschr = ' '
|
|
else
|
|
flags = flags | insmode
|
|
curschr = '+'
|
|
fin
|
|
break
|
|
is keyctrlc
|
|
if flags & uppercase
|
|
txtlower
|
|
else
|
|
txtupper
|
|
fin
|
|
redraw
|
|
break
|
|
is keyescape
|
|
cursoff
|
|
cmdmode(TRUE)
|
|
redraw
|
|
break
|
|
wend
|
|
until FALSE
|
|
end
|
|
//
|
|
// Command mode
|
|
//
|
|
def prfiles(optpath)#0
|
|
byte path[64]
|
|
byte refnum
|
|
byte firstblk
|
|
byte entrylen, entriesblk
|
|
byte i, type, len
|
|
word entry, filecnt
|
|
|
|
if ^optpath
|
|
strcpy(@path, optpath)
|
|
else
|
|
getpfx(@path)
|
|
prstr(@path)
|
|
crout
|
|
fin
|
|
refnum = open(@path, iobuffer)
|
|
if !refnum
|
|
return
|
|
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
|
|
end
|
|
def striplead(strptr, chr)#0
|
|
while ^strptr and ^(strptr + 1) == chr
|
|
memcpy(strptr + 1, strptr + 2, ^strptr)
|
|
^strptr--
|
|
loop
|
|
end
|
|
def parsecmd(strptr)
|
|
byte cmd
|
|
|
|
cmd = 0
|
|
striplead(strptr, ' ')
|
|
if ^strptr
|
|
cmd = ^(strptr + 1)
|
|
memcpy(strptr + 1, strptr + 2, ^strptr)
|
|
^strptr--
|
|
fin
|
|
if ^strptr
|
|
striplead(strptr, ' ')
|
|
fin
|
|
return cmd
|
|
end
|
|
def chkchng
|
|
if flags & changed
|
|
prstr(@losechng)
|
|
if toupper(keyin()) == 'N'
|
|
crout
|
|
return FALSE
|
|
fin
|
|
crout
|
|
fin
|
|
return TRUE
|
|
end
|
|
def quit#0
|
|
if chkchng
|
|
exit
|
|
fin
|
|
end
|
|
def cmdmode(clearscr)#0
|
|
byte slot
|
|
word cmdptr
|
|
|
|
if (clearscr)
|
|
clrscrn
|
|
prstr(@version)
|
|
fin
|
|
crout
|
|
while TRUE
|
|
prstr(@txtfile)
|
|
cmdptr = rdstr($BA)
|
|
when toupper(parsecmd(cmdptr))
|
|
is 'A'
|
|
readtxt(cmdptr)
|
|
flags = flags | changed
|
|
break
|
|
is 'R'
|
|
if chkchng
|
|
inittxtbuf
|
|
numlines = 0
|
|
entrypoint = 0
|
|
strcpy(@txtfile, cmdptr)
|
|
readtxt(@txtfile)
|
|
if numlines == 0; numlines = 1; fin
|
|
flags = flags & ~changed
|
|
fin
|
|
break
|
|
is 'W'
|
|
if ^cmdptr
|
|
strcpy(@txtfile, cmdptr)
|
|
fin
|
|
writetxt(@txtfile)
|
|
if flags & changed; entrypoint = 0; fin
|
|
flags = flags & ~changed
|
|
break
|
|
is 'C'
|
|
prfiles(cmdptr); break
|
|
is 'P'
|
|
setpfx(cmdptr); break
|
|
is 'H'
|
|
if ^cmdptr
|
|
slot = cmdptr.1 - '0'
|
|
else
|
|
slot = 1
|
|
fin
|
|
printtxt(slot)
|
|
break
|
|
is 'Q'
|
|
quit
|
|
is 'E'
|
|
is 0
|
|
return
|
|
is 'N'
|
|
if chkchng
|
|
inittxtbuf
|
|
strcpy(@txtfile, @untitled)
|
|
entrypoint = 0
|
|
fin
|
|
break
|
|
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
|
|
entrypoint = 0
|
|
else
|
|
crout
|
|
fin
|
|
fin
|
|
if entrypoint
|
|
save_vmstate
|
|
entrypoint()
|
|
restore_vmstate
|
|
fin
|
|
crout
|
|
break
|
|
otherwise
|
|
bell
|
|
cout('?')
|
|
crout
|
|
wend
|
|
if perr
|
|
prstr(@errorstr)
|
|
call($FDDA, perr, 0, 0, 0)
|
|
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
|
|
//
|
|
// Code tags. Upper bit is IS_RESOLVED flag, lower 15 is offset into codebuff
|
|
// Flags are:
|
|
//
|
|
def ctag_new
|
|
if codetag >= ctag_max; return parse_err(@ctag_full); fin
|
|
codetag = codetag + 1
|
|
ctag_tbl:[codetag] = 0 // Unresolved, nothing to update yet
|
|
return codetag | IS_CTAG
|
|
end
|
|
def ctag_resolve(ctag)#0
|
|
word updtptr, nextptr
|
|
|
|
ctag = ctag & MASK_CTAG // Better be a ctag!
|
|
if ctag_tbl:[ctag] & IS_RESOLVED;parse_err(@dup_id); return; fin
|
|
updtptr = ctag_tbl:[ctag] & MASK_CTAG
|
|
while updtptr
|
|
//
|
|
// Update list of addresses needing resolution
|
|
//
|
|
updtptr = updtptr + codebuff
|
|
nextptr = *updtptr & MASK_CTAG
|
|
if *updtptr & IS_RELATIVE
|
|
*updtptr = codeptr - updtptr
|
|
else
|
|
*updtptr = codeptr
|
|
fin
|
|
updtptr = nextptr
|
|
loop
|
|
ctag_tbl:[ctag] = (codeptr - codebuff) | IS_RESOLVED
|
|
end
|
|
//
|
|
// Emit data/bytecode
|
|
//
|
|
def emit_byte(bval)#0
|
|
^codeptr = bval
|
|
codeptr++
|
|
end
|
|
def emit_word(wval)#0
|
|
*codeptr = wval
|
|
codeptr = codeptr + 2
|
|
end
|
|
def emit_fill(size)#0
|
|
memset(codeptr, 0, size)
|
|
codeptr = codeptr + size
|
|
end
|
|
def emit_op(op)#0
|
|
lastop = op
|
|
emit_byte(op)
|
|
end
|
|
def emit_addr(tag)#0
|
|
word updtptr
|
|
|
|
if tag & IS_CTAG
|
|
tag = tag & MASK_CTAG
|
|
if ctag_tbl:[tag] & IS_RESOLVED
|
|
updtptr = (ctag_tbl:[tag] & MASK_CTAG) + codebuff
|
|
else
|
|
//
|
|
// Add to list of tags needing resolution
|
|
//
|
|
updtptr = ctag_tbl:[tag] & MASK_CTAG
|
|
ctag_tbl:[tag] = codeptr - codebuff
|
|
fin
|
|
emit_word(updtptr)
|
|
else
|
|
emit_word(tag + codebuff)
|
|
fin
|
|
end
|
|
def emit_reladdr(tag)#0
|
|
word updtptr
|
|
|
|
if tag & IS_CTAG
|
|
tag = tag & MASK_CTAG
|
|
if ctag_tbl:[tag] & IS_RESOLVED
|
|
updtptr = ((ctag_tbl:[tag] & MASK_CTAG) + codebuff) - codeptr
|
|
else
|
|
//
|
|
// Add to list of tags needing resolution
|
|
//
|
|
updtptr = ctag_tbl:[tag] | IS_RELATIVE
|
|
ctag_tbl:[tag] = codeptr - codebuff
|
|
fin
|
|
emit_word(updtptr)
|
|
else
|
|
emit_word(tag - (codeptr - codebuff))
|
|
fin
|
|
end
|
|
def emit_iddata(value, size, namestr)#0
|
|
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--
|
|
emit_byte(constsize)
|
|
while constsize > 0
|
|
emit_byte(^chrptr)
|
|
chrptr++
|
|
constsize--
|
|
loop
|
|
else
|
|
if vartype & BYTE_TYPE
|
|
size = 1
|
|
emit_byte(constval)
|
|
else
|
|
size = 2
|
|
if consttype == CONSTADDR_TYPE
|
|
emit_addr(constval)
|
|
else
|
|
emit_word(constval)
|
|
fin
|
|
fin
|
|
fin
|
|
return size
|
|
end
|
|
def emit_const(cval)#0
|
|
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_constr(str, size)#0
|
|
emit_op($2E)
|
|
emit_data(0, STR_TYPE, str, size)
|
|
end
|
|
def emit_lb#0
|
|
emit_op($60)
|
|
end
|
|
def emit_lw#0
|
|
emit_op($62)
|
|
end
|
|
def emit_llb(offset)#0
|
|
emit_op($64)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_llw(offset)#0
|
|
emit_op($66)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_lab(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($68)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_law(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($6A)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_sb#0
|
|
emit_op($70)
|
|
end
|
|
def emit_sw#0
|
|
emit_op($72)
|
|
end
|
|
def emit_slb(offset)#0
|
|
emit_op($74)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_slw(offset)#0
|
|
emit_op($76)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_dup#0
|
|
parse_err("No DUP op!")
|
|
end
|
|
def emit_dlb(offset)#0
|
|
emit_op($6C)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_dlw(offset)#0
|
|
emit_op($6E)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_sab(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($78)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_saw(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($7A)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_dab(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($7C)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_daw(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($7E)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_call(tag)#0
|
|
emit_op($54)
|
|
emit_addr(tag)
|
|
end
|
|
def emit_ical#0
|
|
emit_op($56)
|
|
end
|
|
def emit_push#0
|
|
parse_err("Function call too complex")
|
|
end
|
|
def emit_pull#0
|
|
end
|
|
def emit_localaddr(offset)#0
|
|
emit_op($28)
|
|
emit_byte(offset)
|
|
end
|
|
def emit_globaladdr(tag, offset)#0
|
|
if tag & IS_CTAG and offset
|
|
parse_err(@no_ctag_offst)
|
|
else
|
|
emit_op($26)
|
|
emit_addr(tag+offset)
|
|
fin
|
|
end
|
|
def emit_indexbyte#0
|
|
emit_op($02)
|
|
end
|
|
def emit_indexword#0
|
|
emit_op($1E)
|
|
end
|
|
def emit_unaryop(op)
|
|
when op
|
|
is NEG_TKN
|
|
emit_op($10); break
|
|
is ALT_COMP_TKN
|
|
is COMP_TKN
|
|
emit_op($12); break
|
|
is LOGIC_NOT_TKN
|
|
emit_op($20); break
|
|
is INC_TKN
|
|
emit_op($0C); break
|
|
is DEC_TKN
|
|
emit_op($0E); break
|
|
is BPTR_TKN
|
|
emit_op($60); break
|
|
is WPTR_TKN
|
|
emit_op($62); break
|
|
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
|
|
break
|
|
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
|
|
break
|
|
is MOD_TKN
|
|
emit_op($0A); break
|
|
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
|
|
break
|
|
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
|
|
break
|
|
is SHL_TKN
|
|
emit_op($1A); break
|
|
is SHR_TKN
|
|
emit_op($1C); break
|
|
is AND_TKN
|
|
emit_op($14); break
|
|
is OR_TKN
|
|
emit_op($16); break
|
|
is EOR_TKN
|
|
emit_op($18); break
|
|
is EQ_TKN
|
|
emit_op($40); break
|
|
is NE_TKN
|
|
emit_op($42); break
|
|
is GE_TKN
|
|
emit_op($48); break
|
|
is LT_TKN
|
|
emit_op($46); break
|
|
is GT_TKN
|
|
emit_op($44); break
|
|
is LE_TKN
|
|
emit_op($4A); break
|
|
is LOGIC_OR_TKN
|
|
emit_op($22); break
|
|
is LOGIC_AND_TKN
|
|
emit_op($24); break
|
|
otherwise
|
|
return FALSE
|
|
wend
|
|
return TRUE
|
|
end
|
|
def emit_brtru(tag)#0
|
|
emit_op($4E)
|
|
emit_reladdr(tag)
|
|
end
|
|
def emit_brfls(tag)#0
|
|
emit_op($4C)
|
|
emit_reladdr(tag)
|
|
end
|
|
def emit_brgt(tag)#0
|
|
emit_op($38)
|
|
emit_reladdr(tag)
|
|
end
|
|
def emit_brlt(tag)#0
|
|
emit_op($3A)
|
|
emit_reladdr(tag)
|
|
end
|
|
def emit_brne(tag)#0
|
|
emit_op($3E)
|
|
emit_reladdr(tag)
|
|
end
|
|
def emit_branch(tag)#0
|
|
emit_op($50)
|
|
emit_reladdr(tag)
|
|
end
|
|
def emit_drop#0
|
|
emit_op($30)
|
|
end
|
|
def emit_leave#0
|
|
if framesize
|
|
emit_op($5A)
|
|
else
|
|
emit_op($5C)
|
|
fin
|
|
end
|
|
def emit_enter(cparams)#0
|
|
emit_byte(emit_enter.[0])
|
|
emit_byte(emit_enter.[1])
|
|
emit_byte(emit_enter.[2])
|
|
if framesize
|
|
emit_op($58)
|
|
emit_byte(framesize)
|
|
emit_byte(cparams)
|
|
fin
|
|
end
|
|
//
|
|
// Symbol table
|
|
//
|
|
def 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--
|
|
loop
|
|
return 0
|
|
end
|
|
def dumpsym(idptr, idcnt)#0
|
|
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_tbl:[idptr=>idval & MASK_CTAG] & MASK_CTAG) + codebuff)
|
|
else
|
|
prword(idptr=>idval + codebuff)
|
|
fin
|
|
else
|
|
prword(idptr=>idval)
|
|
fin
|
|
crout
|
|
idptr = idptr + idptr->idname + idrecsz
|
|
idcnt--
|
|
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++
|
|
lastlocal = lastlocal + idrecsz + len
|
|
if lastlocal > idlocal_tbl + idlocal_tblsz
|
|
prstr(@local_sym_overflw)
|
|
exit
|
|
fin
|
|
framesize = framesize + size
|
|
if framesize > 255
|
|
return parse_err(@local_overflw)
|
|
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++
|
|
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)#0
|
|
if varsize > initsize
|
|
datasize = datasize + varsize
|
|
emit_data(0, 0, 0, varsize - initsize)
|
|
else
|
|
datasize = datasize + initsize
|
|
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++
|
|
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#0
|
|
word ctag
|
|
|
|
lineno = 0
|
|
parserr = 0
|
|
codeptr = codebuff
|
|
lastop = $FF
|
|
entrypoint = 0
|
|
globals = 0
|
|
lastglobal = idglobal_tbl
|
|
codetag = -1
|
|
//
|
|
// Create local jump table to some library functions
|
|
//
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime0 + 1, runtime0, ctag)
|
|
idfunc_add(@RUNTIME0 + 1, RUNTIME0, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@call)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime1 + 1, runtime1, ctag)
|
|
idfunc_add(@RUNTIME1 + 1, RUNTIME1, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@syscall)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime2 + 1, runtime2, ctag)
|
|
idfunc_add(@RUNTIME2 + 1, RUNTIME2, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@memset)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime3 + 1, runtime3, ctag)
|
|
idfunc_add(@RUNTIME3 + 1, RUNTIME3, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@memcpy)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime4 + 1, runtime4, ctag)
|
|
idfunc_add(@RUNTIME4 + 1, RUNTIME4, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@cout)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime5 + 1, runtime5, ctag)
|
|
idfunc_add(@RUNTIME5 + 1, RUNTIME5, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@cin)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime6 + 1, runtime6, ctag)
|
|
idfunc_add(@RUNTIME6 + 1, RUNTIME6, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@prstr)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime7 + 1, runtime7, ctag)
|
|
idfunc_add(@RUNTIME7 + 1, RUNTIME7, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@rdstr)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime8 + 1, runtime8, ctag)
|
|
idfunc_add(@RUNTIME8 + 1, RUNTIME8, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@print)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime9 + 1, runtime9, ctag)
|
|
idfunc_add(@RUNTIME9 + 1, RUNTIME9, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@home)
|
|
ctag = ctag_new
|
|
idfunc_add(@runtime10 + 1, runtime10, ctag)
|
|
idfunc_add(@RUNTIME10 + 1, RUNTIME10, ctag)
|
|
ctag_resolve(ctag)
|
|
emit_byte($4C)
|
|
emit_word(@gotoxy)
|
|
//
|
|
// Start data after jump table
|
|
//
|
|
datasize = codeptr - codebuff
|
|
end
|
|
def idlocal_init#0
|
|
locals = 0
|
|
framesize = 0
|
|
lastlocal = idlocal_tbl
|
|
end
|
|
//
|
|
// Alebraic op to stack op
|
|
//
|
|
def push_op(op, prec)#0
|
|
opsp++
|
|
if opsp == 16; parse_err(@estk_overflw); return; fin
|
|
opstack[opsp] = op
|
|
precstack[opsp] = prec
|
|
end
|
|
def pop_op
|
|
if opsp < 0; return parse_err(@estk_underflw); fin
|
|
opsp--
|
|
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
|
|
def push_val(value, size, type)#0
|
|
valsp++
|
|
if valsp == 16; parse_err(@estk_overflw); return; fin
|
|
valstack[valsp] = value
|
|
sizestack[valsp] = size
|
|
typestack[valsp] = type
|
|
end
|
|
def pop_val(valptr, sizeptr, typeptr)
|
|
if valsp < 0; return parse_err(@estk_underflw); fin
|
|
*valptr = valstack[valsp]
|
|
^sizeptr = sizestack[valsp]
|
|
^typeptr = typestack[valsp]
|
|
valsp--
|
|
return valsp + 1
|
|
end
|
|
//
|
|
// Lexical anaylzer
|
|
//
|
|
def keymatch
|
|
byte i, keypos
|
|
word chrptr
|
|
|
|
keypos = 0
|
|
while keywrds[keypos] < tknlen
|
|
keypos = keypos + keywrds[keypos] + 2
|
|
loop
|
|
chrptr = tknptr - 1
|
|
while keywrds[keypos] == tknlen
|
|
for i = 1 to tknlen
|
|
if toupper(^(chrptr + i)) <> keywrds[keypos + i]
|
|
break
|
|
fin
|
|
next
|
|
if i > tknlen
|
|
return keywrds[keypos + keywrds[keypos] + 1]
|
|
fin
|
|
keypos = keypos + keywrds[keypos] + 2
|
|
loop
|
|
return ID_TKN
|
|
end
|
|
def scan
|
|
//
|
|
// Skip whitespace
|
|
//
|
|
scanptr = skipspace(scanptr)
|
|
tknptr = scanptr
|
|
scanchr = ^scanptr
|
|
//
|
|
// Scan for token based on first character
|
|
//
|
|
if isalpha(scanchr)
|
|
//
|
|
// ID, either variable name or reserved word
|
|
//
|
|
repeat
|
|
scanptr++
|
|
until !isalphanum(^scanptr)
|
|
tknlen = scanptr - tknptr
|
|
token = keymatch
|
|
elsif isnum(scanchr)
|
|
//
|
|
// Decimal constant
|
|
//
|
|
token = INT_TKN
|
|
constval = 0
|
|
repeat
|
|
constval = constval * 10 + ^scanptr - '0'
|
|
scanptr++
|
|
until !isnum(^scanptr)
|
|
else
|
|
//
|
|
// Potential multiple character tokens
|
|
//
|
|
when scanchr
|
|
is '/'
|
|
if ^(scanptr + 1) == '/'
|
|
token = EOL_TKN
|
|
^scanptr = $00
|
|
else
|
|
token = DIV_TKN
|
|
scanptr++
|
|
fin
|
|
break
|
|
is '='
|
|
if ^(scanptr + 1) == '='
|
|
token = EQ_TKN
|
|
scanptr = scanptr + 2
|
|
elsif ^(scanptr + 1) == '>'
|
|
token = PTRW_TKN
|
|
scanptr = scanptr + 2
|
|
else
|
|
token = SET_TKN
|
|
scanptr++
|
|
fin
|
|
break
|
|
is '-'
|
|
if ^(scanptr + 1) == '>'
|
|
token = PTRB_TKN
|
|
scanptr = scanptr + 2
|
|
elsif ^(scanptr + 1) == '-'
|
|
token = DEC_TKN
|
|
scanptr = scanptr + 2
|
|
else
|
|
token = SUB_TKN
|
|
scanptr++
|
|
fin
|
|
break
|
|
is '+'
|
|
if ^(scanptr + 1) == '+'
|
|
token = INC_TKN
|
|
scanptr = scanptr + 2
|
|
else
|
|
token = ADD_TKN
|
|
scanptr++
|
|
fin
|
|
break
|
|
is '>'
|
|
if ^(scanptr + 1) == '>'
|
|
token = SHR_TKN
|
|
scanptr = scanptr + 2
|
|
elsif ^(scanptr + 1) == '='
|
|
token = GE_TKN
|
|
scanptr = scanptr + 2
|
|
else
|
|
token = GT_TKN
|
|
scanptr++
|
|
fin
|
|
break
|
|
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++
|
|
fin
|
|
break
|
|
is '$'
|
|
//
|
|
// Hexadecimal constant
|
|
//
|
|
token = INT_TKN
|
|
constval = 0
|
|
repeat
|
|
scanptr++
|
|
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
|
|
break
|
|
is $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; break
|
|
is 'r'
|
|
constval = $0A; break
|
|
is 't'
|
|
constval = $09; break
|
|
otherwise
|
|
constval = ^(scanptr + 2)
|
|
wend
|
|
if ^(scanptr + 3) <> $27 // '
|
|
return parse_err(@bad_cnst)
|
|
fin
|
|
scanptr = scanptr + 4
|
|
fin
|
|
break
|
|
is '"'
|
|
//
|
|
// String constant
|
|
//
|
|
token = STR_TKN
|
|
constval = scanptr
|
|
scanptr++
|
|
while ^scanptr and ^scanptr <> '"'
|
|
scanptr++
|
|
loop
|
|
if !^scanptr
|
|
return parse_err(@bad_cnst)
|
|
fin
|
|
scanptr++
|
|
break
|
|
is 0
|
|
is ';'
|
|
if token <> EOF_TKN
|
|
token = EOL_TKN
|
|
fin
|
|
break
|
|
otherwise
|
|
//
|
|
// Simple single character tokens
|
|
//
|
|
token = scanchr | $80
|
|
scanptr++
|
|
wend
|
|
fin
|
|
tknlen = scanptr - tknptr
|
|
return token
|
|
end
|
|
def rewind(ptr)#0
|
|
scanptr = ptr
|
|
end
|
|
def lookahead
|
|
word backptr, backtkn
|
|
byte prevtkn, prevlen, look
|
|
backptr = scanptr
|
|
backtkn = tknptr
|
|
prevtkn = token
|
|
prevlen = tknlen
|
|
look = scan
|
|
scanptr = backptr
|
|
tknptr = backtkn
|
|
token = prevtkn
|
|
tknlen = prevlen
|
|
return look
|
|
end
|
|
//
|
|
// Get next line of input
|
|
//
|
|
def nextln
|
|
if ^scanptr == ';'
|
|
scanptr++
|
|
scan
|
|
else
|
|
scanptr = inbuff
|
|
if lineno < numlines
|
|
cpyln(strlinbuf:[lineno], instr)
|
|
lineno++
|
|
if !(lineno & $0F); cout('.'); fin
|
|
//print(lineno);cout(':');print(numlines);cout('>');prstr(instr);crout
|
|
scan
|
|
else
|
|
//cout('<');crout
|
|
*instr = 0
|
|
//^inbuff = 0
|
|
token = DONE_TKN
|
|
fin
|
|
fin
|
|
return token
|
|
end
|
|
//
|
|
// Parser
|
|
//
|
|
//
|
|
// Constant expression parsing
|
|
//
|
|
def calc_binaryop(op)
|
|
word val1, val2
|
|
byte size1, size2, type1, type2
|
|
|
|
if not pop_val(@val2, @size2, @type2); return 0; fin
|
|
pop_val(@val1, @size1, @type1)
|
|
if type1 <> CONST_TYPE and type2 <> CONST_TYPE; return parse_err(@bad_cnst); fin
|
|
when op
|
|
is MUL_TKN
|
|
val1 = val1 * val2
|
|
break
|
|
is DIV_TKN
|
|
val1 = val1 / val2
|
|
break
|
|
is MOD_TKN
|
|
val1 = val1 % val2
|
|
break
|
|
is ADD_TKN
|
|
val1 = val1 + val2
|
|
break
|
|
is SUB_TKN
|
|
val1 = val1 - val2
|
|
break
|
|
is SHL_TKN
|
|
val1 = val1 << val2
|
|
break
|
|
is SHR_TKN
|
|
val1 = val1 >> val2
|
|
break
|
|
is AND_TKN
|
|
val1 = val1 & val2
|
|
break
|
|
is OR_TKN
|
|
val1 = val1 | val2
|
|
break
|
|
is EOR_TKN
|
|
val1 = val1 ^ val2
|
|
break
|
|
otherwise
|
|
return FALSE
|
|
wend
|
|
if size2 > size1; size1 = size2; fin
|
|
push_val(val1, size1, type1)
|
|
return TRUE
|
|
end
|
|
def parse_constterm(valptr, sizeptr)
|
|
word type
|
|
|
|
when scan
|
|
is OPEN_PAREN_TKN
|
|
type = parse_constexpr(valptr, sizeptr)
|
|
if token <> CLOSE_PAREN_TKN; return parse_err(@no_close_paren); fin
|
|
return type
|
|
is ID_TKN
|
|
is INT_TKN
|
|
is CHR_TKN
|
|
is STR_TKN
|
|
return token
|
|
wend
|
|
return FALSE
|
|
end
|
|
def parse_constval
|
|
byte mod, type, size
|
|
word idptr, ctag, value
|
|
|
|
value = 0
|
|
size = 1
|
|
mod = 0
|
|
repeat
|
|
type = parse_constterm(@value, @size)
|
|
if !type
|
|
when token
|
|
is SUB_TKN
|
|
mod = mod | 1; break
|
|
is ALT_COMP_TKN
|
|
is COMP_TKN
|
|
mod = mod | 2; break
|
|
is LOGIC_NOT_TKN
|
|
mod = mod | 4; break
|
|
is AT_TKN
|
|
mod = mod | 8; break
|
|
is ADD_TKN
|
|
break
|
|
otherwise
|
|
return 0
|
|
wend
|
|
fin
|
|
until type
|
|
when token
|
|
is CLOSE_PAREN_TKN
|
|
break
|
|
is STR_TKN
|
|
size = tknlen - 1
|
|
value = constval
|
|
type = STR_TYPE
|
|
if mod; return parse_err(@bad_op); fin
|
|
break
|
|
is CHR_TKN
|
|
size = 1
|
|
value = constval
|
|
type = CONST_TYPE
|
|
break
|
|
is INT_TKN
|
|
size = 2
|
|
value = constval
|
|
type = CONST_TYPE
|
|
break
|
|
is ID_TKN
|
|
size = 2
|
|
idptr = id_lookup(tknptr, tknlen)
|
|
if !idptr; return parse_err(@bad_cnst); fin
|
|
type = idptr->idtype
|
|
if type & ADDR_TYPE
|
|
if mod <> 8; return parse_err(@bad_cnst); fin
|
|
type = CONSTADDR_TYPE
|
|
fin
|
|
value = idptr=>idval
|
|
break
|
|
otherwise
|
|
return 0
|
|
wend
|
|
if mod & 1
|
|
value = -value
|
|
fin
|
|
if mod & 2
|
|
value = ~value
|
|
fin
|
|
if mod & 4
|
|
value = !value
|
|
fin
|
|
push_val(value, size, type)
|
|
return type
|
|
end
|
|
def parse_constexpr(valptr, sizeptr)
|
|
byte prevmatch, matchop, i, type
|
|
word optos
|
|
|
|
*valptr = 0
|
|
^sizeptr = 1
|
|
matchop = 0
|
|
optos = opsp
|
|
repeat
|
|
prevmatch = matchop
|
|
matchop = 0
|
|
if parse_constval
|
|
matchop = 1
|
|
scan
|
|
for i = 0 to bops_tblsz
|
|
if token == bops_tbl[i]
|
|
matchop = 2
|
|
if bops_prec[i] >= tos_op_prec(optos)
|
|
if !calc_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 == 0; return 0; fin
|
|
if matchop == 0 and prevmatch == 2; return parse_err(@missing_op); fin
|
|
while optos < opsp
|
|
if !calc_binaryop(pop_op); return parse_err(@bad_op); fin
|
|
loop
|
|
pop_val(valptr, sizeptr, @type)
|
|
return type
|
|
end
|
|
def parse_const(valptr)
|
|
word idptr
|
|
|
|
when scan
|
|
is CHR_TKN
|
|
is INT_TKN
|
|
*valptr = constval
|
|
break
|
|
is ID_TKN
|
|
idptr = id_lookup(tknptr, tknlen)
|
|
if !idptr; return parse_err(@bad_cnst); fin
|
|
if idptr->idtype & CONST_TYPE
|
|
*valptr = idptr=>idval
|
|
break
|
|
fin
|
|
otherwise
|
|
return 0
|
|
wend
|
|
return CONST_TYPE
|
|
end
|
|
//
|
|
// Normal expression parsing
|
|
//
|
|
def ispostop
|
|
when scan
|
|
is OPEN_PAREN_TKN
|
|
is OPEN_BRACKET_TKN
|
|
is DOT_TKN
|
|
is COLON_TKN
|
|
is PTRB_TKN
|
|
is PTRW_TKN
|
|
return TRUE
|
|
wend
|
|
return FALSE
|
|
end
|
|
def parse_term
|
|
when scan
|
|
is OPEN_PAREN_TKN
|
|
if !parse_expr
|
|
return FALSE
|
|
fin
|
|
if token <> CLOSE_PAREN_TKN; return parse_err(@no_close_paren); fin
|
|
is ID_TKN
|
|
is INT_TKN
|
|
is CHR_TKN
|
|
is STR_TKN
|
|
return TRUE
|
|
wend
|
|
return FALSE
|
|
end
|
|
def parse_value(rvalue)
|
|
byte cparams, deref, type, emit_val
|
|
word optos, idptr, value
|
|
byte const_size, ref_type
|
|
word ref_offset, const_offset
|
|
|
|
deref = rvalue
|
|
optos = opsp
|
|
type = 0
|
|
emit_val = FALSE
|
|
value = 0
|
|
|
|
//
|
|
// Parse pre-ops
|
|
//
|
|
while !parse_term
|
|
when token
|
|
is ADD_TKN
|
|
break
|
|
is BPTR_TKN
|
|
if deref
|
|
push_op(token, 0)
|
|
else
|
|
deref++
|
|
type = type | BPTR_TYPE
|
|
fin
|
|
break
|
|
is WPTR_TKN
|
|
if deref
|
|
push_op(token, 0)
|
|
else
|
|
deref++
|
|
type = type | WPTR_TYPE
|
|
fin
|
|
break
|
|
is AT_TKN
|
|
deref--
|
|
break
|
|
is SUB_TKN
|
|
is ALT_COMP_TKN
|
|
is COMP_TKN
|
|
is LOGIC_NOT_TKN
|
|
push_op(token, 0)
|
|
break
|
|
otherwise
|
|
return 0
|
|
wend
|
|
loop
|
|
//
|
|
// Determine terminal type
|
|
//
|
|
when token
|
|
is INT_TKN
|
|
is CHR_TKN
|
|
value = constval
|
|
type = type | CONST_TYPE
|
|
break
|
|
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
|
|
break
|
|
is CLOSE_PAREN_TKN
|
|
// type = type | WORD_TYPE
|
|
emit_val = TRUE
|
|
break
|
|
is STR_TKN
|
|
//
|
|
// Special case
|
|
//
|
|
emit_constr(constval, tknlen - 1)
|
|
scan
|
|
return WORD_TYPE
|
|
break
|
|
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
|
|
break
|
|
is ALT_COMP_TKN
|
|
is COMP_TKN
|
|
pop_op
|
|
value = ~value
|
|
break
|
|
is LOGIC_NOT_TKN
|
|
pop_op
|
|
value = !value
|
|
break
|
|
otherwise
|
|
cparams = FALSE
|
|
wend
|
|
loop
|
|
fin
|
|
//
|
|
// Parse post-ops
|
|
//
|
|
ref_type = type & ~PTR_TYPE
|
|
ref_offset = 0
|
|
while ispostop
|
|
when token
|
|
is OPEN_PAREN_TKN
|
|
//
|
|
// Function call
|
|
//
|
|
if emit_val
|
|
if ref_offset <> 0
|
|
emit_const(ref_offset)
|
|
emit_op($02)
|
|
ref_offset = 0
|
|
fin
|
|
if ref_type & BPTR_TYPE; emit_lb
|
|
elsif ref_type & WPTR_TYPE; emit_lw
|
|
fin
|
|
if lookahead <> CLOSE_PAREN_TKN
|
|
emit_push
|
|
fin
|
|
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 ref_type & FUNC_CONST_TYPE
|
|
emit_call(value)
|
|
else
|
|
if !emit_val
|
|
if ref_type & CONST_TYPE
|
|
emit_const(value)
|
|
elsif ref_type & VAR_TYPE
|
|
if type & LOCAL_TYPE
|
|
emit_llw(value + ref_offset)
|
|
else
|
|
emit_law(value, ref_offset)
|
|
fin
|
|
ref_offset = 0
|
|
fin
|
|
else
|
|
if cparams
|
|
emit_pull
|
|
fin
|
|
fin
|
|
emit_ical
|
|
fin
|
|
emit_val = TRUE
|
|
ref_type = 0
|
|
break
|
|
is OPEN_BRACKET_TKN
|
|
//
|
|
// Array of arrays
|
|
//
|
|
if !emit_val
|
|
if type & CONST_TYPE
|
|
emit_const(value)
|
|
elsif type & ADDR_TYPE
|
|
if type & LOCAL_TYPE
|
|
emit_localaddr(value + ref_offset)
|
|
else
|
|
emit_globaladdr(value, ref_offset)
|
|
fin
|
|
ref_offset = 0
|
|
fin
|
|
emit_val = TRUE
|
|
else
|
|
if ref_offset <> 0
|
|
emit_const(ref_offset)
|
|
emit_op($02)
|
|
ref_offset = 0
|
|
fin
|
|
fin
|
|
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 ref_type & (WPTR_TYPE | WORD_TYPE)
|
|
emit_indexword
|
|
ref_type = WPTR_TYPE
|
|
else
|
|
emit_indexbyte
|
|
ref_type = BPTR_TYPE
|
|
fin
|
|
break
|
|
is PTRB_TKN
|
|
is PTRW_TKN
|
|
//
|
|
// Structure member pointer
|
|
//
|
|
if !emit_val
|
|
if (type & CONST_TYPE)
|
|
emit_const(value)
|
|
elsif type & ADDR_TYPE
|
|
if type & LOCAL_TYPE
|
|
if ref_type & BYTE_TYPE
|
|
emit_llb(value + ref_offset)
|
|
else
|
|
emit_llw(value + ref_offset)
|
|
fin
|
|
else
|
|
if ref_type & BYTE_TYPE
|
|
emit_lab(value, ref_offset)
|
|
else
|
|
emit_law(value, ref_offset)
|
|
fin
|
|
fin
|
|
fin
|
|
emit_val = 1;
|
|
else
|
|
if ref_offset <> 0
|
|
emit_const(ref_offset)
|
|
emit_op($02)
|
|
ref_offset = 0
|
|
fin
|
|
if ref_type & BPTR_TYPE; emit_lb
|
|
elsif ref_type & WPTR_TYPE; emit_lw; fin
|
|
fin
|
|
if token == PTRB_TKN
|
|
ref_type = BPTR_TYPE
|
|
else
|
|
ref_type = WPTR_TYPE
|
|
fin
|
|
ref_offset = 0
|
|
if !parse_const(@ref_offset)
|
|
rewind(tknptr)
|
|
fin
|
|
if ref_offset <> 0
|
|
emit_const(ref_offset)
|
|
emit_op($02)
|
|
ref_offset = 0
|
|
fin
|
|
break
|
|
is DOT_TKN
|
|
is COLON_TKN
|
|
//
|
|
// Structure member offset
|
|
//
|
|
if ref_type & (VAR_TYPE | CONST_TYPE)
|
|
if token == DOT_TKN
|
|
ref_type = BYTE_TYPE
|
|
else
|
|
ref_type = WORD_TYPE
|
|
fin
|
|
else
|
|
if token == DOT_TKN
|
|
ref_type = BPTR_TYPE
|
|
else
|
|
ref_type = WPTR_TYPE
|
|
fin
|
|
fin
|
|
if parse_const(@const_offset)
|
|
ref_offset = ref_offset + const_offset
|
|
else
|
|
rewind(tknptr)
|
|
fin
|
|
if !emit_val
|
|
if type & CONST_TYPE
|
|
value = value + ref_offset
|
|
ref_offset = 0
|
|
elsif type & FUNC_TYPE
|
|
emit_globaladdr(value, ref_offset)
|
|
ref_offset = 0
|
|
emit_val = TRUE
|
|
fin
|
|
fin
|
|
break
|
|
wend
|
|
loop
|
|
if emit_val
|
|
if ref_offset <> 0
|
|
emit_const(ref_offset)
|
|
emit_op($02)
|
|
ref_offset = 0
|
|
fin
|
|
if deref
|
|
if ref_type & BPTR_TYPE
|
|
emit_lb
|
|
elsif ref_type & WPTR_TYPE
|
|
emit_lw
|
|
fin
|
|
fin
|
|
else // emit_val
|
|
if deref
|
|
if ref_type & CONST_TYPE
|
|
emit_const(value)
|
|
if ref_type & VAR_TYPE
|
|
if ref_type & BYTE_TYPE
|
|
emit_lb()
|
|
else
|
|
emit_lw()
|
|
fin
|
|
fin
|
|
elsif ref_type & FUNC_TYPE
|
|
emit_call(value)
|
|
elsif ref_type & VAR_TYPE
|
|
if type & LOCAL_TYPE
|
|
if ref_type & BYTE_TYPE
|
|
emit_llb(value + ref_offset)
|
|
else
|
|
emit_llw(value + ref_offset)
|
|
fin
|
|
else
|
|
if ref_type & BYTE_TYPE
|
|
emit_lab(value, ref_offset)
|
|
else
|
|
emit_law(value, ref_offset)
|
|
fin
|
|
fin
|
|
fin
|
|
else
|
|
if type & CONST_TYPE
|
|
emit_const(value)
|
|
elsif type & ADDR_TYPE
|
|
if type & LOCAL_TYPE
|
|
emit_localaddr(value + ref_offset)
|
|
else
|
|
emit_globaladdr(value, ref_offset)
|
|
fin
|
|
fin
|
|
fin
|
|
fin // emit_val
|
|
while optos < opsp
|
|
if !emit_unaryop(pop_op); return parse_err(@bad_op); fin
|
|
loop
|
|
if type & PTR_TYPE
|
|
ref_type = type
|
|
fin
|
|
if !ref_type
|
|
ref_type = WORD_TYPE
|
|
fin
|
|
return ref_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, elem_type, elem_size, i
|
|
word elem_offset, tag_prevbrk, tag_prevcnt, tag_else, tag_endif, tag_while, tag_wend
|
|
word tag_repeat, tag_for, tag_choice, tag_of, idptr, saveptr, addr, stepdir
|
|
|
|
if token <> END_TKN and token <> DONE_TKN and token <> OF_TKN and token <> DEFAULT_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_branch(tag_endif)
|
|
ctag_resolve(tag_else)
|
|
if !parse_expr; return FALSE; fin
|
|
tag_else = ctag_new
|
|
emit_brfls(tag_else)
|
|
until FALSE
|
|
if token == ELSE_TKN
|
|
emit_branch(tag_endif)
|
|
ctag_resolve(tag_else)
|
|
scan
|
|
while parse_stmnt
|
|
nextln
|
|
loop
|
|
ctag_resolve(tag_endif)
|
|
else
|
|
ctag_resolve(tag_else)
|
|
ctag_resolve(tag_endif)
|
|
fin
|
|
if token <> FIN_TKN; return parse_err(@no_fin); fin
|
|
break
|
|
is WHILE_TKN
|
|
tag_while = ctag_new
|
|
tag_wend = ctag_new
|
|
tag_prevcnt = cont_tag
|
|
cont_tag = tag_while
|
|
tag_prevbrk = break_tag
|
|
break_tag = tag_wend
|
|
ctag_resolve(tag_while)
|
|
if !parse_expr; return FALSE; fin
|
|
emit_brfls(tag_wend)
|
|
while parse_stmnt
|
|
nextln
|
|
loop
|
|
if token <> LOOP_TKN; return parse_err(@no_loop); fin
|
|
emit_branch(tag_while)
|
|
ctag_resolve(tag_wend)
|
|
break_tag = tag_prevbrk
|
|
cont_tag = tag_prevcnt
|
|
break
|
|
is REPEAT_TKN
|
|
tag_repeat = ctag_new
|
|
tag_prevbrk = break_tag
|
|
break_tag = ctag_new
|
|
tag_prevcnt = cont_tag
|
|
cont_tag = ctag_new
|
|
ctag_resolve(tag_repeat)
|
|
scan
|
|
while parse_stmnt
|
|
nextln
|
|
loop
|
|
if token <> UNTIL_TKN; return parse_err(@no_until); fin
|
|
ctag_resolve(cont_tag)
|
|
cont_tag = tag_prevcnt
|
|
if !parse_expr; return FALSE; fin
|
|
emit_brfls(tag_repeat)
|
|
ctag_resolve(break_tag)
|
|
break_tag = tag_prevbrk
|
|
break
|
|
is FOR_TKN
|
|
stack_loop = stack_loop + 1
|
|
tag_for = ctag_new
|
|
tag_prevcnt = cont_tag
|
|
cont_tag = tag_for
|
|
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
|
|
ctag_resolve(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, 0)
|
|
else
|
|
emit_daw(addr, 0)
|
|
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_branch(tag_for)
|
|
cont_tag = tag_prevcnt
|
|
ctag_resolve(break_tag)
|
|
emit_drop
|
|
break_tag = tag_prevbrk
|
|
stack_loop = stack_loop - 1
|
|
break
|
|
is CASE_TKN
|
|
stack_loop = stack_loop + 1
|
|
tag_prevbrk = break_tag
|
|
break_tag = ctag_new
|
|
tag_choice = ctag_new
|
|
tag_of = 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)
|
|
ctag_resolve(tag_of)
|
|
while parse_stmnt
|
|
nextln
|
|
loop
|
|
tag_of = ctag_new
|
|
if prevstmnt <> BREAK_TKN // Fall through to next OF if no break
|
|
emit_branch(tag_of)
|
|
fin
|
|
ctag_resolve(tag_choice)
|
|
tag_choice = ctag_new
|
|
break
|
|
is DEFAULT_TKN
|
|
ctag_resolve(tag_of)
|
|
tag_of = 0
|
|
scan
|
|
while parse_stmnt
|
|
nextln
|
|
loop
|
|
if token <> ENDCASE_TKN; return parse_err(@bad_stmnt); fin
|
|
break
|
|
is EOL_TKN
|
|
nextln
|
|
break
|
|
otherwise
|
|
return parse_err(@bad_stmnt)
|
|
wend
|
|
loop
|
|
if (tag_of)
|
|
ctag_resolve(tag_of)
|
|
fin
|
|
ctag_resolve(break_tag)
|
|
emit_drop
|
|
break_tag = tag_prevbrk
|
|
stack_loop = stack_loop - 1
|
|
break
|
|
is BREAK_TKN
|
|
if break_tag
|
|
emit_branch(break_tag)
|
|
else
|
|
return parse_err(@bad_stmnt)
|
|
fin
|
|
break
|
|
is CONT_TKN
|
|
if cont_tag
|
|
emit_branch(cont_tag)
|
|
else
|
|
return parse_err(@bad_stmnt)
|
|
fin
|
|
break
|
|
is RETURN_TKN
|
|
if infunc
|
|
for i = 1 to stack_loop
|
|
emit_drop
|
|
next
|
|
fin
|
|
if !parse_expr
|
|
emit_const(0)
|
|
fin
|
|
emit_leave
|
|
break
|
|
is EOL_TKN
|
|
return TRUE
|
|
is ELSE_TKN
|
|
is ELSEIF_TKN
|
|
is FIN_TKN
|
|
is LOOP_TKN
|
|
is UNTIL_TKN
|
|
is NEXT_TKN
|
|
is OF_TKN
|
|
is DEFAULT_TKN
|
|
is ENDCASE_TKN
|
|
is END_TKN
|
|
is DONE_TKN
|
|
is DEF_TKN
|
|
return FALSE
|
|
is ID_TKN
|
|
saveptr = tknptr
|
|
idptr = id_lookup(tknptr, tknlen)
|
|
if !idptr; return FALSE; fin
|
|
type = idptr->idtype
|
|
addr = idptr=>idval
|
|
if type & VAR_TYPE
|
|
elem_type = type
|
|
elem_offset = 0
|
|
if scan == DOT_TKN or token == COLON_TKN
|
|
//
|
|
// Structure member offset
|
|
//
|
|
if token == DOT_TKN
|
|
elem_type = BYTE_TYPE
|
|
else
|
|
elem_type = WORD_TYPE
|
|
fin
|
|
if !parse_const(@elem_offset)
|
|
token = ID_TKN
|
|
else
|
|
scan
|
|
fin
|
|
fin
|
|
if token == SET_TKN
|
|
if !parse_expr; return parse_err(@bad_expr); fin
|
|
if type & LOCAL_TYPE
|
|
if elem_type & BYTE_TYPE
|
|
emit_slb(addr + elem_offset)
|
|
else
|
|
emit_slw(addr + elem_offset)
|
|
fin
|
|
else
|
|
if elem_type & BYTE_TYPE
|
|
emit_sab(addr, elem_offset)
|
|
else
|
|
emit_saw(addr, elem_offset)
|
|
fin
|
|
fin
|
|
break
|
|
elsif token == INC_TKN or token == DEC_TKN
|
|
if type & LOCAL_TYPE
|
|
if elem_type & BYTE_TYPE
|
|
emit_llb(addr + elem_offset)
|
|
emit_unaryop(token)
|
|
emit_slb(addr + elem_offset)
|
|
else
|
|
emit_llw(addr + elem_offset)
|
|
emit_unaryop(token)
|
|
emit_slw(addr + elem_offset)
|
|
fin
|
|
else
|
|
if elem_type & BYTE_TYPE
|
|
emit_lab(addr, elem_offset)
|
|
emit_unaryop(token)
|
|
emit_sab(addr, elem_offset)
|
|
else
|
|
emit_law(addr, elem_offset)
|
|
emit_unaryop(token)
|
|
emit_saw(addr, elem_offset)
|
|
fin
|
|
fin
|
|
break
|
|
fin
|
|
elsif type & FUNC_TYPE
|
|
if scan == EOL_TKN
|
|
emit_call(addr)
|
|
emit_drop
|
|
break
|
|
fin
|
|
fin
|
|
tknptr = saveptr
|
|
otherwise
|
|
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
|
|
elsif token == INC_TKN or token == DEC_TKN
|
|
emit_dup
|
|
if type & XBYTE_TYPE
|
|
emit_lb
|
|
emit_unaryop(token)
|
|
emit_sb
|
|
else
|
|
emit_lw
|
|
emit_unaryop(token)
|
|
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
|
|
|
|
//cout('T')
|
|
idlen = 0
|
|
size = 1
|
|
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
|
|
if token == 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
|
|
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_struc
|
|
byte strucid[16]
|
|
byte type, idlen, struclen, constsize
|
|
word size, offset, idstr
|
|
|
|
//cout('S')
|
|
struclen = 0
|
|
if scan == ID_TKN
|
|
struclen = tknlen
|
|
if struclen > 16
|
|
struclen = 16
|
|
fin
|
|
for idlen = 0 to struclen
|
|
strucid[idlen] = ^(tknptr + idlen)
|
|
next
|
|
fin
|
|
offset = 0
|
|
while nextln == BYTE_TKN or token == WORD_TKN
|
|
size = 1
|
|
if token == BYTE_TKN
|
|
type = BYTE_TYPE
|
|
else
|
|
type = WORD_TYPE
|
|
fin
|
|
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
|
|
repeat
|
|
idlen = 0;
|
|
if token == ID_TKN
|
|
idstr = 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 idlen
|
|
idconst_add(idstr, idlen, offset)
|
|
fin
|
|
offset = offset + size
|
|
until token <> COMMA_TKN
|
|
if token <> EOL_TKN; return FALSE; fin
|
|
loop
|
|
if struclen
|
|
idconst_add(@strucid, struclen, offset)
|
|
fin
|
|
return token == END_TKN
|
|
end
|
|
def parse_vars
|
|
byte idlen, type, size
|
|
word value, idptr
|
|
|
|
//cout('V')
|
|
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)
|
|
break
|
|
is STRUC_TKN
|
|
if !parse_struc; parse_err(@bad_struc); fin
|
|
break
|
|
is BYTE_TKN
|
|
is WORD_TKN
|
|
if token == BYTE_TKN
|
|
type = BYTE_TYPE
|
|
else
|
|
type = WORD_TYPE
|
|
fin
|
|
repeat
|
|
if !parse_var(type)
|
|
return FALSE
|
|
fin
|
|
until token <> COMMA_TKN
|
|
break
|
|
is PREDEF_TKN
|
|
repeat
|
|
if scan == ID_TKN
|
|
idfunc_add(tknptr, tknlen, ctag_new)
|
|
else
|
|
return parse_err(@bad_decl)
|
|
fin
|
|
until scan <> COMMA_TKN
|
|
break
|
|
is EOL_TKN
|
|
break
|
|
otherwise
|
|
return FALSE
|
|
wend
|
|
return TRUE
|
|
end
|
|
def parse_defs
|
|
byte cfnparms
|
|
word func_tag, idptr
|
|
|
|
if token == DEF_TKN
|
|
//cout('D')
|
|
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
|
|
ctag_resolve(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(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
|
|
fin
|
|
return TRUE
|
|
elsif token == EOL_TKN
|
|
return TRUE
|
|
fin
|
|
return FALSE
|
|
end
|
|
def parse_module
|
|
idglobal_init
|
|
idlocal_init
|
|
if nextln
|
|
while parse_vars
|
|
nextln
|
|
loop
|
|
while parse_defs
|
|
nextln
|
|
loop
|
|
//cout('I')
|
|
framesize = 0
|
|
entrypoint = codeptr
|
|
emit_enter(0)
|
|
prevstmnt = 0
|
|
if token <> DONE_TKN
|
|
while parse_stmnt
|
|
nextln
|
|
loop
|
|
fin
|
|
//cout('!')
|
|
if prevstmnt <> RETURN_TKN
|
|
emit_const(0)
|
|
emit_leave
|
|
fin
|
|
if not parserr
|
|
//dumpsym(idglobal_tbl, globals)
|
|
//prstr(@entrypt_str)
|
|
//prword(entrypoint)
|
|
prstr(@bytes_compiled_str)
|
|
prword(codeptr - codebuff)
|
|
crout
|
|
keyin()
|
|
fin
|
|
return not parserr
|
|
fin
|
|
return FALSE
|
|
end
|
|
//
|
|
// Close all files
|
|
//
|
|
^$BFD8 = 0
|
|
close(0)
|
|
//
|
|
// Set memory bitmap
|
|
//
|
|
memset($BF58, 0, 24)
|
|
^$BF58 = $CF
|
|
^$BF6F = $01
|
|
//
|
|
// Init editor
|
|
//
|
|
if !(^machid & $80)
|
|
flags = uppercase | shiftlock
|
|
keyin = @keyin2
|
|
else
|
|
keyin = @keyin2e
|
|
fin
|
|
if signature == $EEEE
|
|
inittxtbuf
|
|
if argbuff
|
|
strcpy(@txtfile, @argbuff)
|
|
prstr(@txtfile)
|
|
numlines = 0
|
|
readtxt(@txtfile)
|
|
fin
|
|
signature = 0
|
|
else
|
|
cmdmode(FALSE)
|
|
fin
|
|
curschr = '+'
|
|
flags = flags | insmode
|
|
drawscrn(scrntop, scrnleft)
|
|
curson
|
|
editmode
|
|
done
|