         3*
* El Cheapo Assembler
*
* A simple and straightforward assembler by
* one s. judd 1998
*

         REL
*        DSK '@0:cheapo'

TEXT     EXT
TEXTEND  EXT
CODEFLAG EXT
PRINTNUM EXT
STROUT   EXT
GETERR   EXT
GETERR2  EXT
OPENERR  EXT
SWAPBANK EXT

LINK     EXT
LINKID   EXT
                          ; ORG $1000

VARTAB   = $C000
FILEBUF  = $00BF00
BUFFER   = $0200          ;Text buffer

EXTTAB   = $03C000

POINT    = $FE
VARPNT   = $FC
TEXTP    = $F9
CODEP    = $5D

COD      = $50
RELP     = $57
EXTP     = $5A

TEMPY    = $C0
TEMP     = $C1
TEMPX    = $C2

FAC1     = $60
FAC2     = $69

SETLFS   = $FFBA
SETNAM   = $FFBD
OPEN     = $FFC0
CLOSE    = $FFC3
CHKIN    = $FFC6
CHKOUT   = $FFC9
CLRCHN   = $FFCC
CHROUT   = $FFD2
LOAD     = $FFD5
STOP     = $FFE1
GETIN    = $FFE4
CLALL    = $FFE7

* Errors

BADLINE  = 1
BADOP    = 2
BADMODE  = 3
BADNUM   = 4
BADVAR   = 5
BADVMEM  = 6              ;Too many variables
SYNERR   = 7
BADFIX   = 8
PHASERR  = 9
DUPVAR   = 10
BADBRAN  = 11
BADARG   = 12
ILVAR    = 13
NOQUOTE  = 14
DISKERR  = 15
PUTON    = 16
PUTMEM   = 17
NOTREL   = 18
REL1     = 19
EXTERR   = 20
LINKERR  = $FF

ASSEMBLE ENT
INIT     
         LDA #1
         STA PASS

         LDA $BA
         CMP #8
         BCS :C1
         LDA #8
:C1      STA LASTDEV

         LDA #00
         STA $9D
         STA ERROR
         STA VARTAB
         STA DFBFLAG
         STA CODEFLAG
*         STA PRFLAG
         STA EXTBLAH
         LDA #$FF
         STA SREG
         STA DOFLAG
         STA FILEOPEN
         STA ATEOF
         STA RELFLAG

         LDA #4           ;File 4 = output
         LDX #3           ;screen
         JSR SETLFS
         JSR OPEN

         LDA #<EXTTAB
         STA EXTP
         LDA #>EXTTAB
         STA EXTP+1
         LDA #^EXTTAB
         STA EXTP+2

INIT2    
         LDX #2
:L0      LDA COD,X
         STA RELP,X
         STA CODEEND,X
         DEX
         BPL :L0

         LDA #00          ;Default $C000
         STA CURLINE
         STA CURLINE+1
         STA NBYTES
         STA NBYTES+1
         STA CODEP
         STA CODEP+2
         STA DORG
         STA DORG+2
         STA REGFLAG
         STA LINKFLAG
         LDA #$C0
         STA CODEP+1
         STA DORG+1
         STA $9D          ;Kernal messages on

         LDA #00          ;Always $020000
         STA COD
         STA COD+1
         LDA #02
         STA COD+2

         LDA #01
         STA XLEN
         STA MLEN
         LDA #$FF
         STA SREG

         LDA TEXT
         STA TEXTP
         LDA TEXT+1
         STA TEXTP+1
         LDA TEXT+2
         STA TEXTP+2

         LDA #<VARTAB
         STA LASTGLOB
         LDA #>VARTAB
         STA LASTGLOB+1

NEWREAD  STZ NEWFILE

READ     LDA $91          ;STOP key
         BMI :C00
         JMP FIXFILES
:C00     CMP DEBOUNCE
         BEQ :C0
         STA DEBOUNCE
         CMP #$FD         ;_ key
         BNE :C0
         EOR PRFLAG
         STA PRFLAG
:C0      
         INC CURLINE
         BNE :C1
         INC CURLINE+1
:C1      LDA PASS
         BNE :CONT
         JSR PRADDR
:CONT    LDY #00
         LDA DOFLAG
         BNE :CONT2
         JSR DOREME       ;C set means ELSE or FIN found.
         BCS :NEXT
         BCC :PRLINE
:CONT2   
         STY NOUT
         LDA [TEXTP],Y
         CMP #224
         BEQ :GETOP
         AND #$7F
         BEQ :CHECKF
         CMP #32
         BEQ :GETOP
         CMP #'*'
         BEQ :COM
         CMP #';'
         BEQ :COM
         CMP #13
         BEQ :CR          ;C is *set*
         JSR ADDVAR
         LDX ERROR
         BNE PRERR
:GETOP   JSR NEXTCHAR
         BCC :PRLINE
         JSR GETOP
         LDX ERROR
         BNE PRERR
         LDA NEWFILE      ;PUT opened a file
         BNE NEWREAD
         LDA LINKFLAG
         BEQ :NEXT
         JMP EXIT
:NEXT    JSR NEXTCHAR
         BCS :ERR         ;Should be no more valid chars
         BNE :PRLINE
:CHECKF  LDA FILEOPEN
         BNE :DONE
         JSR FILEDONE
         DEC FILEOPEN
         BNE :NEXT
:PRLINE  LDA PASS
         BNE :ADD
         JSR PRLINE
:CLC     CLC
:ADD     TYA
         ADC TEXTP        ;And advance...
         STA TEXTP
         BCC :JR
         INC TEXTP+1
         BNE :JR
         INC TEXTP+2
:JR      JMP READ
:DONE    DEC PASS
         BMI EXIT
         JMP INIT2

:CR      INY
         CLC
         BCC :PRLINE

:COM     JSR NEXTLINE
         BCC :PRLINE

:ERR     LDX #BADLINE     ;Garbage in the line
PRERR    
         TXA
         BMI :JMP         ;e.g. linker errors
         ASL
         TAX
         LDA ERRTAB,X
         STA POINT
         LDA ERRTAB+1,X
         STA POINT+1

         JSR CLRCHN

         JSR STROUT
         DFB 13
         TXT 'Line ',00
         LDA CURLINE      ;Line number
         LDX CURLINE+1
         LDY #10          ;Base
         JSR PRINTNUM
         JSR STROUT
         TXT ': ',00
         LDY #00
:L1      LDA [TEXTP],Y    ;Line
         JSR CHROUT
         CMP #13
         BEQ :C1
         INY              ;Horribly mangled lines won't
         BNE :L1          ;error
:C1      JSR CHROUT       ;Extra CR
         LDY #00
:L2      LDA (POINT),Y    ;Error
         JSR CHROUT
         INY
         CMP #13
         BNE :L2

:JMP     JMP FIXFILES

EXIT     
         LDA RELFLAG
         BNE :CONT
         INC PASS
         JSR RELOUT
         JSR ENTOUT
         JSR EXTOUT
         LDA LINKID
         JSR CODEOUT
         LDA LINKID+1
         JSR CODEOUT

*         LDA NBYTES   ;works normal and link
*         STA CODEEND
*         LDA NBYTES+1
*         STA CODEEND+1
*         LDA #$02
*         STA CODEEND+2

:CONT    JSR FIXFILES
         JSR STROUT
         DFB 13
         TXT 'assembly sucessful -- ',00
         LDA NBYTES
         LDX NBYTES+1
         LDY #10
         JSR PRINTNUM
         JSR STROUT
         TXT ' bytes',0d
         TXT '$',00
         LDA DORG
         LDX DORG+1
         LDY #16
         JSR PRINTNUM
         JSR STROUT
         TXT '-$',00
         LDA DORG
         CLC
         ADC NBYTES
         PHA
         LDA DORG+1
         ADC NBYTES+1
         TAX
         PLA
         LDY #16
         JSR PRINTNUM

         LDA NBYTES
         ORA NBYTES+1     ;0 check
         STA CODEFLAG     ;Successful assemble
         RTS

         TXT 'hola d00d'

ASSERR   ENT
ERROR    DFB 00
PASS     DFB 00
NOUT     DFB 00           ;Number of bytes outputted
SREG     DFB $FF          ;Status register
LINKFLAG DFB 00
REGFLAG  DFB 00
XLEN     DFB 01           ;length of LDX #
MLEN     DFB 01           ;length of LDA #

ASSLINE  ENT
CURLINE  DA 00            ;Current line

DORG     ENT
         DS 3             ;"Master" ORG

CODELEN  ENT
NBYTES   DA 00

CODEEND  ENT
         DLA 00

LASTDEV  ENT
         DFB 08           ;Last device (input)
PRFLAG   DFB 00           ;To print, or not to print...
DOFLAG   DFB $FF          ;Do be do be do (0=OFF)
RELFLAG  ENT
         DFB $FF          ;0 -> REL assembly
FILEOPEN DFB $FF
ATEOF    DFB $FF
SETSTUFF DFB $FF
LASTLINE DA 00

DEBOUNCE DFB 00

FILEDONE 
         LDA OLDTEXTP
         STA TEXTP
         LDA OLDTEXTP+1
         STA TEXTP+1
         LDA OLDTEXTP+2
         STA TEXTP+2
         LDA OLDCURLINE
         STA CURLINE
         LDA OLDCURLINE+1
         STA CURLINE+1
         LDY #00
         RTS

FIXFILES 
         LDA #00
         STA $C6
*         JSR CLOSEFILE
         LDA #15
         JSR CLOSE
         JSR CLRCHN
CLOSEPRT LDA #4
         JSR CLOSE
         LDA LASTDEV
         STA $BA
         RTS
* JMP CLALL

*
* Print out the just-assembled line
*
PRLINE   
         STY TEMPY
         LDY PRFLAG
         BNE :DONE
* LDY #00
         LDX #4
         JSR CHKOUT
         LDA #32
         LDX NOUT         ;Line up spaces
         CPX #4
         BEQ :CONT
:L2      JSR CHROUT
         JSR CHROUT
         JSR CHROUT
         INX
         CPX #4
         BCC :L2
:CONT    
         LDA [TEXTP],Y
         CMP #';'
         BEQ :REST
         CMP #'*'
         BEQ :REST
         LDX #9           ;Columns
         JSR :PRINT
         BCC :DONE

         LDX #4
         JSR :PRINT       ;Print opcode
         BCC :DONE

:REST    LDA [TEXTP],Y    ;Print rest of line
         JSR CHROUT
         INY
         CPY TEMPY
         BCC :REST

         DO 0
:DONE    LDA $0277        ;GETIN may fetch from file
         CMP #'_'
         BNE :DONE2
         EOR PRFLAG
         STA PRFLAG
         FIN
:DONE    LDY TEMPY
         RTS

:PRINT   
:LOOP    LDA [TEXTP],Y    ;Print label
         JSR CHROUT
         INY
         DEX
         AND #$7F
         CMP #32
         BCC :RTS         ;Space or CR terminates
         BNE :LOOP
:LOOP2   DEX              ;Move to next column
         BMI :RTS
         JSR CHROUT
         SEC
         BCS :LOOP2
:RTS     RTS              ;C clear -> CR

*
* CODEOUT
*
* This little guy outputs the byte in A into the code
* pointed to by CODEP, and increments CODEP.
*
* CODEP = code address, COD = physical address
*
* X Y and A are preserved (but TEMPy is hosed :)
*
CODEOUT  
         STY TEMPY
         INC NBYTES
         BNE :C0
         INC NBYTES+1
:C0      INC CODEP
         BNE :C1
         INC CODEP+1
:C1      LDY PASS
         BNE :INC2        ;for RELP, CODEEND
         PHA
         LDA NOUT
         CMP #4
         BCC :CONT
         STY NOUT         ;New line
         LDY PRFLAG
         BNE :CONT
         LDA #13
         JSR CHROUT
         JSR PRADDR
:CONT    PLA
                          ; STA (COD),Y ;.Y = 0
         STA [COD]
         LDY PRFLAG
         BNE :INC
         PHA
         JSR PRHEX
         LDA #32
         JSR CHROUT
         PLA
:INC     INC NOUT
:INC2    INC COD
         BNE :DONE
         INC COD+1        ;No more than 64k of code
:DONE    LDY TEMPY
         RTS

PRHEX    ENT
         PHA
         LSR
         LSR
         LSR
         LSR
         JSR :PR
         PLA
         AND #$0F
:PR      CMP #10
         SED
         ADC #00
         CLD
         ADC #48
         JMP CHROUT

*
* Get a character and convert to lower-case
*

GETCHAR  LDA [TEXTP],Y
         AND #$7F
         CMP #96
         BCC :SKIP
         AND #$5F         ;Convert to lower-case
:SKIP    RTS

*
* TERMCHAR
*
* Check for a character which can legally terminate
* a variable.
*
TERMCHAR 
         LDA [TEXTP],Y
         CMP #224
         BEQ :DONE
         AND #$7F
         CMP #13
         BEQ :DONE
         CMP #' '
         BEQ :DONE
         CMP #','         ;Indexed mode
         BEQ :DONE
         CMP #')'         ;zp
         BEQ :DONE
         CMP #']'         ;zp long
         BEQ :DONE
         CMP #'+'
         BEQ :DONE
         CMP #'-'
:DONE    RTS

*
* GETOP
*
* GETOP is the opcode parser.  It just searches the
* opcode table and calls the appropriate parse routine.
*
* The opcodes must be stored in alphabetical order,
* 5 bytes per entry:
*
*    3 byte mnemonic, 2 byte address
*

MNEM     DFB 0,0,0
JUMPADR  DA 0
OPCODE   DFB 00

GETOP    
         LDA [TEXTP],Y
         CMP #'='
         BNE :STAR
         INY
         JMP IEQU
:STAR    CMP #'*'
         BNE :NOPE
         JMP IORG2

:NOPE    LDA #<OPTAB
         STA POINT
         LDA #>OPTAB
         STA POINT+1

         LDX #$00
:L1      JSR GETCHAR
         STA MNEM,X
         INY
         INX
         CPX #3
         BNE :L1
         STY TEMPY

         LDY #$00
         LDX #00
:LOOP    LDA MNEM,X       ;Get character
:LOOP2   CMP (POINT),Y    ;See if matches a valid keyword
         BCC :ERR         ;If less than, then we have a
                          ;syntax error
         BEQ :CONT        ;A-ha!  A match!
         LDA POINT        ;Otherwise, move forwards
         ADC #5           ;carry is set
         STA POINT
         BCC :LOOP
         INC POINT+1
         BNE :LOOP
:ERR     LDA #BADOP       ;Bad opcode
         STA ERROR
         RTS
:CONT                     ;A successful match
         INX              ;match next character
         INY
         CPX #3
         BNE :LOOP
         LDA (POINT),Y    ;Otherwise, get address
         STA JUMPADR
         INY
         LDA (POINT),Y
         STA JUMPADR+1
         INY
         LDA (POINT),Y    ;Opcode
         LDY TEMPY
         JMP (JUMPADR)    ;Off we go!

*
* Output opcode and argument
*
* On entry, .A = opcode and TEMP=arg length
*
PCODE    
         JSR CODEOUT
VAROUT   
         LDA TEMP
         BEQ RTS1
         LDA PASS
         BNE :CONT
         LDA RELFLAG
         BNE :CONT
         LDA ISLAB
         BEQ :C1
         JSR ADDREL
:C1      LDA ISEXT
         BEQ :CONT
         JSR ADDEXT
:CONT    
         LDX ARGOFF
:LOOP    LDA ARG1,X
         JSR CODEOUT
         INX
         DEC TEMP
         BNE :LOOP
RTS1     RTS

PRADDR   
         LDA PRFLAG
         BNE RTS1
         LDA FILEOPEN
         BNE :C1
         LDA #'>'
         JSR CHROUT
:C1      LDA #':'
         JSR CHROUT
         LDA CODEP+1
         JSR PRHEX
         LDA CODEP
         JSR PRHEX
         LDA #32
         JMP CHROUT

* REL routines

ADDREL                    ;Argument to be fixed up
         LDA NUMRELS
         BEQ SRTS         ;# of offsets
         LDA TEMP         ;len
         CMP #2
         BNE :SPECIAL
         LDA ARGOFF
         BEQ :NORMAL
:SPECIAL LDA NUMRELS
         ORA #$80
         STA NUMRELS
         JSR :NORMAL
         LDA ARG1
         JSR STARELP
         LDA ARG1+1
         JSR STARELP
         LDA TEMP         ;len
         ASL
         ASL
         ASL
         ASL
         ORA ARGOFF
         JMP STARELP
:NORMAL  
         LDA NBYTES
         JSR STARELP
         LDA NBYTES+1
         JSR STARELP
         LDA NUMRELS
STARELP  DFB $87,RELP     ;sta [relp]
         INC RELP
         BNE SRTS
         INC RELP+1
         BNE SRTS
         INC RELP+2
SRTS     RTS

RELOUT                    ;Output REL label table
         LDA RELP
         SEC
         SBC COD
         STA FAC1         ;temp
         LDA RELP+1
         SBC COD+1
         STA FAC1+1       ;length
         ORA FAC1
         BEQ :ZIP
:L1      DFB $A7,COD
         JSR CODEOUT
         LDA COD
         CMP RELP
         LDA COD+1
         SBC RELP+1
         BCC :L1
:ZIP     LDA FAC1
         JSR CODEOUT
         LDA FAC1+1
         JMP CODEOUT

ENTOUT   
         LDA COD
         STA FAC1
         LDA COD+1
         STA FAC1+1
         LDA #<VARTAB
         STA VARPNT
         LDA #>VARTAB
         STA VARPNT+1
:L1      LDA VARPNT
         STA POINT
         LDA VARPNT+1
         STA POINT+1
         LDY #00
:L2      LDA (VARPNT),Y
         BEQ :DONE
         BMI :CHK
         INY
         BNE :L2
:CHK     STY TEMP
         CMP #$E0
         BNE :NEXT
         LDY #00
:L3      LDA (VARPNT),Y
         INY
         CPY TEMP
         BEQ :C1
         JSR CODEOUT
         JMP :L3
:C1      ORA #$80         ;DCI
         JSR CODEOUT
         INY
         LDA (VARPNT),Y
         JSR CODEOUT      ;value
         INY
         LDA (VARPNT),Y
         JSR CODEOUT
:NEXT    LDA TEMP
         CLC
         ADC #4
         ADC VARPNT
         STA VARPNT
         BCC :L1
         INC VARPNT+1
         BNE :L1
:DONE    LDA COD
         SEC
         SBC FAC1         ;block len
         PHP
         JSR CODEOUT
         PLP
         LDA COD+1
         SBC FAC1+1
         JMP CODEOUT

EXTBLAH  DFB 00

ADDEXT   
         LDA PASS
         BNE :RTS
         STY TEMPY
         LDA LASTVAR
         STA POINT
         LDA LASTVAR+1
         STA POINT+1
         LDY #00
:LOOP    LDA (POINT),Y    ;name (DCI)
         BMI :C0
         DFB $97,EXTP     ;sta [extp],y
         INY
         BNE :LOOP
:C0      DEY
         LDA (POINT),Y
         ORA #$80
         DFB $97,EXTP
         INY
         LDA NBYTES       ;offset
         CLC
         ADC EXTBLAH      ;LDA EXT1+... outputs EXT1 before
         DFB $97,EXTP     ;opcode, so inc nbytes
         INY
         LDA NBYTES+1
         ADC #00
         DFB $97,EXTP
         INY
         LDA EXTCODE      ;16-bit add/sub
         BMI :STA         ;add/sub does error check
         LDA TEMP         ;size
         ASL
         ASL
         ASL
         ASL
         ORA ARGOFF
:STA     DFB $97,EXTP
         TYA
         SEC              ;+1
         ADC EXTP
         STA EXTP
         BCC :C1
         INC EXTP+1
         BNE :C1
         INC EXTP+2
:C1      LDY TEMPY
:RTS     RTS

EXTOUT   
         LDA EXTP
         SEC
         SBC #<EXTTAB
         STA FAC1
         STA FAC2
         LDA EXTP+1
         SBC #>EXTTAB
         STA FAC1+1       ;len
         STA FAC2+1

         LDA #<EXTTAB
         STA EXTP
         LDA #>EXTTAB
         STA EXTP+1       ;assume bank ok

:LOOP    LDA FAC2
         BNE :DEC
         DEC FAC2+1
         BMI :EXIT
:DEC     DEC FAC2
         DFB $A7,EXTP
         JSR CODEOUT
         INC EXTP
         BNE :LOOP
         INC EXTP+1
         BNE :LOOP
:EXIT    LDA FAC1
         JSR CODEOUT
         LDA FAC1+1
         JMP CODEOUT

*-------------------------------
*
* Variable processing routines
*
*-------------------------------

*
* ROMOUT and ROMIN -- Vartab can lie under roms
*

ROMOUT   PHA
         SEI
         LDA #$38
         STA $01
         PLA
         RTS

ROMIN2   LDY TEMPY
ROMIN    PHA
         LDA #$36
         STA $01
         PLA
         CLI
         RTS

*
* ADDVAR is used to add variables to the variable table.
* Variables have the form
*
*    name type lo med hi
*
* where type has high bit set.  The list is
* terminated with a null byte.  Current types are:
*
*   $FF  Normal label
*   $FE  EQUated label
*   $FD  Label EQUated to a normal label (for REL)
*   $E1  EXTernal label
*   $E0  ENTry point
*

LOCFLAG  DFB 00
LASTGLOB DA 00            ;pointer

ADDVAR   
         JSR ROMOUT
         LDA PASS
         BEQ :PASS2
         JSR FINDVAR
         BCS :ERROR
         JSR COPYVAR
         BCC :ERROR
         STY TEMPY
         LDA #$FF
         LDY #$00
         STA (VARPNT),Y
:LOOP    LDA CODEP,Y
         INY
         STA (VARPNT),Y
         CPY #3
         BNE :LOOP
         INY
         LDA #00
         STA (VARPNT),Y   ;end of variables
         BEQ :SAVELOC
:ERROR   LDA ERROR
         BNE :RTS         ;Out of mem
         LDA #DUPVAR
         DFB $2C
:ERR2    LDA #PHASERR
         DFB $2C
:ERR3    LDA #BADVAR
         STA ERROR
:RTS     JMP ROMIN

:PASS2                    ;Compare with earlier value to
                          ;check for phase errors
         JSR FINDVAR      ;FINDVAR sets LOCFLAG
         BCC :ERR3
         STY TEMPY
         LDY #00
         LDA (VARPNT),Y
         CMP #$FF         ;Only care about labels
         BEQ :CHECK       ;not EQUates etc.
         CMP #$E0
         BNE :LDY
:CHECK   LDY #3
:L2      LDA CODEP-1,Y
         CMP (VARPNT),Y
         BNE :ERR2
         DEY
         BNE :L2
:SAVELOC 
         LDA LOCFLAG      ;If global, then save address
         BEQ :LDY         ;of next var.
         LDA VARPNT
         CLC
         ADC #4
         STA LASTGLOB
         LDA VARPNT+1
         ADC #00
         STA LASTGLOB+1
:LDY     JMP ROMIN2

COPYVAR  
         LDX #00
         LDY TEMPY
         LDA [TEXTP],Y
         CMP #':'         ;Must start with : or letter
         BEQ :STA
:L1      LDA [TEXTP],Y
         CMP #224         ;tab
         BEQ :DONE
         AND #$7F
         CMP #96          ;lcase
         BCC :C1
         SBC #32
:C1      CMP #32
         BEQ :DONE        ;Only space or CR terminates var
         CMP #$0D
         BEQ :DONE
         CMP #48
         BCC :ERROR
         CMP #58
         BCC :STA
:CMP     CMP #65
         BCC :ERROR
* AND #$5F ;Valid chars are 0-9, a-z ] [ etc.
:STA     STA (VARPNT,X)
         INY
         INC VARPNT
         BNE :L1
         INC VARPNT+1
         BNE :L1
         LDA #BADVMEM     ;WAY too many vars
         DFB $2C
:ERROR   LDA #ILVAR
         STA ERROR
         CLC
         RTS
:DONE    SEC
         RTS

*
* Find a variable in the variable list.  If found,
*
* On exit:
*    C set -> Variable found, VARPNT points to last char
*    C clr -> Variable not found, VARPNT points to end
*             of variable list.
*
* In both cases, .Y is advanced to the character
* immediately following the variable, and TEMPY
* contains the first character of the variable.
* This way, expressions like BLAH+12 may be
* correctly evaluated even if BLAH has not yet
* been defined.
*
* The list is terminated with a null byte.
*

FINDVAR  
         LDA #<VARTAB     ;Start at beginning of list
         STA VARPNT
         LDA #>VARTAB
         STA VARPNT+1
         STY TEMPY
         LDA [TEXTP],Y
         SEC
         SBC #':'
         STA LOCFLAG
         BNE :GLOBAL
         LDA LASTGLOB     ;Start search from last global
         STA VARPNT
         LDA LASTGLOB+1
         STA VARPNT+1
:GLOBAL  
         LDX #00
:LOOP2   LDY TEMPY
         LDA VARPNT
         STA LASTVAR
         LDA VARPNT+1
         STA LASTVAR+1
         LDA (VARPNT,X)
         BEQ :EXIT
         CMP #':'         ;Local variable check
         BEQ :LOOP
         LDA LOCFLAG      ;Exit if next global reached
         BEQ :EXIT
:LOOP    LDA (VARPNT,X)
         BMI :MATCH       ;matched all chars
         CMP [TEXTP],Y
         BEQ :NEXT
         CMP #65          ;Upper-case perhaps?
         BCC :NEXTVAR
         LDA [TEXTP],Y
         AND #$5F         ;lower-case
         CMP (VARPNT,X)
         BEQ :NEXT        ;minor kludge

:NEXTVAR LDY #$FF         ;otherwise advance to next variable
:L3      INY
         LDA (VARPNT),Y
         BPL :L3
         TYA
         CLC
         ADC #4           ;Hopefully no 250-character
         ADC VARPNT       ;variables :)
         STA VARPNT
         BCC :LOOP2
         INC VARPNT+1
         BCS :LOOP2

:NEXT    INY              ;Advance to next char
         INC VARPNT
         BNE :LOOP
         INC VARPNT+1
         BNE :LOOP

:MATCH                    ;Make sure really at the
         JSR TERMCHAR     ;last char
         BNE :NEXTVAR
         SEC              ;Thar she is
         RTS

:EXIT    JSR TERMCHAR     ;Advance to next char
         BEQ :CLC
         INY
         BNE :EXIT
:CLC     CLC
         RTS

*
* NEXTCHAR: Advance to next relevant character in the
* text.  Either skips space or advances to the next line.
*
* Strategy: advance through the text until a non-space
* character is found.  If character is 00 then we are at
* EOF.  If char is ; then advance to end of line.  If character
* is CHR$(13) then add one to pointer and exit.
*
* On entry:
*    Y is set to the current position within TEXTP
*
* On exit:
*       Z set -> At EOF
*       C clear -> At end of line
*       Y contains index into next relevant character
*
NEXTCHAR ENT

:LOOP    LDA [TEXTP],Y
         CMP #224
         BEQ :INY
         AND #$7F
         CMP #32
         BNE :OUT
:INY     INY
         BNE :LOOP
:OUT     
         CMP #00
         BEQ :CLC
:COM     CMP #';'
         BEQ NEXTLINE
:NEXT    CMP #13          ;If CHR$(13) then advance
         BEQ :INY2
         SEC
         RTS
:INY2    INY
:CLC     CLC
         TAX              ;Set Z correctly
         RTS

* Move to the end of the line + 1

NEXTLINE INY
         LDA [TEXTP],Y
         AND #$7F
         CMP #13
         BNE NEXTLINE
         CLC
         INY              ;Better not be zero!
         RTS

*
* READARG
*
* READARG parses the operand.  It first checks for any
* leading character, and if it doesn't find one assumes
* that the data is an absolute address or variable.
*
* Basic mathematical operations (+ - / *) are supported
* in a first-come first-serve basis.
*
* Argument types:
*   0 = no arg   1 = immediate  2 = absolute
*   3 = abs,x    4 = abs,y      5 = abs,s
*   6 = abs,abs
*   7 = (zp)     8 = (zp),y     9 = (zp,x)
*  10 = (zp,s),y 11= [zp]      12 = [zp],y
*

ARGLEN   DFB 00
ARGOFF   DFB 00           ;Argument offset
ARGTYPE  DFB 00           ;Argument type (abs, zp, etc.)
ARG1     DFB 0,0,0,0      ;Main argument
         DFB 0,0          ;padding for PCODE
ARG2     DFB 0,0,0,0      ;MVN/MVP arguments
PREFIX   DFB 00           ;Prefix char < > ! ^

READARG  
         LDA #00
         STA ARGOFF
         STA ARGTYPE
         STA ARGLEN
         STA ISENT
         STA ISEXT
         STA ISLAB
         STA NOEXT
         STA EXTCODE
         STA NDIGITS      ;Numeric flag

         JSR NEXTCHAR
         BCC :DEY
* LDA (TEXTP),Y
         CMP #'#'         ;Is it immediate mode?
         BEQ IMMED
         CMP #'('
         BEQ :ZP
         CMP #'['
         BEQ :ZPLONG
         CMP #'a'         ;Check for accumulator
         BEQ :CHECK
         CMP #'A'
         BNE ADDRESS
:CHECK   STY TEMPY
         INY
         JSR NEXTCHAR
         BCS ADDR2
:DEY     DEY              ;Y at EOL+1
:EMPTY   
         RTS
:ZP      JMP ZP
:ZPLONG  JMP ZPLONG

IMMED2   DEY              ;Used to fake out DFB
IMMED    LDA #01
         STA ARGTYPE
         STA ARGLEN
         INY
         JSR GETARG
         LDA PREFIX
         BEQ :RTS
         CMP #'>'
         BEQ :S1
         CMP #'^'
         BEQ :S2
         CMP #'<'
         BEQ :RTS
         LDX #BADFIX
         STX ERROR
:RTS     RTS
:S1      LDA #1
         DFB $2C
:S2      LDA #2
         STA ARGOFF
         RTS

ADDR2    LDY TEMPY
ADDRESS  
         LDA DFBFLAG
         BNE IMMED2
         LDA #2
         STA ARGTYPE
         LDA #1
         STA ARGLEN       ;Assume ZP
         JSR GETARG
         JSR CHKCOM       ;Check for ,
         BNE :DONE
         LDA DFBFLAG      ;DFB handles commas special
         BNE :DONE
         JSR COMX         ;Check for ,X
         BEQ :INC1
         JSR COMY
         BEQ :INC2
         JSR COMS
         BEQ :INC3
         INY
         JSR ARG2ARG
         JSR GETARG       ;MVP and MVN perhaps
         DEY              ;For INY below
         INC ARGTYPE
:INC3    INC ARGTYPE
:INC2    INC ARGTYPE
:INC1    INC ARGTYPE
         INY
:DONE    
CALCLEN  LDX #1           ;len
         LDA EXTCODE
         BMI :INX         ;force 16-bits for +/-EXT
         LDA PREFIX
         BNE :FIX
         LDA ISEXT
         BNE :INX         ;force 16-bits for non-prefixed EXT
         LDA ARG1+2       ;8-bits if $0000xx
         ORA ARG1+1
         BEQ :C2
         INX
         LDA ARG1+2       ;16-bits if $00xxxx or
         BEQ :C2          ;in same bank
         CMP CODEP+2
         BEQ :C2
:INX     INX
:C2      STX ARGLEN
         LDA NDIGITS      ;check for long numerics like $001234
         BEQ :RTS
         LSR              ;1-2 digits per byte
         ADC #00
         CMP ARGLEN       ;if numeric and ndigits > arg len
         BCC :RTS
         STA ARGLEN
:RTS     RTS
:TAB     TXT '<!>'
:FIX     LDX #3
:L1      CMP :TAB-1,X
         BEQ :C2
         DEX
         BNE :L1
         LDX #BADFIX
         STX ERROR
         RTS

ZPLONG   INC NOEXT
         LDA #11
         STA ARGTYPE
         LDA #1
         STA ARGLEN
         INY
         JSR GETARG
         JSR CHKSQR
         BNE :ERR
         INY
         JSR CHKCOM
         BNE :DONE
         JSR COMY
         BNE :ERR
         INC ARGTYPE
         INY
:DONE    JMP CALCLEN
:ERR     JMP LINERR

ZP       LDA #7
         STA ARGTYPE
         LDA #1
         STA ARGLEN
         INC NOEXT
         INY
         JSR GETARG
         JSR CHKPAR
         BNE :C1
:CHECKY  INY
         JSR CHKCOM
         BNE :DONE
         JSR COMY         ;(zp),y ?
         BNE :ERR
         INC ARGTYPE
:INY     INY
:DONE    JMP CALCLEN

:C1      LDA #9
         STA ARGTYPE
         JSR CHKCOM       ;(zp,.) instructions
         BNE :ERR
         JSR COMX
         BNE :COMS
         INY
         JSR CHKPAR       ;(zp,x)
         BEQ :INY
:ERR     JMP LINERR

:COMS    JSR COMS
         BNE :ERR
         INC ARGTYPE      ;(zp,s),y
         INY
         JSR CHKPAR
         BNE :ERR
         BEQ :CHECKY

LINERR   LDA #SYNERR
         STA ERROR
         RTS

CHKSQR   LDA #']'
         DFB $2C
CHKPAR   LDA #')'
         DFB $2C
CHKCOM   LDA #','
         CMP [TEXTP],Y
         RTS

COMX     INY
         LDA [TEXTP],Y
         AND #$5F
         CMP #'x'
         BEQ :DONE
         DEY
         LDA #$FF
:DONE    RTS

COMY     INY
         LDA [TEXTP],Y
         AND #$5F
         CMP #'y'
         BEQ :DONE
         DEY
         LDA #$FF
:DONE    RTS

COMS     INY
         LDA [TEXTP],Y
         CMP #'s'
         BEQ :DONE
         CMP #'S'
         BEQ :DONE
         DEY
         LDA #$FF
:DONE    RTS

ARG2ARG  LDX #2
:LOOP    LDA ARG1,X
         STA ARG2,X
         DEX
         BPL :LOOP
         RTS

ARG1ARG  LDX #2
:LOOP    LDA ARG2,X
         STA ARG1,X
         DEX
         BPL :LOOP
         RTS

* Adjust characters within quotes
*   " -> space=160, caps=192-223
*   ' -> space=32, caps=96-127

FIXCHAR  
         LDX TEMP
         LDA [TEXTP],Y
         BPL :POS
         CPX #34
         BEQ :EXIT
         CMP #160
         BEQ :AND
         CMP #224
         BCS :EXIT
         CMP #192
         BCC :EXIT
         EOR #32
:AND     AND #$7F
:EXIT    RTS

:POS     
         CPX #34
         BNE :EXIT
         CMP #32
         BEQ :ORA
         CMP #96
         BCC :EXIT
         EOR #32
:ORA     ORA #$80
         RTS


* Read in an address, immediate or variable

NUMRELS  DFB 00           ;Number of times to REL
ADDOFF   DFB 00           ;add or sub offset

GETARG   
         LDA #00
         STA FAC1
         STA FAC1+1
         STA FAC2+1
         STA EXTCODE
         STA NUMRELS
         STA ADDOFF       ;always start +

         LDA [TEXTP],Y
         LDX #3           ;Check for prefix chars
:CT      CMP :TAB1,X
         BEQ :STA
         DEX
         BPL :CT
         LDA #00
         DEY
:STA     STA PREFIX
         INY
         JSR :GET

:EXTRA   JSR GETCHAR      ;Check for + - * /

         CMP #32
         BEQ :RTS
         CMP #'+'
         BEQ :ADD
         CMP #'-'
         BEQ :SUB
         CMP #'*'
         BEQ :TIMES
         CMP #'/'
         BEQ :DIV
:RTS     JMP FAC2ARG

:GET     JSR GETCHAR
         JSR ISNUM
         BCS :ADDR
         CMP #'%'
         BEQ :ADDR
         CMP #'$'
         BEQ :ADDR
         CMP #'*'
         BEQ :STAR
         CMP #34
         BEQ :ASCII
         CMP #39
         BEQ :ASCII
         CMP #'-'
         BEQ :SUB
         JMP READVAR
:ADDR    JMP READNUM

:STAR    LDX #02
:SL1     LDA CODEP,X
         STA FAC1,X
         DEX
         BPL :SL1
         INY
         RTS

:ASCII   STA TEMP
         INY
         LDA #00
         STA FAC1+1
         STA FAC1+2
         JSR FIXCHAR
         STA FAC1
         INY
         LDA [TEXTP],Y
         CMP TEMP
         BNE :ERR1
         INY
         RTS
:ERR1    LDA #BADARG
         STA ERROR
         RTS

:TIMES   
:DIV     
         JMP :EXTRA

:ADD     
         LDA #01
         STA ADDOFF
         LDA #$80
         JSR :FIXFAC
         CLC
         LDA FAC2
         ADC FAC1
         STA FAC1
         LDA FAC2+1
         ADC FAC1+1
         STA FAC1+1
         LDA FAC2+2
         ADC FAC1+2
         STA FAC1+2
         JMP :EXTRA
:SUB     
         LDA #$FF
         STA ADDOFF
         LDA #$C0
         JSR :FIXFAC
         SEC
         LDA FAC2
         SBC FAC1
         STA FAC1
         LDA FAC2+1
         SBC FAC1+1
         STA FAC1+1
         LDA FAC2+2
         SBC FAC1+2
         STA FAC1+2
         JMP :EXTRA

:FIXFAC  PHA
         LDA #$80
         JSR EXTCHECK
         JSR FAC2FAC
         INY
         JSR :GET
         PLA
         JMP EXTCHECK

:TAB1    TXT '<>!^'


FAC2ARG  LDX #3
:L1      LDA FAC1,X
         STA ARG1,X
         DEX
         BPL :L1
         RTS

FAC2FAC  LDX #3
:L       LDA FAC1,X
         STA FAC2,X
         DEX
         BPL :L
         RTS

* If 1st arg is an EXT then output as special code #$80
* (16-bit add), so that e.g. EXT1-1 will work correctly.
* Moreover, when EXT is used in add or sub the high byte
* must be set to zero, so that e.g. REL1+EXT1 will not
* indicate two relative offsets, and REL1-EXT1 will not
* indicate zero!  (Same for EXT1+REL1).

EXTCHECK 
         LDX ISEXT
         BEQ :RTS
         STA EXTCODE
         LDA PREFIX       ;must be 16-bits
         BNE :CHECKPRE
:C1      LDA NOEXT
         BNE :ERR
         INC EXTBLAH      ;Opcode not yet output
         LDA DFBFLAG
         BEQ :ADDEXT
         DEC EXTBLAH      ;no opcode to output!
         LDA DFBLEN
         CMP #2
         BNE :ERR
:ADDEXT  JSR ADDEXT
         LDA #00
         STA EXTBLAH
         STA ISEXT
         STA FAC1+2
:RTS     RTS
:CHECKPRE
         CMP #'^'
         BEQ :C1
:ERR     LDA #EXTERR
         STA ERROR
         RTS

*
* READNUM
*
* Calculates number and stores result in FAC1
*
* Strategy: ans = base*ans + num
* Custom multiply routine for speed.
*
BASE     DFB 0
NDIGITS  DFB 00           ;# of (hex) digits, for STA $00xxxx
                          ;used as flag for CALCLEN

READNUM  
         LDA #00
         STA FAC1
         STA FAC1+1
         STA FAC1+2
         LDX #10          ;Base 10 = default
         LDA [TEXTP],Y
         CMP #'$'
         BNE :C1
         LDX #16          ;Hex
         INY              ;Advance to next char
:C1      CMP #'%'
         BNE :C2
         LDX #2           ;Binary
         INY
:C2      STX BASE         ;.X = base

:LOOP    
         LDA [TEXTP],Y    ;Check for valid number
         CMP #224         ;tab
         BEQ :DONE
         CMP #58
         BCC :SBC
         AND #$5F         ;Convert to lower-case
         SBC #8
:SBC     SBC #47          ;0 -> 0 etc.
:C4      CMP BASE
         BCS :DONE        ;number > base so exit
         STA TEMP

         LDA FAC1+2
         ASL FAC1
         ROL FAC1+1
         ROL
         BCS :ERR
         CPX #16          ;If binary then we are done
         BNE :BASE10
         INC NDIGITS
         ASL FAC1         ;Multiply by 16
         ROL FAC1+1
         ROL
         BCS :ERR
         ASL FAC1
         ROL FAC1+1
         ROL
         BCS :ERR
         ASL FAC1
         ROL FAC1+1
         ROL
         BCS :ERR

:BASE10  CPX #10          ;To mult by ten, use
         BNE :OK          ;10*x = 8*x + 2*x
* LDA FAC1+2
         PHA
         LDA FAC1+1
         PHA
         LDA FAC1
         ASL FAC1         ;temp=8*x
         ROL FAC1+1
         ROL FAC1+2
         BCS :ERR
         ASL FAC1
         ROL FAC1+1
         ROL FAC1+2
         BCS :ERR
         ADC FAC1         ;Add in 2*x
         STA FAC1
         PLA
         ADC FAC1+1
         STA FAC1+1
         PLA
         ADC FAC1+2
         BCS :ERR
:OK      STA FAC1+2

         LDA TEMP         ;Add in the number
         CLC
         ADC FAC1
         STA FAC1
         BCC :C5
         INC FAC1+1
         BNE :C5
         INC FAC1+2
:C5      INY
         BNE :LOOP
:ERR     LDA #BADNUM
         STA ERROR
:DONE    RTS

*
* READVAR
*
* Reads in a variable, by searching through the variable
* list.  Current variable storage is:
*
*    name type lo med hi
*
* where name is dextral char inverted
*

VARTYPE  DFB 00
ISENT    DFB 00
ISEXT    DFB 00
ISLAB    DFB 00
NOEXT    DFB 00
EXTCODE  DFB 00           ;$80/$C0 -> 16-bit add/sub
LASTVAR  DA 00

READVAR  
         JSR ROMOUT
         JSR FINDVAR
         BCC :ERR
         STY TEMPY
         LDY #00
         LDA (VARPNT),Y
         STA VARTYPE
         CMP #$FE
         BEQ :INY         ;plain equate
         CMP #$FD
         BCC :C2
         STA ISLAB        ;$FF and $FD
         BCS :INY
:C2      CMP #$E0
         BNE :C1
         STA ISENT
         STA ISLAB
:C1      CMP #$E1
         BNE :INY
         STA ISEXT
         LDA NOEXT
         BNE :EXTERR
:INY     INY
:L1      LDA (VARPNT),Y
         STA FAC1-1,Y
         INY
         CPY #4
         BNE :L1
         LDA RELFLAG
         BNE :JMP
         LDA VARTYPE      ;Adjust # of offsets
         CMP #$FE         ;don't adjust EXT or plain EQU
         BEQ :JMP
         CMP #$E1
         BEQ :JMP
         LDA ADDOFF
         BMI :DEC
         INC NUMRELS
         JMP ROMIN2
:DEC     DEC NUMRELS
:JMP     JMP ROMIN2

:ERR     LDA PASS         ;Assume 2-byte variable
         BEQ :ERR2        ;if on first pass
         LDA #00
         STA ERROR
         LDX #2
:L2      LDA CODEP,X      ;CODEP will ensure branches
         STA FAC1,X       ;work.
         DEX
         BPL :L2
         LDA #2
         STA ARGLEN
         JMP ROMIN

:EXTERR  LDA #EXTERR
         DFB $2C
:ERR2    LDA #BADVAR
         STA ERROR
         JMP ROMIN

*
* These routines checks for a decimal number in the
* code, to differentiate e.g. l1 from l1a
*
* On exit, Carry clear indicates that it is not
* a number.
*
ISNUM    
         CMP #'0'
         BCC :EXIT
         CMP #'9'+1
         ROL              ;Invert carry
         EOR #$01
         ROR
:EXIT    RTS

         PUT 'opcodes.l.s'
         PUT 'optab.c.s'
