; ******************************** ; * SUPERMON+ 64 JIM BUTTERFIELD * ; * V1.2 AUGUST 20 1985 * ; ******************************** ; Reformatted and annotated in late 2016/early 2017 by J.B. Langston. ; ; I've made the minimum necessary changes to this code to get it to assemble ; with 64tass. Specifically, I changed the following directives from PAL ; that 64tass doesn't support: ; - .ASC => .TEXT ; - *=*+X => .FILL X ; ; Aside from this, I have adopted a strict whitespace and comments only ; policy so that I preserve code exactly as Jim Butterfield wrote it. ; ; I think my comments are correct but I don't guarantee I haven't made ; any errors. Sadly Jim isn't around to ask anymore. If you spot any ; misunderstanings or errors in my comments, please report them. ; ----------------------------------------------------------------------------- ; temporary pointers TMP0 = $C1 ; used to return input, often holds end address TMP2 = $C3 ; usually holds start address ; ----------------------------------------------------------------------------- ; kernal variables SATUS = $90 ; kernal i/o status word FNLEN = $B7 ; length of current filename SADD = $B9 ; current secondary address (official name SA) FA = $BA ; current device number FNADR = $BB ; pointer to current filename NDX = $C6 ; number of characters in keyboard buffer KEYD = $0277 ; keyboard buffer BKVEC = $0316 ; BRK instruction vector (official name CBINV) *= $0100 ; store variables in tape error buffer ; ----------------------------------------------------------------------------- ; variables ACMD .FILL 1 ; addressing command LENGTH .FILL 1 ; length of operand MNEMW .FILL 3 ; 3 letter mnemonic buffer SAVX .FILL 1 ; 1 byte temp storage, often to save X register OPCODE .FILL 1 ; current opcode for assembler/disassembler UPFLG .FILL 1 ; flag: count up (bit 7 clear) or down (bit 7 set) DIGCNT .FILL 1 ; digit count INDIG .FILL 1 ; numeric value of single digit NUMBIT .FILL 1 ; numeric base of input STASH .FILL 2 ; 2-byte temp storage U0AA0 .FILL 10 ; work buffer U0AAE =* ; end of work buffer STAGE .FILL 30 ; staging buffer for filename, search, etc. ESTAGE =* ; end of staging buffer *= $0200 ; store more variables in basic line editor buffer INBUFF .FILL 40 ; 40-character input buffer ENDIN =* ; end of input buffer ; the next 7 locations are used to store the registers when ; entering the monitor and restore them when exiting. PCH .FILL 1 ; program counter high byte PCL .FILL 1 ; program counter low byte SR .FILL 1 ; status register ACC .FILL 1 ; accumulator XR .FILL 1 ; X register YR .FILL 1 ; Y register SP .FILL 1 ; stack pointer STORE .FILL 2 ; 2-byte temp storage CHRPNT .FILL 1 ; current position in input buffer SAVY .FILL 1 ; temp storage, often to save Y register U9F .FILL 1 ; index into assembler work buffer ; ----------------------------------------------------------------------------- ; kernal entry points SETMSG = $FF90 ; set kernel message control flag SECOND = $FF93 ; set secondary address after LISTEN TKSA = $FF96 ; send secondary address after TALK LISTEN = $FFB1 ; command serial bus device to LISTEN TALK = $FFB4 ; command serial bus device to TALK SETLFS = $FFBA ; set logical file parameters SETNAM = $FFBD ; set filename ACPTR = $FFA5 ; input byte from serial bus CIOUT = $FFA8 ; output byte to serial bus UNTLK = $FFAB ; command serial bus device to UNTALK UNLSN = $FFAE ; command serial bus device to UNLISTEN CHKIN = $FFC6 ; define input channel CLRCHN = $FFCC ; restore default devices INPUT = $FFCF ; input a character (official name CHRIN) CHROUT = $FFD2 ; output a character LOAD = $FFD5 ; load from device SAVE = $FFD8 ; save to device STOP = $FFE1 ; check the STOP key GETIN = $FFE4 ; get a character ; ----------------------------------------------------------------------------- ; set up origin .WEAK ORG = $9519 .ENDWEAK * = ORG ; ----------------------------------------------------------------------------- ; initial entry point SUPER LDY #MSG4-MSGBAS ; display "..SYS " JSR SNDMSG LDA SUPAD ; store entry point address in tmp0 STA TMP0 LDA SUPAD+1 STA TMP0+1 JSR CVTDEC ; convert address to decimal LDA #0 LDX #6 LDY #3 JSR NMPRNT ; print entry point address JSR CRLF LDA LINKAD ; set BRK vector STA BKVEC LDA LINKAD+1 STA BKVEC+1 LDA #$80 ; disable kernel control messages JSR SETMSG ; and enable error messages BRK ; ----------------------------------------------------------------------------- ; BRK handler BREAK LDX #$05 ; pull registers off the stack BSTACK PLA ; order: Y,X,A,SR,PCL,PCH STA PCH,X ; store in memory DEX BPL BSTACK CLD ; disable bcd mode TSX ; store stack pointer in memory STX SP CLI ; enable interupts ; ----------------------------------------------------------------------------- ; display registers [R] DSPLYR LDY #MSG2-MSGBAS ; display headers JSR SNDCLR LDA #$3B ; prefix registers with "; " to allow editing JSR CHROUT LDA #$20 JSR CHROUT LDA PCH ; print 2-byte program counter JSR WRTWO LDY #1 ; start 1 byte after PC high byte DISJ LDA PCH,Y ; loop through rest of the registers JSR WRBYTE ; print 1-byte register value INY CPY #7 ; there are a total of 5 registers to print BCC DISJ ; ----------------------------------------------------------------------------- ; main loop STRT JSR CRLF ; new line LDX #0 ; point at start of input buffer STX CHRPNT SMOVE JSR INPUT ; CHRIN kernal call to input a character STA INBUFF,X ; store in input buffer INX CPX #ENDIN-INBUFF ; error if buffer is full BCS ERROR CMP #$0D ; keep reading until CR BNE SMOVE LDA #0 ; null-terminate input buffer STA INBUFF-1,X ; (replacing the CR) ST1 JSR GETCHR ; get a character from the buffer BEQ STRT ; start over if buffer is empty CMP #$20 ; skip leading spaces BEQ ST1 S0 LDX #KEYTOP-KEYW ; loop through valid command characters S1 CMP KEYW,X ; see if input character matches BEQ S2 ; command matched, dispatch it DEX ; no match, check next command BPL S1 ; keep trying until we've checked them all ; then fall through to error handler ; ----------------------------------------------------------------------------- ; handle error ERROR LDY #MSG3-MSGBAS ; display "?" to indicate error and go to new line JSR SNDMSG JMP STRT ; back to main loop ; ----------------------------------------------------------------------------- ; dispatch command S2 CPX #$13 ; last 3 commands in table are load/save/validate BCS LSV ; which are handled by the same subroutine CPX #$0F ; next 4 commands are base conversions BCS CNVLNK ; which are handled by the same subroutine TXA ; remaining commands dispatch through vector table ASL A ; multiply index of command by 2 TAX ; since table contains 2-byte addresses LDA KADDR+1,X ; push address from vector table onto stack PHA ; so that the RTS from GETPAR will jump there LDA KADDR,X PHA JMP GETPAR ; get the first parameter for the command LSV STA SAVY ; handle load/save/validate JMP LD CNVLNK JMP CONVRT ; handle base conversion ; ----------------------------------------------------------------------------- ; exit monitor [X] EXIT JMP ($A002) ; jump to warm-start vector to reinitialize BASIC ; ----------------------------------------------------------------------------- ; display memory [M] DSPLYM BCS DSPM11 ; start from previous end addr if no address given JSR COPY12 ; save start address in TMP2 JSR GETPAR ; get end address in TMP0 BCC DSMNEW ; did user specify one? DSPM11 LDA #$0B ; if not, show 12 lines by default STA TMP0 BNE DSPBYT ; always true, but BNE uses 1 byte less than JMP DSMNEW JSR SUB12 ; end addr given, calc bytes between start and end BCC MERROR ; error if start is after end LDX #3 ; divide by 8 (shift right 3 times) DSPM01 LSR TMP0+1 ROR TMP0 DEX BNE DSPM01 DSPBYT JSR STOP ; check for stop key BEQ DSPMX ; exit early if pressed JSR DISPMEM ; display 1 line containing 8 bytes LDA #8 ; increase start address by 8 bytes JSR BUMPAD2 JSR SUBA1 ; decrement line counter BCS DSPBYT ; show another line until it's < 0 DSPMX JMP STRT ; back to main loop MERROR JMP ERROR ; handle error ; ----------------------------------------------------------------------------- ; alter registers [;] ALTR JSR COPY1P ; store first parameter in PC LDY #0 ; init counter ALTR1 JSR GETPAR ; get value for next register BCS ALTRX ; exit early if no more values given LDA TMP0 ; store in memory, offset from SR STA SR,Y ; these locations will be transferred to the INY ; actual registers before exiting the monitor CPY #$05 ; have we updated all 5 yet? BCC ALTR1 ; if not, get next ALTRX JMP STRT ; back to main loop ; ----------------------------------------------------------------------------- ; alter memory [>] ALTM BCS ALTMX ; exit if no parameter provided JSR COPY12 ; copy parameter to start address LDY #0 ALTM1 JSR GETPAR ; get value for next byte of memory BCS ALTMX ; if none given, exit early LDA TMP0 ; poke value into memory at start address + Y STA (TMP2),Y INY ; next byte CPY #8 ; have we read 8 bytes yet? BCC ALTM1 ; if not, read the next one ALTMX LDA #$91 ; move cursor up JSR CHROUT JSR DISPMEM ; re-display line to make ascii match hex JMP STRT ; back to main loop ; ----------------------------------------------------------------------------- ; goto (run) [G] GOTO LDX SP ; load stack pointer from memory TXS ; save in SP register GOTO2 JSR COPY1P ; copy provided address to PC SEI ; disable interrupts LDA PCH ; push PC high byte on stack PHA LDA PCL ; push PC low byte on stack PHA LDA SR ; push status byte on stack PHA LDA ACC ; load accumulator from memory LDX XR ; load X from memory LDY YR ; load Y from memory RTI ; return from interrupt (pops PC and SR) ; jump to subroutine [J] JSUB LDX SP ; load stack pointer from memory TXS ; save value in SP register JSR GOTO2 ; same as goto command STY YR ; save Y to memory STX XR ; save X to memory STA ACC ; save accumulator to memory PHP ; push processor status on stack PLA ; pull processor status into A STA SR ; save processor status to memory JMP DSPLYR ; display registers ; ----------------------------------------------------------------------------- ; display 8 bytes of memory DISPMEM JSR CRLF ; new line LDA #">" ; prefix > so memory can be edited in place JSR CHROUT JSR SHOWAD ; show address of first byte on line LDY #0 BEQ DMEMGO ; SHOWAD already printed a space after the address DMEMLP JSR SPACE ; print space between bytes DMEMGO LDA (TMP2),Y ; load byte from start address + Y JSR WRTWO ; output hex digits for byte INY ; next byte CPY #8 ; have we output 8 bytes yet? BCC DMEMLP ; if not, output next byte LDY #MSG5-MSGBAS ; if so, output : and turn on reverse video JSR SNDMSG ; before displaying ascii representation LDY #0 ; back to first byte in line DCHAR LDA (TMP2),Y ; load byte at start address + Y TAX ; stash in X AND #$BF ; clear 6th bit CMP #$22 ; is it a quote (")? BEQ DDOT ; if so, print . instead TXA ; if not, restore character AND #$7F ; clear top bit CMP #$20 ; is it a printable character (>= $20)? TXA ; restore character BCS DCHROK ; if printable, output character DDOT LDA #$2E ; if not, output '.' instaed DCHROK JSR CHROUT INY ; next byte CPY #8 ; have we output 8 bytes yet? BCC DCHAR ; if not, output next byte RTS ; ----------------------------------------------------------------------------- ; compare memory [C] COMPAR LDA #0 ; bit 7 clear signals compare .BYTE $2C ; absolute BIT opcode consumes next word (LDA #$80) ; transfer memory [T] TRANS LDA #$80 ; bit 7 set signals transfer STA SAVY ; save compare/transfer flag in SAVY LDA #0 ; assume we're counting up (bit 7 clear) STA UPFLG ; save direction flag JSR GETDIF ; get two addresses and calculate difference ; TMP2 = source start ; STASH = source end ; STORE = length BCS TERROR ; carry set indicates error JSR GETPAR ; get destination address in TMP0 BCC TOKAY ; carry set indicates error TERROR JMP ERROR ; handle error TOKAY BIT SAVY ; transfer or compare? BPL COMPAR1 ; high bit clear indicates compare LDA TMP2 ; if it's a transfer, we must take steps CMP TMP0 ; to avoid overwriting the source bytes before LDA TMP2+1 ; they have been transferred SBC TMP0+1 ; compare source (TMP2) to destination (TMP0) BCS COMPAR1 ; and count up if source is before than desitnation LDA STORE ; otherwise, start at end and count down... ADC TMP0 ; add length (STORE) to desintation (TMP0) STA TMP0 ; to calculate end of destination LDA STORE+1 ADC TMP0+1 STA TMP0+1 LDX #1 ; change source pointer from beginning to end TDOWN LDA STASH,X ; TMP2 = source end (STASH) STA TMP2,X DEX BPL TDOWN LDA #$80 ; high bit set in UPFLG means count down STA UPFLG COMPAR1 JSR CRLF ; new line LDY #0 ; no offset from pointer TCLOOP JSR STOP ; check for stop key BEQ TEXIT ; exit if pressed LDA (TMP2),Y ; load byte from source BIT SAVY ; transfer or compare? BPL COMPAR2 ; skip store if comparing STA (TMP0),Y ; otherwise, store in destination COMPAR2 CMP (TMP0),Y ; compare to destination BEQ TMVAD ; don't show address if equal JSR SHOWAD ; show address TMVAD BIT UPFLG ; counting up or down? BMI TDECAD ; high bit set means we're counting down INC TMP0 ; increment destination low byte BNE TINCOK INC TMP0+1 ; carry to high byte if necessary BNE TINCOK JMP ERROR ; error if high byte overflowed TDECAD JSR SUBA1 ; decrement destination (TMP0) JSR SUB21 ; decrement source (TMP2) JMP TMOR TINCOK JSR ADDA2 ; increment source (TMP2) TMOR JSR SUB13 ; decrement length BCS TCLOOP ; loop until length is 0 TEXIT JMP STRT ; back to main loop ; ----------------------------------------------------------------------------- ; hunt memory [H] HUNT JSR GETDIF ; get start (TMP2) and end (TMP0) of haystack BCS HERROR ; carry indicates error LDY #0 JSR GETCHR ; get a single character CMP #"'" ; is it a single quote? BNE NOSTRH ; if not, input needle as hex bytes JSR GETCHR ; if so, input needle as string CMP #0 BEQ HERROR ; error if needle isn't at least one byte HPAR STA STAGE,Y ; save char in staging area INY JSR GETCHR ; get another char BEQ HTGO ; if it's null start searching CPY #ESTAGE-STAGE ; have we filled up the needle staging area? BNE HPAR ; if not, get another character BEQ HTGO ; if so, start searching NOSTRH JSR RDPAR ; read hex bytes if string not indicated HLP LDA TMP0 ; save last read byte in staging area STA STAGE,Y INY ; get another hex byte JSR GETPAR BCS HTGO ; if there is none, start searching CPY #ESTAGE-STAGE ; have we filled up the needle staging area? BNE HLP ; if not, get another byte HTGO STY SAVY ; save length of needle JSR CRLF ; new line HSCAN LDY #0 HLP3 LDA (TMP2),Y ; get first byte in haystack CMP STAGE,Y ; compare it to first byte of needle BNE HNOFT ; if it doesn't match, we haven't found anything INY ; if it does, check the next byte CPY SAVY ; have we reached the end of the needle? BNE HLP3 ; if not, keep comparing bytes JSR SHOWAD ; match found, show address HNOFT JSR STOP ; no match, check for stop key BEQ HEXIT ; exit prematurely if pressed JSR ADDA2 ; increment haystack pointer JSR SUB13 ; decrement haystack length BCS HSCAN ; still more haystack? keep searching HEXIT JMP STRT ; back to main loop HERROR JMP ERROR ; handle error ; ----------------------------------------------------------------------------- ; load, save, or verify [LSV] LD LDY #1 ; default to reading from tape, device #1 STY FA STY SADD ; default to secondary address #1 DEY STY FNLEN ; start with an empty filename STY SATUS ; clear status LDA #>STAGE ; set filename pointer to staging buffer STA FNADR+1 LDA #= $E8 indicates relative addressing JSR CHEK2B ; ACMD < $E8 indicates normal addressing DEY ; consume byte BNE TRYAD ; check for 2 more digits if not zero-page TRYMOD ASL ACMD ; shift a bit out of the addressing command BCC UB4DF ; if it's zero, skip checking current character LDA CHAR1-1,X JSR CHEKOP ; otherwise first character against operand LDA CHAR2-1,X ; get second character to check BEQ UB4DF ; if it's zero, skip checking it JSR CHEKOP ; otherwise check it against hte operand UB4DF DEX ; move to next character BNE TRYIT ; repeat tests BEQ TRYBRAN TRY4B JSR CHEK2B ; check for 4 digit address placeholder JSR CHEK2B ; by checking for 2 digits twice TRYBRAN LDA STORE ; get number of bytes in assembly buffer CMP U9F ; more bytes left to check? BEQ ABRAN ; if not, we've found a match; build instruction JMP BUMPOP ; if so, this opcode doesn't match; try the next ; convert branches to relative address ABRAN LDY LENGTH ; get number of bytes in operand BEQ A1BYTE ; if none, just output the opcode LDA STORE+1 ; otherwise check the address format CMP #$9D ; is it a relative branch? BNE OBJPUT ; if not, skip relative branch calculation LDA TMP0 ; calculate the difference between the current SBC TMP2 ; address and the branch target (low byte) TAX ; save it in X LDA TMP0+1 ; borrow from the high byte if necessary SBC TMP2+1 BCC ABBACK ; if result is negative, we're branching back BNE SERROR ; high bytes must be equal when branching forward CPX #$82 ; difference between low bytes must be < 130 BCS SERROR ; error if the address is too far away BCC ABRANX ABBACK TAY ; when branching backward high byte of target must INY ; be 1 less than high byte of current address BNE SERROR ; if not, it's too far away CPX #$82 ; difference between low bytes must be < 130 BCC SERROR ; if not, it's too far away ABRANX DEX ; adjust branch target relative to the DEX ; instruction following this one TXA LDY LENGTH ; load length of operand BNE OBJP2 ; don't use the absolute address ; assemble machine code OBJPUT LDA TMP0-1,Y ; get the operand OBJP2 STA (TMP2),Y ; store it after the opcode DEY BNE OBJPUT ; copy the other byte of operand if there is one A1BYTE LDA OPCODE ; put opcode into instruction STA (TMP2),Y JSR CRLF ; carriage return LDA #$91 ; back up one line JSR CHROUT LDY #MSG7-MSGBAS ; "A " prefix JSR SNDCLR ; clear line JSR DISLIN ; disassemble the instruction we just assembled INC LENGTH ; instruction length = operand length + 1 byte LDA LENGTH ; for the opcode JSR BUMPAD2 ; increment address by length of instruction LDA #"A" ; stuff keyboard buffer with next assemble command: STA KEYD ; "A XXXX " where XXXX is the next address LDA #" " ; after the previously assembled instruction STA KEYD+1 STA KEYD+6 LDA TMP2+1 ; convert high byte of next address to hex JSR ASCTWO STA KEYD+2 ; put it in the keyboard buffer STX KEYD+3 LDA TMP2 ; convert low byte of next address to hex JSR ASCTWO STA KEYD+4 ; put it in the keyboard buffer STX KEYD+5 LDA #7 ; set number of chars in keyboard buffer STA NDX JMP STRT ; back to main loop SERROR JMP ERROR ; handle error ; check characters in operand CHEK2B JSR CHEKOP ; check two bytes against value in accumulator CHEKOP STX SAVX ; stash X LDX U9F ; get current index into work buffer CMP U0AA0,X ; check whether this opcode matches the buffer BEQ OPOK ; matching so far, check the next criteria PLA ; didn't match, so throw away return address PLA ; on the stack because we're starting over BUMPOP INC OPCODE ; check the next opcode BEQ SERROR ; error if we tried every opcode and none fit JMP ATRYOP ; start over with new opcode OPOK INC U9F ; opcode matches so far; check the next criteria LDX SAVX ; restore X RTS ; ----------------------------------------------------------------------------- ; disassemble [D] DISASS BCS DIS0AD ; if no address was given, start from last address JSR COPY12 ; copy start address to TMP2 JSR GETPAR ; get end address in TMP0 BCC DIS2AD ; if one was given, skip default DIS0AD LDA #$14 ; disassemble 14 bytes by default STA TMP0 ; store length in TMP0 BNE DISGO ; skip length calculation DIS2AD JSR SUB12 ; calculate number of bytes between start and end BCC DERROR ; error if end address is before start address DISGO JSR CLINE ; clear the current line JSR STOP ; check for stop key BEQ DISEXIT ; exit early if pressed JSR DSOUT1 ; output disassembly prefix ". " INC LENGTH LDA LENGTH ; add length of last instruction to start address JSR BUMPAD2 LDA LENGTH ; subtract length of last inst from end address JSR SUBA2 BCS DISGO DISEXIT JMP STRT ; back to mainloop DERROR JMP ERROR DSOUT1 LDA #"." ; output ". " prefix to allow edit and reassemble JSR CHROUT JSR SPACE DISLIN JSR SHOWAD ; show the address of the instruction JSR SPACE ; insert a space LDY #0 ; no offset LDA (TMP2),Y ; load operand of current instruction JSR INSTXX ; get mnemonic and addressing mode for opcode PHA ; save index into mnemonic table LDX LENGTH ; get length of operand INX ; add 1 byte for opcode DSBYT DEX ; decrement index BPL DSHEX ; show hex for byte being disassembled STY SAVY ; save index LDY #MSG8-MSGBAS ; skip 3 spaces JSR SNDMSG LDY SAVY ; restore index JMP NXBYT DSHEX LDA (TMP2),Y ; show hex for byte JSR WRBYTE NXBYT INY ; next byte CPY #3 ; have we output 3 bytes yet? BCC DSBYT ; if not, loop PLA ; restore index into mnemonic table LDX #3 ; 3 letters in mnemonic JSR PROPXX ; print mnemonic LDX #6 ; 6 possible address mode character combos PRADR1 CPX #3 ; have we checked the third combo yet? BNE PRADR3 ; if so, output the leading characters LDY LENGTH ; get the length of the operand BEQ PRADR3 ; if it's zero, there's no operand to print PRADR2 LDA ACMD ; otherwise, get the addressing mode CMP #$E8 ; check for relative addressing PHP ; save result of check LDA (TMP2),Y ; get the operand PLP ; restore result of check BCS RELAD ; handle a relative address JSR WRTWO ; output digits from address DEY BNE PRADR2 ; repeat for next byte of operand, if there is one PRADR3 ASL ACMD ; check whether addr mode uses the current char BCC PRADR4 ; if not, skip it LDA CHAR1-1,X ; look up the first char in the table JSR CHROUT ; print first char LDA CHAR2-1,X ; look up the second char in the table BEQ PRADR4 ; if there's no second character, skip it JSR CHROUT ; print second char PRADR4 DEX ; next potential address mode character BNE PRADR1 ; loop if we haven't checked them all yet RTS ; back to caller RELAD JSR UB64D ; calculate absolute address from relative CLC ADC #1 ; adjust address relative to next instruction BNE RELEND ; don't increment high byte unless we overflowed INX ; increment high byte RELEND JMP WRADDR ; print address UB64D LDX TMP2+1 ; get high byte of current address TAY ; is relative address positive or negative? BPL RELC2 ; if positive, leave high byte alone DEX ; if negative, decrement high byte RELC2 ADC TMP2 ; add relative address to low byte BCC RELC3 ; if there's no carry, we're done INX ; if there's a carry, increment the high byte RELC3 RTS ; ----------------------------------------------------------------------------- ; get opcode mode and length ; Note: the labels are different, but the code of this subroutine is almost ; identical to the INSDS2 subroutine of the Apple Mini-Assembler on page 78 of ; the Apple II Red Book. I'm not sure exactly where this code originated ; (MOS or Apple) but it's clear that this part of Supermon64 and the ; Mini-Asssembler share a common heritage. The comments showing the way the ; opcodes are transformed into indexes for the mnemonic lookup table come ; from the Mini-Assembler source. INSTXX TAY ; stash opcode in accumulator in Y for later LSR A ; is opcode even or odd? BCC IEVEN LSR A BCS ERR ; invalid opcodes XXXXXX11 CMP #$22 BEQ ERR ; invalid opcode 10001001 AND #$07 ; mask bits to 10000XXX ORA #$80 IEVEN LSR A ; LSB determines whether to use left/right nybble TAX ; get format index using remaining high bytes LDA MODE,X BCS RTMODE ; look at left or right nybble based on carry bit LSR A ; if carry = 0, use left nybble LSR A LSR A LSR A RTMODE AND #$0F ; if carry = 1, use right nybble BNE GETFMT ERR LDY #$80 ; substitute 10000000 for invalid opcodes LDA #0 GETFMT TAX LDA MODE2,X ; lookup operand format using selected nybble STA ACMD ; save for later use AND #$03 ; lower 2 bits indicate number of bytes in operand STA LENGTH TYA ; restore original opcode AND #$8F ; mask bits to X000XXXX TAX ; save it TYA ; restore original opcode LDY #3 CPX #$8A ; check if opcode = 1XXX1010 BEQ GTFM4 GTFM2 LSR A ; transform opcode into index for mnemonic table BCC GTFM4 LSR A ; opcodes transformed as follows: GTFM3 LSR A ; 1XXX1010->00101XXX ORA #$20 ; XXXYYY01->00111XXX DEY ; XXXYYY10->00111XXX BNE GTFM3 ; XXXYY100->00110XXX INY ; XXXXX000->000XXXXX GTFM4 DEY BNE GTFM2 RTS ; ----------------------------------------------------------------------------- ; extract and print packed mnemonics PROPXX TAY ; use index in accumulator to look up mnemonic LDA MNEML,Y ; and place a temporary copy in STORE STA STORE LDA MNEMR,Y STA STORE+1 PRMN1 LDA #0 ; clear accumulator LDY #$05 ; shift 5 times PRMN2 ASL STORE+1 ; shift right byte ROL STORE ; rotate bits from right byte into left byte ROL A ; rotate bits from left byte into accumulator DEY ; next bit BNE PRMN2 ; loop until all bits shifted ADC #$3F ; calculate ascii code for letter by adding to '?' JSR CHROUT ; output letter DEX ; next letter BNE PRMN1 ; loop until all 3 letters are output JMP SPACE ; output space ; ----------------------------------------------------------------------------- ; read parameters RDPAR DEC CHRPNT ; back up one char GETPAR JSR RDVAL ; read the value BCS GTERR ; carry set indicates error JSR GOTCHR ; check previous character BNE CKTERM ; if it's not null, check if it's a valid separator DEC CHRPNT ; back up one char LDA DIGCNT ; get number of digits read BNE GETGOT ; found some digits BEQ GTNIL ; didn't find any digits CKTERM CMP #$20 ; space or comma are valid separators BEQ GETGOT ; anything else is an error CMP #"," BEQ GETGOT GTERR PLA ; encountered error PLA ; get rid of command vector pushed on stack JMP ERROR ; handle error GTNIL SEC ; set carry to indicate no parameter found .BYTE $24 ; BIT ZP opcode consumes next byte (CLC) GETGOT CLC ; clear carry to indicate paremeter returned LDA DIGCNT ; return number of digits in A RTS ; return to address pushed from vector table ; ----------------------------------------------------------------------------- ; read a value in the specified base RDVAL LDA #0 ; clear temp STA TMP0 STA TMP0+1 STA DIGCNT ; clear digit counter TXA ; save X and Y PHA TYA PHA RDVMOR JSR GETCHR ; get next character from input buffer BEQ RDNILK ; null at end of buffer CMP #$20 ; skip spaces BEQ RDVMOR LDX #3 ; check numeric base [$+&%] GNMODE CMP HIKEY,X BEQ GOTMOD ; got a match, set up base DEX BPL GNMODE ; check next base INX ; default to hex DEC CHRPNT ; back up one character GOTMOD LDY MODTAB,X ; get base value LDA LENTAB,X ; get bits per digit STA NUMBIT ; store bits per digit NUDIG JSR GETCHR ; get next char in A RDNILK BEQ RDNIL ; end of number if no more characters SEC SBC #$30 ; subtract ascii value of 0 to get numeric value BCC RDNIL ; end of number if character was less than 0 CMP #$0A BCC DIGMOR ; not a hex digit if less than A SBC #$07 ; 7 chars between ascii 9 and A, so subtract 7 CMP #$10 ; end of number if char is greater than F BCS RDNIL DIGMOR STA INDIG ; store the digit CPY INDIG ; compare base with the digit BCC RDERR ; error if the digit >= the base BEQ RDERR INC DIGCNT ; increment the number of digits CPY #10 BNE NODECM ; skip the next part if not using base 10 LDX #1 DECLP1 LDA TMP0,X ; stash the previous 16-bit value for later use STA STASH,X DEX BPL DECLP1 NODECM LDX NUMBIT ; number of bits to shift TIMES2 ASL TMP0 ; shift 16-bit value by specified number of bits ROL TMP0+1 BCS RDERR ; error if we overflowed 16 bits DEX BNE TIMES2 ; shift remaining bits CPY #10 BNE NODEC2 ; skip the next part if not using base 10 ASL STASH ; shift the previous 16-bit value one bit left ROL STASH+1 BCS RDERR ; error if we overflowed 16 bits LDA STASH ; add shifted previous value to current value ADC TMP0 STA TMP0 LDA STASH+1 ADC TMP0+1 STA TMP0+1 BCS RDERR ; error if we overflowed 16 bits NODEC2 CLC LDA INDIG ; load current digit ADC TMP0 ; add current digit to low byte STA TMP0 ; and store result back in low byte TXA ; A=0 ADC TMP0+1 ; add carry to high byte STA TMP0+1 ; and store result back in high byte BCC NUDIG ; get next digit if we didn't overflow RDERR SEC ; set carry to indicate error .BYTE $24 ; BIT ZP opcode consumes next byte (CLC) RDNIL CLC ; clear carry to indicate success STY NUMBIT ; save base of number PLA ; restore X and Y TAY PLA TAX LDA DIGCNT ; return number of digits in A RTS ; ----------------------------------------------------------------------------- ; print address SHOWAD LDA TMP2 LDX TMP2+1 WRADDR PHA ; save low byte TXA ; put high byte in A JSR WRTWO ; output high byte PLA ; restore low byte WRBYTE JSR WRTWO ; output byte in A SPACE LDA #$20 ; output space BNE FLIP CHOUT CMP #$0D ; output char with special handling of CR BNE FLIP CRLF LDA #$0D ; load CR in A BIT $13 ; check default channel BPL FLIP ; if high bit is clear output CR only JSR CHROUT ; otherwise output CR+LF LDA #$0A ; output LF FLIP JMP CHROUT FRESH JSR CRLF ; output CR LDA #$20 ; load space in A JSR CHROUT JMP SNCLR ; ----------------------------------------------------------------------------- ; output two hex digits for byte WRTWO STX SAVX ; save X JSR ASCTWO ; get hex chars for byte in X (lower) and A (upper) JSR CHROUT ; output upper nybble TXA ; transfer lower to A LDX SAVX ; restore X JMP CHROUT ; output lower nybble ; ----------------------------------------------------------------------------- ; convert byte in A to hex digits ASCTWO PHA ; save byte JSR ASCII ; do low nybble TAX ; save in X PLA ; restore byte LSR A ; shift upper nybble down LSR A LSR A LSR A ; convert low nybble in A to hex digit ASCII AND #$0F ; clear upper nibble CMP #$0A ; if less than A, skip next step BCC ASC1 ADC #6 ; skip ascii chars between 9 and A ASC1 ADC #$30 ; add ascii char 0 to value RTS ; ----------------------------------------------------------------------------- ; get prev char from input buffer GOTCHR DEC CHRPNT ; get next char from input buffer GETCHR STX SAVX LDX CHRPNT ; get pointer to next char LDA INBUFF,X ; load next char in A BEQ NOCHAR ; null, :, or ? signal end of buffer CMP #":" BEQ NOCHAR CMP #"?" NOCHAR PHP INC CHRPNT ; next char LDX SAVX PLP ; Z flag will signal last character RTS ; ----------------------------------------------------------------------------- ; copy TMP0 to TMP2 COPY12 LDA TMP0 ; low byte STA TMP2 LDA TMP0+1 ; high byte STA TMP2+1 RTS ; ----------------------------------------------------------------------------- ; subtract TMP2 from TMP0 SUB12 SEC LDA TMP0 ; subtract low byte SBC TMP2 STA TMP0 LDA TMP0+1 SBC TMP2+1 ; subtract high byte STA TMP0+1 RTS ; ----------------------------------------------------------------------------- ; subtract from TMP0 SUBA1 LDA #1 ; shortcut to decrement by 1 SUBA2 STA SAVX ; subtrahend in accumulator SEC LDA TMP0 ; minuend in low byte SBC SAVX STA TMP0 LDA TMP0+1 ; borrow from high byte SBC #0 STA TMP0+1 RTS ; ----------------------------------------------------------------------------- ; subtract 1 from STORE SUB13 SEC LDA STORE SBC #1 ; decrement low byte STA STORE LDA STORE+1 SBC #0 ; borrow from high byte STA STORE+1 RTS ; ----------------------------------------------------------------------------- ; add to TMP2 ADDA2 LDA #1 ; shortcut to increment by 1 BUMPAD2 CLC ADC TMP2 ; add value in accumulator to low byte STA TMP2 BCC BUMPEX INC TMP2+1 ; carry to high byte BUMPEX RTS ; ----------------------------------------------------------------------------- ; subtract 1 from TMP2 SUB21 SEC LDA TMP2 ; decrement low byte SBC #1 STA TMP2 LDA TMP2+1 ; borrow from high byte SBC #0 STA TMP2+1 RTS ; ----------------------------------------------------------------------------- ; copy TMP0 to PC COPY1P BCS CPY1PX ; do nothing if parameter is empty LDA TMP0 ; copy low byte LDY TMP0+1 ; copy high byte STA PCL STY PCH CPY1PX RTS ; ----------------------------------------------------------------------------- ; get start/end addresses and calc difference GETDIF BCS GDIFX ; exit with error if no parameter given JSR COPY12 ; save start address in TMP2 JSR GETPAR ; get end address in TMP0 BCS GDIFX ; exit with error if no parameter given LDA TMP0 ; save end address in STASH STA STASH LDA TMP0+1 STA STASH+1 JSR SUB12 ; subtract start address from end address LDA TMP0 STA STORE ; save difference in STORE LDA TMP0+1 STA STORE+1 BCC GDIFX ; error if start address is after end address CLC ; clear carry to indicate success .BYTE $24 ; BIT ZP opcode consumes next byte (SEC) GDIFX SEC ; set carry to indicate error RTS ; ----------------------------------------------------------------------------- ; convert base [$+&%] CONVRT JSR RDPAR ; read a parameter JSR FRESH ; next line and clear LDA #"$" ; output $ sigil for hex JSR CHROUT LDA TMP0 ; load the 16-bit value entered LDX TMP0+1 JSR WRADDR ; print it in 4 hex digits JSR FRESH LDA #"+" ; output + sigil for decimal JSR CHROUT JSR CVTDEC ; convert to BCD using hardware mode LDA #0 ; clear digit counter LDX #6 ; max digits + 1 LDY #3 ; bits per digit - 1 JSR NMPRNT ; print result without leading zeros JSR FRESH ; next line and clear LDA #"&" ; print & sigil for octal JSR CHROUT LDA #0 ; clear digit counter LDX #8 ; max digits + 1 LDY #2 ; bits per digit - 1 JSR PRINUM ; output number JSR FRESH ; next line and clear LDA #"%" ; print % sigil for binary JSR CHROUT LDA #0 ; clear digit counter LDX #$18 ; max digits + 1 LDY #0 ; bits per digit - 1 JSR PRINUM ; output number JMP STRT ; back to mainloop ; ----------------------------------------------------------------------------- ; convert binary to BCD CVTDEC JSR COPY12 ; copy value from TMP0 to TMP2 LDA #0 LDX #2 ; clear 3 bytes in work buffer DECML1 STA U0AA0,X DEX BPL DECML1 LDY #16 ; 16 bits in input PHP ; save status register SEI ; make sure no interrupts occur with BCD enabled SED DECML2 ASL TMP2 ; rotate bytes out of input low byte ROL TMP2+1 ; .. into high byte and carry bit LDX #2 ; process 3 bytes DECDBL LDA U0AA0,X ; load current value of byte ADC U0AA0,X ; add it to itself plus the carry bit STA U0AA0,X ; store it back in the same location DEX ; decrement byte counter BPL DECDBL ; loop until all bytes processed DEY ; decrement bit counter BNE DECML2 ; loop until all bits processed PLP ; restore processor status RTS ; load the input value and fall through to print it PRINUM PHA ; save accumulator LDA TMP0 ; copy input low byte to work buffer STA U0AA0+2 LDA TMP0+1 ; copy input high byte to work buffer STA U0AA0+1 LDA #0 ; clear overflow byte in work buffer STA U0AA0 PLA ; restore accumulator ; print number in specified base without leading zeros NMPRNT STA DIGCNT ; number of digits in accumulator STY NUMBIT ; bits per digit passed in Y register DIGOUT LDY NUMBIT ; get bits to process LDA #0 ; clear accumulator ROLBIT ASL U0AA0+2 ; shift bits out of low byte ROL U0AA0+1 ; ... into high byte ROL U0AA0 ; ... into overflow byte ROL A ; ... into accumulator DEY ; decrement bit counter BPL ROLBIT ; loop until all bits processed TAY ; check whether accumulator is 0 BNE NZERO ; if not, print it CPX #1 ; have we output the max number of digits? BEQ NZERO ; if not, print it LDY DIGCNT ; how many digits have we output? BEQ ZERSUP ; skip output if digit is 0 NZERO INC DIGCNT ; increment digit counter ORA #$30 ; add numeric value to ascii '0' to get ascii char JSR CHROUT ; output character ZERSUP DEX ; decrement number of leading zeros BNE DIGOUT ; next digit RTS ; ----------------------------------------------------------------------------- ; disk status/command [@] DSTAT BNE CHGDEV ; if device address was given, use it LDX #8 ; otherwise, default to 8 .BYTE $2C ; absolute BIT opcode consumes next word (LDX TMP0) CHGDEV LDX TMP0 ; load device address from parameter CPX #4 ; make sure device address is in range 4-31 BCC IOERR CPX #32 BCS IOERR STX TMP0 LDA #0 ; clear status STA SATUS STA FNLEN ; empty filename JSR GETCHR ; get next character BEQ INSTAT1 ; null, display status DEC CHRPNT ; back up 1 char CMP #"$" ; $, display directory BEQ DIRECT LDA TMP0 ; command specified device to listen JSR LISTEN LDA #$6F ; secondary address 15 (only low nybble used) JSR SECOND ; send command to device DCOMD LDX CHRPNT ; get next character from buffer INC CHRPNT LDA INBUFF,X BEQ INSTAT ; break out of loop if it's null JSR CIOUT ; otherwise output it to the serial bus BCC DCOMD ; unconditional loop: CIOUT clears carry before RTS ; get device status INSTAT JSR UNLSN ; command device to unlisten INSTAT1 JSR CRLF ; new line LDA TMP0 ; load device address JSR TALK ; command device to talk LDA #$6F ; secondary address 15 (only low nybble used) JSR TKSA RDSTAT JSR ACPTR ; read byte from serial bus JSR CHROUT ; print it CMP #$0D ; if the byte is CR, exit loop BEQ DEXIT LDA SATUS ; check status AND #$BF ; ignore EOI bit BEQ RDSTAT ; if no errors, read next byte DEXIT JSR UNTLK ; command device to stop talking JMP STRT ; back to mainloop IOERR JMP ERROR ; handle error ; get directory DIRECT LDA TMP0 ; load device address JSR LISTEN ; command device to listen LDA #$F0 ; secondary address 0 (only low nybble used) JSR SECOND LDX CHRPNT ; get index of next character DIR2 LDA INBUFF,X ; get next character from buffer BEQ DIR3 ; break if it's null JSR CIOUT ; send character to device INX ; increment characer index BNE DIR2 ; loop if it hasn't wrapped to zero DIR3 JSR UNLSN ; command device to unlisten JSR CRLF ; new line LDA TMP0 ; load device address PHA ; save on stack JSR TALK ; command device to talk LDA #$60 ; secondary address 0 (only low nybble used) JSR TKSA LDY #3 ; read 3 16-bit values from device DIRLIN STY STORE ; ignore the first 2; 3rd is file size DLINK JSR ACPTR ; read low byte from device STA TMP0 ; store it LDA SATUS ; check status BNE DREXIT ; exit if error or eof occurred JSR ACPTR ; read high byte from device STA TMP0+1 ; store it LDA SATUS ; check status BNE DREXIT ; exit if error or eof cocurred DEC STORE ; decrement byte count BNE DLINK ; loop if bytes remain JSR CVTDEC ; convert last 16-bit value to decimal LDA #0 ; clear digit count LDX #6 ; max 6 digits LDY #3 ; 3 bits per digit JSR NMPRNT ; output number LDA #" " ; output space JSR CHROUT DNAME JSR ACPTR ; get a filename character from the device BEQ DMORE ; if it's null, break out of loop LDX SATUS ; check for errors or eof BNE DREXIT ; if found exit early JSR CHROUT ; output character CLC BCC DNAME ; unconditional branch to read next char DMORE JSR CRLF JSR STOP ; check for stop key BEQ DREXIT ; exit early if pressed JSR GETIN ; pause if a key was pressed BEQ NOPAWS PAWS JSR GETIN ; wait until another key is pressed BEQ PAWS NOPAWS LDY #2 BNE DIRLIN ; unconditional branch to read next file DREXIT JSR UNTLK ; command device to untalk PLA ; restore accumulator JSR LISTEN ; command device to listen LDA #$E0 ; secondary address 0 (only low nybble is used) JSR SECOND JSR UNLSN ; command device to unlisten JMP STRT ; back to mainloop ; ----------------------------------------------------------------------------- ; print and clear routines CLINE JSR CRLF ; send CR+LF JMP SNCLR ; clear line SNDCLR JSR SNDMSG SNCLR LDY #$28 ; loop 40 times SNCLP LDA #$20 ; output space character JSR CHROUT LDA #$14 ; output delete character JSR CHROUT DEY BNE SNCLP RTS ; ----------------------------------------------------------------------------- ; display message from table SNDMSG LDA MSGBAS,Y ; Y contains offset in msg table PHP AND #$7F ; strip high bit before output JSR CHOUT INY PLP BPL SNDMSG ; loop until high bit is set RTS ; ----------------------------------------------------------------------------- ; message table; last character has high bit set MSGBAS =* MSG2 .BYTE $0D ; header for registers .TEXT " PC SR AC XR YR SP V1.2" .BYTE $0D+$80 MSG3 .BYTE $1D,$3F+$80 ; syntax error: move right, display "?" MSG4 .TEXT "..SYS" ; SYS call to enter monitor .BYTE $20+$80 MSG5 .BYTE $3A,$12+$80 ; ":" then RVS ON for memory ASCII dump MSG6 .TEXT " ERRO" ; I/O error: display " ERROR" .BYTE "R"+$80 MSG7 .BYTE $41,$20+$80 ; assemble next instruction: "A " + addr MSG8 .TEXT " " ; pad non-existent byte: skip 3 spaces .BYTE $20+$80 ; ----------------------------------------------------------------------------- ; addressing mode table - nybbles provide index into MODE2 table ; for opcodes XXXXXXY0, use XXXXXX as index into table ; for opcodes WWWXXY01 use $40 + XX as index into table ; use right nybble if Y=0; use left nybble if Y=1 MODE .BYTE $40,$02,$45,$03 ; even opcodes .BYTE $D0,$08,$40,$09 .BYTE $30,$22,$45,$33 .BYTE $D0,$08,$40,$09 .BYTE $40,$02,$45,$33 .BYTE $D0,$08,$40,$09 .BYTE $40,$02,$45,$B3 .BYTE $D0,$08,$40,$09 .BYTE $00,$22,$44,$33 .BYTE $D0,$8C,$44,$00 .BYTE $11,$22,$44,$33 .BYTE $D0,$8C,$44,$9A .BYTE $10,$22,$44,$33 .BYTE $D0,$08,$40,$09 .BYTE $10,$22,$44,$33 .BYTE $D0,$08,$40,$09 .BYTE $62,$13,$78,$A9 ; opcodes ending in 01 ; addressing mode format definitions indexed by nybbles from MODE table ; left 6 bits define which characters appear in the assembly operand ; left 3 bits are before the address; next 3 bits are after ; right-most 2 bits define length of binary operand ; index 654 321 ; 1st character $(# ,), ; 2nd character $$ X Y length format idx mode MODE2 .BYTE $00 ; 000 000 00 0 error .BYTE $21 ; 001 000 01 #$00 1 immediate .BYTE $81 ; 100 000 01 $00 2 zero-page .BYTE $82 ; 100 000 10 $0000 3 absolute .BYTE $00 ; 000 000 00 4 implied .BYTE $00 ; 000 000 00 5 accumulator .BYTE $59 ; 010 110 01 ($00,X) 6 indirect,X .BYTE $4D ; 010 011 01 ($00),Y 7 indirect,Y .BYTE $91 ; 100 100 01 $00,X 8 zero-page,X .BYTE $92 ; 100 100 10 $0000,X 9 absolute,X .BYTE $86 ; 100 001 10 $0000,Y A absolute,Y .BYTE $4A ; 010 010 10 ($0000) B indirect .BYTE $85 ; 100 001 01 $00,Y C zero-page,Y .BYTE $9D ; 100 111 01 $0000* D relative ; * relative is special-cased so format bits don't match ; character lookup tables for the format definitions in MODE2 CHAR1 .BYTE $2C,$29,$2C ; "," ")" "," .BYTE $23,$28,$24 ; "#" "(" "$" CHAR2 .BYTE $59,$00,$58 ; "Y" 0 "X" .BYTE $24,$24,$00 ; "$" "$" 0 ; ----------------------------------------------------------------------------- ; 3-letter mnemonics packed into two bytes (5 bits per letter) ; left 8 bits ; XXXXX000 opcodes MNEML .BYTE $1C,$8A,$1C,$23 ; BRK PHP BPL CLC .BYTE $5D,$8B,$1B,$A1 ; JSR PLP BMI SEC .BYTE $9D,$8A,$1D,$23 ; RTI PHA BVC CLI .BYTE $9D,$8B,$1D,$A1 ; RTS PLA BVS SEI .BYTE $00,$29,$19,$AE ; ??? DEY BCC TYA .BYTE $69,$A8,$19,$23 ; LDY TAY BCS CLV .BYTE $24,$53,$1B,$23 ; CPY INY BNE CLD .BYTE $24,$53,$19,$A1 ; CPX INX BEQ SED ; XXXYY100 opcodes .BYTE $00,$1A,$5B,$5B ; ??? BIT JMP JMP .BYTE $A5,$69,$24,$24 ; STY LDY CPY CPX ; 1XXX1010 opcodes .BYTE $AE,$AE,$A8,$AD ; TXA TXS TAX TSX .BYTE $29,$00,$7C,$00 ; DEX ??? NOP ??? ; XXXYYY10 opcodes .BYTE $15,$9C,$6D,$9C ; ASL ROL LSR ROR .BYTE $A5,$69,$29,$53 ; STX LDX DEC INC ; XXXYYY01 opcodes .BYTE $84,$13,$34,$11 ; ORA AND EOR ADC .BYTE $A5,$69,$23,$A0 ; STA LDA CMP SBC ; right 7 bits, left justified ; XXXXX000 opcodes MNEMR .BYTE $D8,$62,$5A,$48 ; BRK PHP BPL CLC .BYTE $26,$62,$94,$88 ; JSR PLP BMI SEC .BYTE $54,$44,$C8,$54 ; RTI PHA BVC CLI .BYTE $68,$44,$E8,$94 ; RTS PLA BVS SEI .BYTE $00,$B4,$08,$84 ; ??? DEY BCC TYA .BYTE $74,$B4,$28,$6E ; LDY TAY BCS CLV .BYTE $74,$F4,$CC,$4A ; CPY INY BNE CLD .BYTE $72,$F2,$A4,$8A ; CPX INX BEQ SED ; XXXYY100 opcodes .BYTE $00,$AA,$A2,$A2 ; ??? BIT JMP JMP .BYTE $74,$74,$74,$72 ; STY LDY CPY CPX ; 1XXX1010 opcodes .BYTE $44,$68,$B2,$32 ; TXA TXS TAX TSX .BYTE $B2,$00,$22,$00 ; DEX ??? NOP ??? ; XXXYYY10 opcodes .BYTE $1A,$1A,$26,$26 ; ASL ROL LSR ROR .BYTE $72,$72,$88,$C8 ; STX LDX DEC INC ; XXXYYY01 opcodes .BYTE $C4,$CA,$26,$48 ; ORA AND EOR ADC .BYTE $44,$44,$A2,$C8 ; STA LDA CMP SBC .BYTE $0D,$20,$20,$20 ; ----------------------------------------------------------------------------- ; single-character commands KEYW .TEXT "ACDFGHJMRTX@.>;" HIKEY .TEXT "$+&%LSV" KEYTOP =* ; vectors corresponding to commands above KADDR .WORD ASSEM-1,COMPAR-1,DISASS-1,FILL-1 .WORD GOTO-1,HUNT-1,JSUB-1,DSPLYM-1 .WORD DSPLYR-1,TRANS-1,EXIT-1,DSTAT-1 .WORD ASSEM-1,ALTM-1,ALTR-1 ; ----------------------------------------------------------------------------- MODTAB .BYTE $10,$0A,$08,02 ; modulo number systems LENTAB .BYTE $04,$03,$03,$01 ; bits per digit LINKAD .WORD BREAK ; address of brk handler SUPAD .WORD SUPER ; address of entry point