Skip to content

Instantly share code, notes, and snippets.

@jblang
Last active May 8, 2024 00:04
Show Gist options
  • Save jblang/2cab2d04309f1ac98ba40a05d6222c8c to your computer and use it in GitHub Desktop.
Save jblang/2cab2d04309f1ac98ba40a05d6222c8c to your computer and use it in GitHub Desktop.
; kernal locations
CHRGET = $73 ; subroutine: get next basic text character
STATUS = $90 ; kernal i/o status word
STKEY = $91 ; flag: was stop key pressed
DFLTO = $9A ; default output device
MSGFLG = $9D ; flag: kernal message control
SAL = $AC ; pointer: starting address of load/screen scrolling
TAPE1 = $B2 ; pointer: start of tape buffer
FNLEN = $B7 ; length of current filename
SA = $B9 ; current secondary address
FA = $BA ; current device number
FNADR = $BB ; pointer: current filename
MEMUSS = $C3 ; tape load temporary addresses
NDX = $C6 ; number of characters in keyboard buffer
BLNSW = $CC ; cursor blink enable: 0=flash cursor
GDBLN = $CE ; character under cursor
BLNON = $CF ; flag: was last cursor blink on or off
PNT = $D1 ; pointer: address of current screen line
PNTR = $D3 ; cursor column on current line
QTSW = $D4 ; flag: editor in quote mode? 0=no
TBLX = $D6 ; current cursor physical line number
USER = $F3 ; pointer: address of current screen color ram loc
KEYD = $0277 ; keyboard input buffer
RPTFLG = $028A ; flag: which keys will repeat
M51CTR = $0293 ; RS-232: mock 6551 control register
M51CDR = $0294 ; RS-232: mock 6551 command register
CINV = $0314 ; vector: irq handler
CBINV = $0316 ; vector: brk instruction handler
; zero-page variables
PARAM3 = $FB ; third command parameter (often target address)
PARAM1 = $FD ; first command parameter (often start address)
; variables stored in tape buffer ($033C-$03FB)
; register storage
PCH = $033C ; program counter high byte
PCL = $033D ; program counter low byte
SR = $033E ; status register
ACC = $033F ; accumulator
XR = $0340 ; X register
YR = $0341 ; Y register
SP = $0342 ; stack pointer
IRQSAVH = $0343 ; current irq vector high byte
IRQSAVL = $0344 ; current irq vector low byte
WALKF = $0348 ; walk flag:
CURCMD = $0349
LENGTH = $034A
STASH = $034B
OPDLEN = $034D
BUFIDX = $034E
f034F = $034F
a0350 = $0350
a0351 = $0351
a0352 = $0352
DIFF = $0353 ; value of PARAM1 - PARAM3
PARAM2 = $0354 ; second command parameter (often end address)
OVRFLW = $0356 ; overflow indicator
TRNFLG = $0357 ; flag: 0=compare, 1=transfer
ADRFMT = $0358 ; address format for instruction
TMPNYB = $0359 ; temporary nybble storage
NESTIRQ = $0360 ; nested IRQ vector
STPADR = $035A
STPADR+1 = $035B
a035C = $035C
a035D = $035D
a035E = $035E
a035F = $035F
a0362 = $0362
a0363 = $0363
a0354 = $0364
STAGE = $0365
; i/o locations
SCROLY = $D011 ; vertical fine scrolling and control register
FRELO1 = $D400 ; voice 1 frequency control (low byte)
FREHI1 = $D401 ; voice 1 frequency control (high byte)
VCREG1 = $D404 ; voice 1 control register
ATDCY1 = $D405 ; voice 1 attack/decay control register
SUREL1 = $D406 ; voice 1 sustain/release control register
SIGVOL = $D418 ; volume and filter select register
TIMALO = $DC04 ; cia #1 timer A (low byte)
TIMAHI = $DC05 ; cia #1 timer A (high byte)
CIACRA = $DC0E ; cia #1 control register
pDFC5 = $DFC5 ; ? cia #2 register images
; internal kernal calls
BASVEC = $A000 ; basic cold-start vector
STROUT = $AB1E ; print null-terminated message at addr AY
LINPRT = $BDCD ; output 16-bit number in AX in decimal
OCINT = $E518 ; initialize screen and keyboard
CHROUTD = $E716 ; output to screen (ignore i/o redirect)
RESTORD = $FD15 ; restore ram vectors for default i/o
; undocumented kernal calls
PLREG = $EA81 ; pull registers from stack
SAFN = $F3D5 ; send secondary address and filename
CLSER = $F642 ; close serial bus device
INITTM = $FDDD ; initialize irq timer
; kernal jump table
TKSA = $FF96 ; send secondary address after TALK
ACPTR = $FFA5 ; input byte from serial bus
TALK = $FFB4 ; command serial bus device to TALK
SETLFS = $FFBA ; set logical file parameters
SETNAM = $FFBD ; set filename parameters
OPEN = $FFC0 ; open a logical file
CLOSE = $FFC3 ; close a logical file
CHKOUT = $FFC9 ; define an output channel
CHRIN = $FFCF ; input a character
CHROUT = $FFD2 ; output a character
LOAD = $FFD5 ; load from a device
SAVE = $FFD8 ; save to a device
STOP = $FFE1 ; check the STOP key
GETIN = $FFE4 ; get a character
; code entry point
* = $C000
; ----------------------------------------------------------------------------
; entry point
START SEI
JMP SETUP
; ----------------------------------------------------------------------------
RVSLFT LDA #$12 ; turn on reverse video
JSR CHROUT
LDA #$9D ; move cursor left
JSR CHROUT
RTS
JSR RESTORD
JSR OCINT
; ----------------------------------------------------------------------------
; initialization
SETUP LDA #<BRKHND ; set up BRK vector
LDX #>BRKHND
STA CBINV
STX CBINV+1
LDA CINV ; check if irq vector already set to our handler
LDX CINV+1
CMP #<IRQHND
BNE IRQNE
CPX #>IRQHND
BEQ IRQEQ
IRQNE STA NESTIRQ ; if not, save the old vector
STX NESTIRQ+1
JSR SETIRQ ; and set up our irq handler
IRQEQ LDA #$75 ; protect variables stored in tape buffer
STA TAPE1
LDA #$80 ; high bit set
STA RPTFLG ; repeat all keys
STA MSGFLG ; enable kernal control msg/disable error msg
LDX #$D7 ; $FF - length of banner
JSR BANNER ; display micromon banner (X=0 after return)
STX WALKF ; clear walk flag
STX a0354
CLI
BRK ; enter break handler
; ----------------------------------------------------------------------------
; monitor entry point from break handler
BRKENT DEC PCL ; decrement PC because RET-1 is pushed on stack
BNE SKPPCH
DEC PCH ; borrow if necessary
SKPPCH JSR CRLF
LDX #$42 ; print "B" to indicate entry via BRK vector
BRKAST LDA #$2A ; print "*"
JMP BRKREG
; ----------------------------------------------------------------------------
; main loop (with various entry points)
ERROR LDA #$3F ; print "?" to indicate error
JSR CHROUT
NOBEEP LDA #$00 ; stop tone after user presses enter
.BYTE $2C
BEEP LDA #$11 ; play a tone when the previous command finishes
STA VCREG1
JSR CRLF
PROMPT LDA #$2E ; print "." prompt
JSR CHROUT
RESET LDA #$00 ; clear varibles
STA BUFIDX ;
STA OVRFLW ; overflow flag
STA a0354
LDX #$7F ; stack pointer at half-page
TXS
EATCHR JSR INNOCR ; get a character that's not CR
CMP #$2E ; ignore "." prompt
BEQ EATCHR
CMP #$20 ; ignore spaces
BEQ EATCHR
LDX #$24 ; 36 commands to check
CHKCMD CMP CMDCHR,X ; check input matches current command
BNE NXTCMD ; if not, got to next command
STA CURCMD ; if so, save it as the current command
TXA ; multiply index times two
ASL
TAX
LDA CMDVEC,X ; look subroutine address in vector table
STA PARAM3 ; copy vector and jump to it
LDA CMDVEC+1,X
STA PARAM3+1
JMPP3 JMP (PARAM3)
NXTCMD DEX ; decrement command index
BPL CHKCMD ; loop until we've checked them all
JMP ERROR ; error if no valid command matched
; ----------------------------------------------------------------------------
; decrement PARAM1 or PARAM3
; Note: PARAM1 must immediately follow PARAM3 in memory
DECP1 LDX #$02 ; offset 2 bytes to point at PARAM1
BNE DODEC
DECP3 LDX #$00 ; offset 0 bytes to point at PARAM3
DODEC LDY PARAM3,X ; check if low byte is 0
BNE INCLO ; if not, decrement
LDY PARAM3+1,X ; check if high byte is 0
BNE INCHI ; if not decrement
INC OVRFLW ; otherwise, increment overflow
INCHI DEC PARAM3+1,X ; decrement high byte
INCLO DEC PARAM3,X ; decrement low byte
RTS
; ----------------------------------------------------------------------------
; disassemble a line and pad with spaces
DISPAD LDA #$00 ; clear buffer index
STA BUFIDX
JSR DISLIN ; output prefixed address
LDX #$09 ; 9 spaces
; output number of spaces in X
OUTXSP JSR OUTSPC ; output space
DEX
BNE OUTXSP ; repeat X times
RTS
; ----------------------------------------------------------------------------
; swap values of PARAM2 and PARAM3
SWAP23 LDX #$02 ; swap two bytes from PARAM2 to PARAM3
SW23LP LDA PARAM3-1,X ; get byte from PARAM3
PHA ; stash it
LDA PARAM2-1,X ; get byte from PARAM2
STA PARAM3-1,X ; save it to PARAM3
PLA ; restore byte from PARAM3
STA PARAM2-1,X ; save byte in PARAM2
DEX ; next byte
BNE SW23LP
RTS
; ----------------------------------------------------------------------------
; calculate DIFF between two PARAMs
CDIF23 LDA PARAM2 ; difference between P2 and P3
LDY PARAM2+1
JMP SUBP3
CDIF13 LDA PARAM1 ; difference between P1 and P3
LDY PARAM1+1
SUBP3 SEC ; inverse borrow
SBC PARAM3 ; subtract low byte of P3
STA DIFF ; store difference
TYA ; transfer high byte to accumulator
SBC PARAM3+1 ; subtract high byte of P3
TAY ; put high byte back in Y
ORA DIFF ; restore difference to A
RTS
; ----------------------------------------------------------------------------
; compare two blocks of memory [C]
COMPARE LDA #$00 ; 0 for compare
BEQ TXORCP
; transfer memory block [T]
TRNSFR LDA #$01 ; 1 for transfer
TXORCP STA TRNFLG ; store flag for later
JSR IN3PAR ; input 3 parameters
JSR CRLF ; output a CRLF
JSR CDIF13 ; calc diff btw source start (P1) and dest (P3)
JSR SWAP13 ; swap: now PARAM1 = dest; PARAM3 = source start
BCC CNTDN ; work down from end if source (P3) < dest (P1)
UPLOOP JSR CDIF23 ; calc diff between src start (P2) and end (P3)
BCC BEEP1 ; exit to main loop if start < end
JSR TCBYTE ; transfer/compare a single byte
INC PARAM1 ; increment dest (P1) low byte
BNE SKIPHI ; skip high byte unless it overflowed
INC PARAM1+1 ; increment dest (P1) high byte
SKIPHI JSR INCP3 ; increment source (P3) and check for overflow
LDY OVRFLW
BNE BEEP1 ; exit to main loop if high byte overflowed
BEQ UPLOOP
CNTDN JSR CDIF23 ; calculate diff btw src end (P2) and start (P3)
CLC
LDA DIFF ; get low byte of difference (P1)
ADC PARAM1 ; add dest start (P1)
STA PARAM1 ; store result back in P1
TYA ; get high byte of difference
ADC PARAM1+1 ; add high byte of dest (P1)
STA PARAM1+1 ; store back in P1
JSR SWAP23 ; swap: now P2 = src start, P3 = src end
DNLOOP JSR TCBYTE ; transfer/compare a single byte
JSR CDIF23 ; calc diff between start (P2) and end (P3)
BCS BEEP1 ; exit to main loop if start > end
JSR DECP1 ; decrement destination pointer (P1)
JSR DECP3 ; decrement source pointer (P3)
LDY OVRFLW ; check for overflow
BNE BEEP1 ; exit to main loop if high byte overflowed
BEQ DNLOOP
; transfer or compare a single byte between PARAM3 and PARAM1
TCBYTE LDX #$00
LDA (PARAM3,X) ; load source byte
LDY TRNFLG ; check whether doing transfer or compare
BEQ DOCOMP
STA (PARAM1,X) ; copy byte from to destination
DOCOMP CMP (PARAM1,X) ; compare bytes
BEQ MATCH ; don't output anything if they match
OUTADDR JSR OUTP3 ; output PARAM3 in hex digits
JSR OUTSPC ; output a space
JSR STOP ; check for run/stop key
BEQ BEEP1 ; return to main loop if pressed
MATCH RTS
IN2PAR JSR INXSP3 ; input PARAM3
JSR INP1CR ; input PARAM1 and check for CR
BEQ ERROR4 ; error if CR
; check address range in PARAM1 and PARAM3
CHKRNG LDX OVRFLW ; exit to main loop if overflow
BNE BEEP1
JSR CDIF13 ; exit to main loop if P1 < P3
BCC BEEP1
RTS
; ----------------------------------------------------------------------------
; fill memory [F]
FILL JSR IN2W1B ; input address range and fill byte
STA STASH ; stash fill byte
FLOOP JSR CHKRNG ; ensure valid address range
LDA STASH ; restore fill byte
STA (PARAM3,X) ; copy fill byte to pointer
JSR INCP3 ; increment pointer
BNE FLOOP ; loop until range is filled
ERROR4 JMP ERROR
BEEP1 JMP BEEP
; ----------------------------------------------------------------------------
; hunt [H]
HUNT JSR IN2PAR ; input start (P3) and end address (P1)
JSR INNOCR ; get next character
CMP #$27 ; check whether it's a single quote
BNE HEXNDL ; if not, input bytes in hex
JSR INNOCR ; get next char
STRLP STA STAGE,X ; store needle
INX ; next byte
JSR INCKCR ; input another char and check for CR
BEQ DOHUNT ; start hunting if CR found
CPX #$20 ; maximum of 20 bytes in needle
BNE STRLP
BEQ DOHUNT ; start hunting
HEXNDL STX TMPNYB ; clear temp
JSR IN12HX2 ; get 1 or 2 hex digits
BCC ERROR4 ; error if invalid input
HEXLP STA STAGE,X ; store byte in needle
INX ; next byte
JSR INCKCR ; input another char and check for CR
BEQ DOHUNT ; start hunting if CR found
JSR IN12HX ; otherwise input 2 hex digits
BCC ERROR4 ; error if invalid input
CPX #$20 ; maximum of 20 bytes in needle
BNE HEXLP
DOHUNT STX LENGTH ; save length of needle
JSR CRLF ; new line
HSTKLP LDX #$00 ; clear indexes
LDY #$00
NEEDLP LDA (PARAM3),Y ; get char from haystack
CMP STAGE,X ; compare to current byte in needle
BNE NOMATCH ; if chars don't match, stop comparing needle
INY ; increment haystack index
INX ; increment needle index
CPX LENGTH ; check whether entire needle was checked
BNE NEEDLP ; continue with next character if not
JSR OUTADDR ; all characters checked, output matching addr
NOMATCH JSR INCP3 ; increment haystack pointer
JSR CHKRNG ; return to main loop when range is exhausted
BCS HSTKLP ; if not, loop again
; ----------------------------------------------------------------------------
; disassemble [D]
; loop through specified range and disassemble each instruction
DISASM JSR IN12PAR ; get start and (optional) end address
DISLP JSR CDIF13 ; calculate difference between start and end
BCC RETMAIN ; return to main if start is after end
LDY #$2C ; output ',' to allow in-place re-assembly
JSR DISPAD
JSR INC3OP
JSR STOP ; check for stop key
BNE DISLP ; loop if not pressed
RETMAIN JSR CSRUP
BNE BEEP1
; disassemble a single instruction
DISLIN JSR OUTPFX ; output prefix for disassembled line
DISNPF JSR OUTP3 ; output current address
JSR OUTSPC ; output space
JSR INSTP3 ; look up instruction at current pointer
PHA ; save index into mnemonic table
JSR INSBYT ; output instruction bytes as hex
PLA ; restore index into mnemonic table
JSR OUTMNM ; output mnemonics for current opcode
LDX #$06 ; 6 possible address mode characters
PRADR1 CPX #$03 ; have we checked the third combo yet?
BNE PRADR3 ; if so, output the leading characters
LDY OPDLEN ; otherwise check operand length
BEQ PRADR3 ; if it's zero, there's no operand to print
PRADR2 LDA ADRFMT ; otherwise ge the address format
CMP #$E8 ; check for relative addressing
LDA (PARAM3),Y ; get the operand
BCS RELAD ; handle relative address if found
JSR OUTBSX ; output digits from address
DEY ; repeat for next byte of address
BNE PRADR2 ; if there is one
PRADR3 ASL ADRFMT ; check whether addr mode uses current char
BCC PRADR4 ; if not, skip it
LDA CHAR1-1,X ; look up first char in the table
JSR OUTBUF ; print first char
LDA CHAR2-1,X ; lookup second char in table
BEQ PRADR4 ; skip if there is none
JSR OUTBUF ; print second char
PRADR4 DEX ; next potential address character
BNE PRADR1 ; loop if we haven't checked them all
RTS
RELAD JSR sC27B
TAX
INX
BNE bC260
INY
bC260 TYA
JSR OUTBSX
TXA
; output a byte but save X first
OUTBSX STX LENGTH ; save X
JSR OUTBYT ; output hex byte in A
LDX LENGTH ; restore X
RTS
; increment PARAM3 by the operand length
INC3OP LDA OPDLEN ; load the operand length
ADJP3 JSR ADSUBP3 ; add or subtract accumulator to PARAM3
STA PARAM3 ; store result back in PARAM3
STY PARAM3+1
RTS
; add or subtract A from PARAM3
ADSUBP3 SEC ; clear carry
sC27B LDY PARAM3+1 ; save high byte of P3 in Y
TAX ; save A in X
BPL APOS
DEY ; decrement high byte if A is negative
APOS ADC PARAM3 ; add low byte of PARAM3 to A
BCC NOCARRY ; don't increment high byte if no carry
INY ; decrement high byte in Y
NOCARRY 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. Old versions of Supermon for the PET credit
; Wozniak/Baum for the diassembler so this would appear to be the origin.
; 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 Y for later
LSR ; is opcode even or odd?
BCC OPEVEN
LSR
BCS INVOP ; opcodes XXXXXX11 are invalid
CMP #$22
BEQ INVOP ; opcode 10001001 is invalid
AND #$07 ; mask bits to 10000XXX
ORA #$80
OPEVEN LSR ; LSB determines whether to use left/right nybble
TAX
LDA MODE,X
BCS RTNYB ; look at left or right nybble based on carry bit
LSR
LSR
LSR
LSR
RTNYB AND #$0F ; carry = 1, use right nybble
BNE GETFMT
INVOP LDY #$80 ; substitute 10000000 for invalid opcodes
LDA #$00
GETFMT TAX
LDA MODE2,X ; look operand format using selected nybble
STA ADRFMT ; save format for later use
AND #$03 ; lower 2 bits indicate number of bytes in operand
STA OPDLEN
TYA ; restore original opcode
AND #$8F ; mask bits to X000XXXX
TAX ; save it
TYA ; restore original opcode
LDY #$03
CPX #$8A ; check if opcode = 1XXX1010
BEQ GTFM4
GTFM2 LSR ; transform opcode into index for mnemonic table
BCC GTFM4
LSR ; opcodes transformed as follows:
GTFM3 LSR ; 1XXX1010->00101XXX
ORA #$20 ; XXXYYY01->00111XXX
DEY ; XXXYYY10->00111XXX
BNE GTFM3 ; XXXYY100->00110XXX
INY ; XXXXX000->000XXXXX
GTFM4 DEY
BNE GTFM2
RTS
; ----------------------------------------------------------------------------
; output bytes comprising instruction pointed to by PARAM3
INSBYT LDA (PARAM3),Y ; get byte pointed to by P3
JSR OUTBSX ; output the opcode
LDX #$01 ; output 1 space
INSLP JSR OUTXSP
CPY OPDLEN ; compare index to opcode length
INY ; increment index
BCC INSBYT ; loop for each byte in opearnd
LDX #$03 ; maximum of 3 bytes
CPY #$03
BCC INSLP ; loop for each byte
RTS
; ----------------------------------------------------------------------------
; output mnemonic for opcode
OUTMNM TAY ; use index in accumulator to look up mnemonic
LDA MNEML,Y ; and place a temporary copy in PARAM2
STA PARAM2
LDA MNEMR,Y
STA PARAM2+1
MNLP1 LDA #$00 ; clear accumulator
LDY #$05 ; shift 5 times
MNLP2 ASL PARAM2+1 ; shift right byte
ROL PARAM2 ; rotate bits from right byte into left byte
ROL ; rotate bits from left byte into accumulator
DEY ; next bit
BNE MNLP2 ; loop until all bits shifted
ADC #$3F ; calculate ascii code for letter by adding to '?'
JSR CHROUT ; output letter
DEX ; next letter
BNE MNLP1 ; loop until all 3 letters are output
JMP OUTSPC ; output space and return
; ----------------------------------------------------------------------------
; reassemble [,]
REASM JSR INXSP3
LDA #$03
JSR INBYTS
LDY #$2C
JMP jC531
; ----------------------------------------------------------------------------
; directory [>]
DIR LDA #$08
STA FA
LDA #$01
LDX #$A5
LDY #$CF
JSR SETNAM
LDA #$60
STA SA
JSR SAFN
LDA FA
JSR TALK
LDA SA
JSR TKSA
LDA #$00
STA STATUS
LDY #$03
bC33D STY FNLEN
JSR ACPTR
STA MEMUSS
JSR ACPTR
STA MEMUSS+1
LDY STATUS
BNE bC38B
LDY FNLEN
DEY
BNE bC33D
LDX MEMUSS
LDA MEMUSS+1
JSR LINPRT
LDA #$20
JSR CHROUTD
bC35E JSR ACPTR
LDX STATUS
BNE bC38B
CMP #$00
BEQ bC381
JSR CHROUTD
JSR STOP
BEQ bC38B
JSR GETIN
BEQ bC35E
CMP #$20
BNE bC35E
bC37A JSR GETIN
BEQ bC37A
BNE bC35E
bC381 LDA #$0D
JSR CHROUTD
LDY #$02
JMP bC33D
bC38B JSR CLSER
JMP BEEP
; ----------------------------------------------------------------------------
; irq handler
IRQHND LDA #>IRQRET ; push IRQ return address on stack
PHA
LDA #<IRQRET
PHA
PHP ; push processor status on stack
PHA ; push dummy A X and Y values on stack
PHA
PHA
JMP (NESTIRQ) ; jump to nested IRQ handler
; input number of bytes specified in A
INBYTS STA STASH
PHA ; save accumulator
BYTLP JSR INNOCR ; skip separator
JSR INBYT ; input a hex byte
BNE BYTLP ; loop until all bytes output
PLA ; restore accumulator
SUBAP3 EOR #$FF ; one's complement of accumulator
JMP ADJP3 ; subtract from P3
; ----------------------------------------------------------------------------
; display memory [M]
MEMDSP JSR IN12PAR ; get start and (optional) end address
MEMLP LDX OVRFLW ; exit if pointer overflowed
BNE MEMQT
JSR CDIF13 ; exit if pointer passed end address
BCC MEMQT
JSR MEMLIN ; output a line of 8 bytes
JSR STOP ; check for stop key
BNE MEMLP ; loop if not pressed
MEMQT JMP RETMAIN ; quit early
MEMLIN JSR CRLF ; new line
sC3CB LDX #$2E ; prefix line with .: to allow editing
LDA #$3A
JSR OUTXA
JSR OUTSPC ; output a space
JSR OUTP3 ; output address
LDA #$08 ; output 8 bytes
JSR OUTBYTS
LDA #$08
JSR SUBAP3 ; decrement PARAM3 by 8 bytes
JSR OUTSPC ; output a space
JSR RVSLFT ; turn on reverse video and move left
NOP
NOP
LDY #$08
LDX #$00 ; no offset from PARAM3
ASCLP LDA (PARAM3,X) ; load byte pointed to by P3
PHA ; save in accumulator
AND #$7F ; clear high bit
CMP #$20 ; check whether byte is printable
PLA ; restore original byte
BCS NODOT ; output character if printable
LDA #$2E ; output "." for non-printable chars
NODOT JSR CHROUT
LDA #$00 ; disable quote mode
STA QTSW
JSR INCP3 ; increment pointer
DEY ; decrement counter
BNE ASCLP ; loop until 8 bytes output
JMP RVSOFF ; turn off reverse video and return
; ----------------------------------------------------------------------------
; modify memory [:]
MODMEM JSR INXSP3 ; input address
LDA #$08 ; read 8 bytes
JSR INBYTS
JSR CSRUP ; move up a line
JSR MEMLIN ; output line again to sync ascii with hex
LDA #$3A ; put ':' in keyboard buffer
STA KEYD
JMP ADDRKBD ; stuff keyboard buffer with address
; ----------------------------------------------------------------------------
; input one or two parameters
IN12PAR JSR INXSP3 ; get first parameter in P3
STA PARAM1 ; copy to P1
STX PARAM1+1
JSR INCKCR ; get a character and check for CR
BEQ ONLY1 ; if it's a CR skip second par
JSR IN0SP1 ; get second par in P1
ONLY1 JMP CRLF ; output new line
; ----------------------------------------------------------------------------
; assemble [A]
ASM JSR INXSWRD
STA PARAM1
STX PARAM1+1
bC439 LDX #$00
STX STAGE+1
bC43E JSR INNOCR
CMP #$20
BEQ bC439
STA f034F,X
INX
CPX #$03
BNE bC43E
bC44D DEX
BMI bC464
LDA f034F,X
SEC
SBC #$3F
LDY #$05
bC458 LSR
ROR STAGE+1
ROR STAGE
DEY
BNE bC458
BEQ bC44D
bC464 LDX #$02
bC466 JSR INCKCR
BEQ bC48D
CMP #$3A
BEQ bC48D
CMP #$20
BEQ bC466
JSR sC585
BCS bC487
JSR IN2NYB
LDY PARAM3
STY PARAM3+1
STA PARAM3
LDA #$30
STA STAGE,X
INX
bC487 STA STAGE,X
INX
BNE bC466
bC48D STX PARAM2
LDX #$00
STX OVRFLW
jC495 LDX #$00
STX STASH
LDA OVRFLW
JSR INSTXX
LDX ADRFMT
STX PARAM2+1
TAX
LDA MNEMR,X
JSR sC565
LDA MNEML,X
JSR sC565
LDX #$06
bC4B5 CPX #$03
BNE bC4CD
LDY OPDLEN
BEQ bC4CD
bC4BE LDA ADRFMT
CMP #$E8
LDA #$30
BCS bC4E5
JSR sC562
DEY
BNE bC4BE
bC4CD ASL ADRFMT
BCC bC4E0
LDA CHAR1-1,X
JSR sC565
LDA CHAR2-1,X
BEQ bC4E0
JSR sC565
bC4E0 DEX
BNE bC4B5
BEQ bC4EB
bC4E5 JSR sC562
JSR sC562
bC4EB LDA PARAM2
CMP STASH
BNE bC572
JSR SWAP13
LDY OPDLEN
BEQ bC52A
LDA PARAM2+1
CMP #$9D
BNE bC522
JSR CDIF13
BCC bC508
DEY
bC508 INY
BNE ERROR5
TYA
ROL
LDX DIFF
CPX #$82
TAY
BNE bC518
BCS bC51A
SEC
bC518 BCS ERROR5
bC51A DEX
DEX
TXA
LDY OPDLEN
BNE bC525
bC522 LDA PARAM3+1,Y
bC525 STA (PARAM3),Y
DEY
BNE bC522
bC52A LDA OVRFLW
STA (PARAM3),Y
LDY #$41
jC531 STY KEYD
JSR CSRUP
JSR DISPAD
JSR INC3OP
ADDRKBD LDA #$20 ; put spaces in buffer before and after address
STA KEYD+1
STA KEYD+6
LDA PARAM3+1 ; convert address high byte to hex
JSR BYT2HX
STX KEYD+2 ; put hex digits in keyboard buffer
STA KEYD+3
LDA PARAM3 ; convert address low byte to hex
JSR BYT2HX
STX KEYD+4 ; put hex digits in keyboard buffer
STA KEYD+5
LDA #$07 ; 7 characters in keyboard buffer
STA NDX
JMP BEEP ; return to main loop
; ----------------------------------------------------------------------------
; i/o routines
sC562 JSR sC565
sC565 STX LENGTH
LDX STASH
CMP STAGE,X
BEQ bC57D
PLA
PLA
bC572 INC OVRFLW
BEQ ERROR5
JMP jC495
ERROR5 JMP ERROR
bC57D INX
STX STASH
LDX LENGTH
RTS
sC585 CMP #$30
BCC bC58C
CMP #$47
RTS
bC58C SEC
RTS
; output character from buffer
OUTBUF CMP BUFIDX
BNE OUTCDB
RTS
; convert byte in A to hex digits in X (high nybble) and A (low nybble)
BYT2HX PHA ; save accumulator
LSR ; shift high nybble into low
LSR
LSR
LSR
JSR NYB2HD ; convert nybble to hex digit
TAX ; transfer low digit to X
PLA ; restore accumulator
AND #$0F ; clear high nybble
JMP NYB2HD ; convert nybble to hex digit and return
CRLF LDA #$0D ; carriage return
JSR CHROUT
LDA #$0A ; line feed
.BYTE $2C ; skip next instr
CSRUP LDA #$91 ; cursor up
OUTCDB JSR CHROUT ; output character
LDA SCROLY ; disable screen blanking
ORA #$10
STA SCROLY
RTS
; ----------------------------------------------------------------------------
NOP
BRKRET NOP
STA ACC ; save accumulator
PHP ; push processor status
PLA ; pull it into accumulator
AND #$EF ; clear sign flag
STA SR ; save status in memory
STX XR ; save x register
STY YR ; save y register
PLA ; pull return address low byte off stack
CLC ; increment it by 1
ADC #$01
STA PCL ; save it in memory
PLA ; pull return address high byte off stack
ADC #$00 ; handle borrow
STA PCH ; save it in memory
LDA #$80 ; enable walking
STA WALKF
BNE SAVIRQ ; save IRQ vector
; ----------------------------------------------------------------------------
; BRK handler
BRKHND JSR SETIRQ ; set up irq handler
JSR INITTM ; initialize irq timer
CLD
PLA ; save y register
STA YR
PLA ; save x register
STA XR
PLA ; save accumulator
STA ACC
PLA ; save status register
STA SR
PLA ; save PC low byte
STA PCL
PLA ; save PC high byte
STA PCH
SAVIRQ LDA CINV ; save current IRQ vector for later
STA IRQSAVL
LDA CINV+1
STA IRQSAVH
TSX ; save stack pointer in memory
STX SP
CLI ; enable interrupts
LDA SR ; check BRK flag in status register
AND #$10
BEQ NOBRK
DOBRK JMP BRKENT
NOBRK BIT WALKF ; check walk flag
BVC bC63D
LDA PCH ; get PC high byte
CMP STPADR+1 ; compare to high byte of stop address
BNE CKSTOP ; if doesn't match continue
LDA PCL ; get PC low byte
CMP STPADR ; compare to low byte of stop address
BNE CKSTOP ; if it doesn't match continue
LDA a035E
BNE bC68E
LDA a035F
BNE bC68B
LDA #$80
STA WALKF
bC63D BMI WALK80 ; start of walk
LSR WALKF ; shift walk flag down 1
BCC DOBRK ; break if it hasn't
LDX SP
TXS
LDA #>BRKRET
PHA
LDA #<BRKRET
PHA
JMP SETREG
WALK80 JSR CRLF ; new line
JSR REGPREP ; prepare to output registers
STA STASH ;
LDY #$00
JSR OUTBNS
LDA PCL
LDX PCH
STA PARAM3
STX PARAM3+1
JSR OUTSPC ; output space
LDA #$24
STA BUFIDX
JSR DISNPF
bC674 JSR GETIN
BEQ bC674
CMP #$03
BNE bC680
JMP BEEP
bC680 CMP #$4A
BNE BLANK
LDA #$01
STA WALKF
BNE BLANK
bC68B DEC a035F
bC68E DEC a035E
CKSTOP LDA STKEY ; check for stop key
CMP #$7F
BNE BLANK ; if not found, blank screen
LDX #$53 ; display "S*" to indicate STOP key was pressed
JMP BRKAST
; ----------------------------------------------------------------------------
; goto (run) [G]
GOTO LDA #$00
BEQ RUNCODE
; quick trace [Q]
QTRACE LDA a035C
LDX a035D
STA a035E
STX a035F
LDA #$40
BNE RUNCODE
; walk [W]
WALK LDA #$80 ; set walk flag to change behavior of interrupts
RUNCODE STA WALKF
JSR INCKCR ; check next character
BEQ CURPC ; use current PC if it's CR
CMP #$20 ; error if next char isn't a space
BNE ERROR6
JSR IN0SWRD ; input an address
JSR SETPC ; set the PC to the address provided
JSR INCKCR ; error if next char isn't CR
BNE ERROR6
CURPC JSR CRLF ; output new line
LDA WALKF ; check walk flag
BEQ SKPWLK ; skip walk setup if it's 0
BLANK LDX #$00
LDA SCROLY ; get current value of VIC control register
TAY ; save original value in Y
AND #$10 ; check whether blanking is enabled
BEQ INTTMR ; if not set up interrupt timer
TYA ; restore current value to A
AND #$EF ; enable screen blanking
STA SCROLY ; set register
NOP
NOP
LDY #$0C
DELAY DEX ; inner delay loop - 256 times
BNE DELAY
DEY ; outer delay loop - 12 times
BNE DELAY
INTTMR SEI
LDA #$54 ; count 84 clock cycles
STA TIMALO
STX TIMAHI ; X = 0 at this point
LDA CIACRA ; load CIA control register A
AND #$80 ; clear all bits except TOD frequency
; output disabled, count pulses on CNT
ORA #$11 ; latch in timer value and start timer
STA CIACRA
LDA #>pDFC5 ; load timer value
LDX #<pDFC5
STA IRQSAVL ; save in interrupt vector
STX IRQSAVH
SKPWLK LDX SP ; set up stack pointer with saved value
TXS
SEI ; disable interrupts
LDA IRQSAVL ; set up IRQ vector to saved address
LDX IRQSAVH
JSR SETIRQ2
SETREG LDA PCH ; set up registers with saved values
PHA
LDA PCL
PHA
LDA SR
PHA
LDA ACC
LDX XR
LDY YR
RTI
ERROR6 JMP ERROR
; ----------------------------------------------------------------------------
; set BRK handler
BRKSET JSR INXSWRD
STA STPADR
STX STPADR+1
LDA #$00
STA a035C
STA a035D
JSR IN1SWRD
STA a035C
STX a035D
JMP BEEP
; ----------------------------------------------------------------------------
; new locator [N]
NEWLOC JSR IN3PAR
STA a0362
STX a0363
JSR IN1SWRD
STA f034F
STX a0350
JSR IN1SWRD
STA a0351
STX a0352
JSR INCKCR
BEQ bC776
JSR CHRIN
CMP #$57
BNE bC776
INC BUFIDX
bC776 JSR SWAP13
bC779 LDX OVRFLW
BNE BEEP2
JSR CDIF23
BCC BEEP2
LDY BUFIDX
BNE bC7A2
LDA (PARAM3),Y
JSR INSTXX
TAX
LDA MNEML,X
BNE bC799
JSR DISPAD
BEEP2 JMP BEEP
bC799 LDY OPDLEN
CPY #$02
BNE bC7D3
BEQ bC7A5
bC7A2 STY OPDLEN
bC7A5 DEY
SEC
LDA (PARAM3),Y
TAX
SBC f034F
INY
LDA (PARAM3),Y
SBC a0350
BCC bC7D3
DEY
LDA a0351
SBC (PARAM3),Y
INY
LDA a0352
SBC (PARAM3),Y
BCC bC7D3
DEY
CLC
TXA
ADC a0362
STA (PARAM3),Y
INY
LDA (PARAM3),Y
ADC a0363
STA (PARAM3),Y
bC7D3 JSR INCP3
DEY
BPL bC7D3
BMI bC779
; ----------------------------------------------------------------------------
; misc i/o routines
; input 3 parameters
IN3PAR JSR INXSWRD ; input 1st word following any number of spaces
STA PARAM1 ; store in 1st parameter
STX PARAM1+1
JSR IN1SWRD ; input 2nd word following a single space
STA PARAM2 ; store in 2nd parameter
STX PARAM2+1
IN1PAR JSR INNOCR ; input 3rd word following a single space
JSR IN0SWRD
STPAR3 STA PARAM3 ; store in 3rd parameter
STX PARAM3+1
RTS
INXSP3 JSR INXSWRD ; input word following any number of spaces
BCS STPAR3 ; store in PARAM3
IN0SP1 JSR IN0SWRD ; input word without any leading spaces
BCS STPAR1 ; store in PARAM1
IN1SP1 JSR IN1SWRD ; input word with 1 leading space
STPAR1 STA PARAM1 ; store in PARAM1
STX PARAM1+1
RTS
; output word in PARAM3 as 4 hex digits
OUTP3 LDA PARAM3+1 ; output PARAM3 in hex
JSR OUTBYT
LDA PARAM3
; output byte in A as 2 hex digits
OUTBYT PHA ; save original byte
LSR ; shift high nybble into low
LSR
LSR
LSR
JSR NYB2HD ; convert high nybble to ascii hex digit
TAX ; save in X
PLA ; restore original byte
AND #$0F ; mask off high nybble
JSR NYB2HD ; convert low nybble to ascii hex digit
; output characters in X and A
OUTXA PHA
TXA
JSR CHROUT ; print char in X
PLA
JMP CHROUT ; print char in A
; convert 4-bit number in A to a hex digit, returned in A
NYB2HD CLC ; clear carry
ADC #$F6 ; maps $0-$9 to $F6-$FF, $A-$F to $100-$105
BCC DIG09 ; if there's no carry, it's a digit 0-9
ADC #$06 ; otherwise A-F, account for 6 chars btw 0 and A
DIG09 ADC #$3A ; subtracts from ':' for 0-9, add for A-F
RTS ; (using twos complement math)
; swap PARAM3 with PARAM1
SWAP13 LDX #$02 ; swap two bytes in a loop
SW13LP LDA PARAM3-1,X ; save byte from P3
PHA
LDA PARAM1-1,X ; move byte from P1 to P3
STA PARAM3-1,X
PLA ; restore byte from P3
STA PARAM1-1,X ; move to P1
DEX ; next byte
BNE SW13LP
RTS
; input 1 word following any number of spaces
INXSWRD LDA #$00 ; clear temporary variable
STA TMPNYB
SPCLP JSR INNOCR ; get 1 non CR character
CMP #$20 ; skip if it's a space
BEQ SPCLP
JSR IN2NYB ; get two nybbles
BCS LSTBYT ; if only 1 nybble
; input 1 word following a single space
IN1SWRD JSR INNOCR ; skip a char
; input 1 word without any preceeding spaces
IN0SWRD JSR IN12HX ; get 1 or 2 hex digits
BCC ERROR7 ; error if invalid input
LSTBYT TAX ; save byte in X
JSR IN12HX ; get 1 or 2 hex digits
BCC ERROR7 ; error if invalid input
RTS
ERROR7 JMP ERROR
IN2W1B JSR IN2PAR ; input 2 words and 1 byte
; input one or two hex digits - carry set indicates valid input
IN12HX LDA #$00 ; clear temporary byte
STA TMPNYB
JSR INNOCR ; get non-CR character
IN12HX2 CMP #$20 ; check if it's a space
BNE IN2NYB ; if not, input two hex digits
JSR INNOCR ; get another non-CR character
CMP #$20 ; check if it's a space
BNE IN1NYB ; if not, input one hex digit
CLC ; clear carry to indicate input failure
RTS
IN2NYB JSR HD2NYB ; convert hex to lower nybble
ASL ; shift into upper nybble
ASL
ASL
ASL
STA TMPNYB ; store in temp location
JSR INNOCR ; get non-CR character
IN1NYB JSR HD2NYB ; convert hex to nybble
ORA TMPNYB ; add in high nybble
SEC ; set carry to indicate success
RTS
; convert ascii hex digit to numeric value
HD2NYB CMP #$3A ; is digit 0-9?
PHP ; save comparison result
AND #$0F ; clear upper nybble
PLP ; restore comparison result
BCC RET1 ; done if 0-9
ADC #$08 ; otherwise A-F, so add to 8
RET1 RTS
; input a character and return to main loop if it's CR
INNOCR JSR INCKCR
BNE RET1
JMP NOBEEP
; ----------------------------------------------------------------------------
; set IRQ vectors
SETIRQ LDA #<IRQHND
LDX #>IRQHND
SETIRQ2 STA CINV
STX CINV+1
RTS
; ----------------------------------------------------------------------------
; switch print [P]
PRINTSW JSR INCKCR
BEQ bC8EB
JSR INXSP3
LDA PARAM3
ORA PARAM3+1
BEQ bC8DF
LDA DFLTO
CMP #$03
BNE ERROR7
LDA PARAM3
STA M51CTR
LDA PARAM3+1
STA M51CDR
bC8CD LDA #$02
TAX
TAY
JSR SETLFS
JSR OPEN
LDX #$02
JSR CHKOUT
JMP RESET
bC8DF LDA #$02
JSR CLOSE
bC8E4 LDA #$03
STA DFLTO
JMP BEEP
bC8EB LDA DFLTO
CMP #$03
BEQ bC8CD
BNE bC8E4
SETPC STA PCL
STX PCH
RTS
; ----------------------------------------------------------------------------
; misc i/o routines
OUTBYTS STA STASH ; save count
LDY #$00 ; no offset from pointer
REGLP1 JSR OUTSPC ; output a space
OUTBNS LDA (PARAM3),Y ; load byte at current pointer
JSR OUTBYT ; output byte in hex
JSR INCP3 ; increment the pointer
DEC STASH ; decrement the count
BNE REGLP1 ; loop until all registers output
RTS
INBYT JSR IN12HX ; input a byte
BCC SKPREG ; skip setting reg if input is invalid
LDX #$00 ; no offset from pointer
STA (PARAM3,X) ; save input in register
CMP (PARAM3,X) ; make sure it saved
BNE ERROR1 ; error if it didn't
SKPREG JSR INCP3 ; increment the pointer
DEC STASH ; decrement the count
RTS
REGPREP LDA #<SR ; store address of status register in PARAM3
STA PARAM3
LDA #>SR
STA PARAM3+1
LDA #$05 ; set up index to output 5 register
RTS
INCP3 INC PARAM3 ; increment PARAM3 low byte
BNE INCP3R ; skip next increment unless it overflowed
INC PARAM1+2 ; increment $FF (not sure why, it's never used)
INC PARAM3+1 ; increment PARAM3 high byte
BNE INCP3R ; skip next unless it overflowed
INC OVRFLW ; increment overflow indicator
INCP3R RTS
; output prefix for an editable line using command char in A
OUTPFX TYA ; save Y on stack
PHA
JSR CRLF ; new line
PLA ; pull saved value from stack into accumulator
ACCDOT LDX #$2E ; output "." followed by char in accumulator
JSR OUTXA
OUTSPC LDA #$20 ; output a space
JMP CHROUT
; ----------------------------------------------------------------------------
; display registers [R]
BRKREG JSR OUTXA ; characters in XA to indicate type of break
REGDSP LDX #$00 ; init index to 0
REGLP2 LDA REGNAM,X ; output register labels
JSR CHROUT
INX
CPX #$1C ; loop until outputting 28 characters
BNE REGLP2
LDY #$3B ; output ";" prefix for in-place modification
JSR OUTPFX
LDA PCH ; output PC high byte
JSR OUTBYT
LDA PCL ; output PC low byte
JSR OUTBYT
JSR OUTSPC
LDA IRQSAVH ; output IRQ vector high byte
JSR OUTBYT
LDA IRQSAVL ; output IRQ vector low byte
JSR OUTBYT
JSR REGPREP ; set up index to output remaining registers
JSR OUTBYTS ; output remaining registers
BEEP3 JMP BEEP
ERROR1 JMP ERROR ; relative error jump
REGMOD JSR INXSWRD ; skip leading spaces and input a word
JSR SETPC ; save it in the PC
JSR IN1SWRD ; skip 1 space and input another word
STA IRQSAVL ; save it in the IRQ vector
STX IRQSAVH
JSR REGPREP ; prepare to input remaining registers
STA STASH
INRGLP JSR INNOCR ; skip space and make sure it's not CR
JSR INBYT ; input the register byte
BNE INRGLP ; loop as long as we got a valid byte
BEQ BEEP3
INSKSP JSR CHRIN ; get first non-OUTSPC character
CMP #$20 ; skip OUTSPCs
BEQ INSKSP
BNE CHKCR ; check for CR
INP1CR JSR IN1SP1
INCKCR JSR CHRIN ; get character and check if it's CR
CHKCR CMP #$0D
RTS
; ----------------------------------------------------------------------------
; load/save/validate [LSV]
LSV LDY #$01
STY FA
LDA #$00
LDX #$65
LDY #$03
JSR SETNAM
TAY
JSR INXSP3
LDA CURCMD
CMP #$53
BNE bC9DA
JSR INCKCR
BEQ ERROR1
JSR IN0SP1
bC9DA JSR INSKSP
BEQ bCA08
CMP #$22
bC9E1 BNE ERROR1
bC9E3 JSR CHRIN
CMP #$22
BEQ bC9F5
STA (FNADR),Y
INC FNLEN
INY
CPY #$51
BCC bC9E3
BCS ERROR1
bC9F5 JSR INCKCR
BEQ bCA08
JSR IN12HX
AND #$1F
BEQ ERROR1
STA FA
JSR INSKSP
BNE bC9E1
bCA08 LDA #$00
STA SA
LDA CURCMD
CMP #$53
BNE bCA1F
LDA #$FB
LDX PARAM1
LDY PARAM1+1
JSR SAVE
BEEP4 JMP BEEP
bCA1F EOR #$4C
BEQ bCA25
LDA #$01
bCA25 LDX PARAM3
LDY PARAM3+1
JSR LOAD
LDA STATUS
AND #$10
BEQ BEEP4
LDA #$69
LDY #$A3
JSR STROUT
JMP ERROR
; ----------------------------------------------------------------------------
; jump to subroutine [J]
JMPTO JSR INXSP3 ; get jump address optionally proceeded by spaces
JSR JMPP3 ; jump to address specified
JMP BEEP ; back to main loop
; ----------------------------------------------------------------------------
; calculate offset [O]
OFFSET JSR INXSP3
JSR INCP3
JSR INCP3
JSR IN1SP1
JSR OUTSPC
JSR CDIF13
BCC bCA63
TYA
BNE ERROR2
LDA DIFF
BMI ERROR2
BPL bCA6B
bCA63 INY
BNE ERROR2
LDA DIFF
BPL ERROR2
bCA6B JSR OUTBYT
JMP BEEP
ERROR2 JMP ERROR
; ----------------------------------------------------------------------------
; convert hex [$]
HEXCNV JSR INXSP3
JSR sCA8A
JMP BEEP
sCA7D JSR CRLF
sCA80 LDX #$2E
LDA #$24
JSR OUTXA
JSR OUTP3
sCA8A JSR sCAEA
sCA8D JSR sCAB0
JSR OUTSPC
JSR sCA96
sCA96 JSR sCA99
sCA99 JSR OUTSPC
LDX #$04
bCA9E LDA #$30
CLC
ASL PARAM2
ROL PARAM2+1
ADC #$00
JSR CHROUT
DEX
BNE bCA9E
bCAAF RTS
sCAB0 LDA PARAM3+1
LDX PARAM3
STA PARAM2+1
STX PARAM2
JSR OUTSPC
LDA PARAM3+1
JSR sCAC4
LDA PARAM3
sCAC4 TAX
JSR OUTSPC
TXA
AND #$7F
CMP #$20
PHP
BCS bCADA
LDA #$12
JSR CHROUT
TXA
CLC
ADC #$40
TAX
bCADA TXA
JSR CHROUT
LDA #$00
STA QTSW
PLP
BCS bCAAF
RVSOFF LDA #$92 ; turn off reverse video
JMP CHROUT
sCAEA JSR OUTSPC
LDX PARAM3
LDA PARAM3+1
sCAF1 JMP LINPRT
; ----------------------------------------------------------------------------
; convert decimal [#]
DECCNV JSR sCB05
BCS ERROR3
JSR OUTSPC
JSR OUTP3
JSR sCA8D
JMP BEEP
sCB05 LDX #$04
LDA #$00
STA PARAM3+1
JSR EATSPC
JSR sCB2B
STA PARAM3
bCB13 JSR sCB22
JSR sCB3D
DEX
BNE bCB13
PHP
JSR OUTSPC
PLP
RTS
sCB22 JSR INCKCR
BEQ bCB36
CMP #$20
BEQ bCB36
sCB2B CMP #$30
BCC ERROR3
CMP #$3A
BCS ERROR3
AND #$0F
RTS
bCB36 PLA
PLA
CLC
RTS
ERROR3 JMP ERROR
sCB3D STA PARAM1+1
LDA PARAM3+1
PHA
LDA PARAM3
PHA
ASL PARAM3
ROL PARAM3+1
ASL PARAM3
ROL PARAM3+1
PLA
ADC PARAM3
STA PARAM3
PLA
ADC PARAM3+1
STA PARAM3+1
ASL PARAM3
ROL PARAM3+1
LDA PARAM1+1
ADC PARAM3
STA PARAM3
LDA #$00
ADC PARAM3+1
STA PARAM3+1
RTS
; ----------------------------------------------------------------------------
; convert ASCII ["]
ASCII JSR EATSPC
STA PARAM2+1
PHA
PHA
JSR OUTSPC
JSR OUTSPC
PLA
JSR OUTBYT
JSR OUTSPC
PLA
TAX
LDA #$00
JSR sCAF1
JSR OUTSPC
JSR sCA96
JMP BEEP
; ----------------------------------------------------------------------------
; convert binary [%]
BINCNV JSR sCB9F
JSR OUTSPC
JSR OUTP3
JSR sCAEA
JSR sCAB0
JMP BEEP
sCB9F LDX #$0F
LDA #$00
STA PARAM3
STA PARAM3+1
JSR EATSPC
JSR sCB2B
JSR sCBBC
bCBB0 JSR sCB22
JSR sCBBC
DEX
BNE bCBB0
JMP OUTSPC
sCBBC LSR
ROL PARAM3
ROL PARAM3+1
RTS
; consume OUTSPCs in input
EATSPC JSR INNOCR
CMP #$20
BEQ EATSPC
RET2 RTS
; ----------------------------------------------------------------------------
; enable tone [(]
TONEON LDA #$0F
STA SIGVOL
LDA #$00
STA ATDCY1
LDA #$F0
LDX #$44
LDY #$95
jCBDA STA SUREL1
STX $D401 ;Voice 1: Frequency Control - High-Byte
STY $D400 ;Voice 1: Frequency Control - Low-Byte
JMP NOBEEP
BRK
sCBE7 JSR IN1SP1
JMP IN1PAR
; ----------------------------------------------------------------------------
; addition [+]
ADDIT JSR sCBE7
CLC
LDA PARAM3
ADC PARAM1
STA PARAM3
LDA PARAM3+1
ADC PARAM1+1
STA PARAM3+1
JMP jCC0D
; ----------------------------------------------------------------------------
; subtraction [-]
SUBTR JSR sCBE7
JSR CDIF13
STY PARAM3+1
LDA DIFF
STA PARAM3
jCC0D JSR OUTSPC
JSR OUTP3
JMP BEEP
; ----------------------------------------------------------------------------
; disable tone [)]
TONEOFF LDA #$00
TAX
TAY
STA SIGVOL
JMP jCBDA
BRK
; ----------------------------------------------------------------------------
; exit monitor [X]
EXITM SEI
JSR RESTORD
CLI
LDA #$3C
STA TAPE1
; ----------------------------------------------------------------------------
; exit and unregister [E]
EXITB LDX SP
TXS
LDA CHRGET
CMP #$E6
BEQ RET2
JMP (BASVEC)
; ----------------------------------------------------------------------------
; compute checksum [&]
CHKSUM JSR sCBE7
JSR SWAP13
JSR OUTSPC
LDY #$00
STY PARAM2
STY PARAM2+1
jCC48 JSR CDIF13
BCC bCC68
LDY OVRFLW
BNE bCC68
CLC
LDA (PARAM3),Y
ADC PARAM2
STA PARAM2
TYA
ADC PARAM2+1
STA PARAM2+1
JSR INCP3
JMP jCC48
bCC68 LDA PARAM2+1
JSR OUTBYT
LDA PARAM2
JSR OUTBYT
JMP BEEP
; ----------------------------------------------------------------------------
; irq stuff
IRQRET LDA a0354
BNE PLREG1
LDA NDX
BNE bCC83
PLREG1 JMP PLREG
bCC83 LDA KEYD
CMP #$11
BNE bCD07
LDA TBLX
CMP #$18
BNE PLREG1
LDA PNT
STA PARAM1
LDA PNT+1
STA PARAM1+1
LDA #$19
STA a035E
bCC9D LDY #$01
JSR sCE54
CMP #$3A
BEQ bCCC0
CMP #$2C
BEQ bCCC0
CMP #$24
BEQ bCCC0
DEC a035E
BEQ PLREG1
SEC
LDA PARAM1
SBC #$28
STA PARAM1
BCS bCC9D
DEC PARAM1+1
BNE bCC9D
bCCC0 STA CURCMD
JSR sCE0D
BCS PLREG1
LDA CURCMD
CMP #$3A
BNE bCCE0
CLC
LDA PARAM3
ADC #$08
STA PARAM3
BCC bCCDA
INC PARAM3+1
bCCDA JSR MEMLIN
JMP jCCF4
bCCE0 CMP #$24
BEQ bCCFE
JSR INSTP3
JSR INC3OP
LDA #$00
STA BUFIDX
LDY #$2C
JSR DISLIN
jCCF4 LDA #$00
STA NDX
JMP RETMAIN
PLREG2 JMP PLREG
bCCFE JSR INCP3
JSR sCA7D
JMP jCCF4
bCD07 CMP #$91
BNE PLREG2
LDA TBLX
BNE PLREG2
LDA PNT
STA PARAM1
LDA PNT+1
STA PARAM1+1
LDA #$19
STA a035E
bCD1C LDY #$01
JSR sCE54
CMP #$3A
BEQ bCD3F
CMP #$2C
BEQ bCD3F
CMP #$24
BEQ bCD3F
DEC a035E
BEQ PLREG3
CLC
LDA PARAM1
ADC #$28
STA PARAM1
BCC bCD1C
INC PARAM1+1
BNE bCD1C
bCD3F STA CURCMD
JSR sCE0D
BCC bCD4A
PLREG3 JMP PLREG
bCD4A LDA CURCMD
CMP #$3A
BEQ bCD57
CMP #$24
BEQ bCD72
BNE bCD7E
bCD57 JSR sCDD0
SEC
LDA PARAM3
SBC #$08
STA PARAM3
BCS bCD65
DEC PARAM3+1
bCD65 JSR sC3CB
jCD68 LDA #$00
STA NDX
JSR sCE08
JMP PROMPT
bCD72 JSR sCDD0
JSR DECP3
JSR sCA80
JMP jCD68
bCD7E JSR sCDD0
LDA PARAM3
LDX PARAM3+1
STA PARAM1
STX PARAM1+1
LDA #$10
STA a035E
bCD8E SEC
LDA PARAM1
SBC a035E
STA PARAM3
LDA PARAM1+1
SBC #$00
STA PARAM3+1
bCD9C JSR INSTP3
JSR INC3OP
JSR CDIF13
BEQ bCDAE
BCS bCD9C
DEC a035E
BNE bCD8E
bCDAE INC OPDLEN
LDA OPDLEN
JSR SUBAP3
LDX #$00
LDA (PARAM3,X)
STX BUFIDX
LDA #$2C ; comma
JSR ACCDOT
JSR DISNPF
JMP jCD68
INSTP3 LDX #$00
LDA (PARAM3,X)
JMP INSTXX
sCDD0 LDX PNT+1
JSR sCDD7
LDX USER+1
sCDD7 INX
INX
INX
STX SAL+1
STX PARAM1+1
LDX #$00
STX SAL
LDA #$28
STA PARAM1
LDY #$C0
LDX #$03
bCDEA DEY
LDA (SAL),Y
STA (PARAM1),Y
TYA
BNE bCDEA
DEC SAL+1
DEC PARAM1+1
DEX
BPL bCDEA
LDA #$20
LDX PNT+1
STX PARAM1+1
STY PARAM1
LDY #$27
bCE03 STA (PARAM1),Y
DEY
BPL bCE03
sCE08 LDA #$13
JMP CHROUT
sCE0D CPY #$28
BNE bCE13
SEC
RTS
bCE13 JSR sCE54
CMP #$20
BEQ sCE0D
DEY
JSR sCE3D
TAX
JSR sCE3D
STA PARAM3
STX PARAM3+1
LDA #$FF
STA a0354
STA BLNSW
LDA BLNON
BEQ bCE3B
LDA GDBLN
LDY PNTR
STA (PNT),Y
LDA #$00
STA BLNON
bCE3B CLC
RTS
sCE3D JSR sCE54
JSR HD2NYB
ASL
ASL
ASL
ASL
STA TMPNYB
JSR sCE54
JSR HD2NYB
ORA TMPNYB
RTS
sCE54 LDA (PARAM1),Y
INY
AND #$7F
CMP #$20
BCS bCE5F
ORA #$40
bCE5F RTS
; ----------------------------------------------------------------------------
; output micromon banner
BANNER LDA $CD98,X ; load char from banner address - $D7 + X
JSR CHROUT ; output character
INX ; loop until all chars output
BNE BANNER
RTS
.BYTE $00,$00,$00,$00,$00
.BYTE $93,$11,$20,$20,$12
.TEXT " MICROMON-64 COMPUTE! BOOKS "
; -----------------------------------------------------------------------------
; 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 .TEXT ",",")",",","#","(","$"
CHAR2 .TEXT "Y",$00,"X","$","$",$00
; -----------------------------------------------------------------------------
; 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
; -----------------------------------------------------------------------------
; register header
REGNAM .TEXT $0D, " PC IRQ SR AC XR YR "
; -----------------------------------------------------------------------------
; command dispatch table: letters in CMDCHR match addresses in CMDVEC 1-1
CMDCHR .TEXT "SPABC"
.TEXT "DFGHL"
.TEXT "MNQR("
.TEXT "TWX,:"
.TEXT ";$#", $22, "+"
.TEXT "-OIJ%"
.TEXT "&EV)>", $FF, $FF
CMDVEC .WORD LSV,PRINTSW,ASM,BRKSET,COMPARE
.WORD DISASM,FILL,GOTO,HUNT,LSV
.WORD MEMDSP,NEWLOC,QTRACE,REGDSP,TONEON
.WORD TRNSFR,WALK,EXITB,REASM,MODMEM
.WORD REGMOD,HEXCNV,DECCNV,ASCII,ADDIT
.WORD SUBTR,OFFSET,ERROR,JMPTO,BINCNV
.WORD CHKSUM,EXITM,LSV,TONEOFF,DIR
.WORD ERROR,ERROR
.BYTE $FF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment