; ACORN monitor, written in TASM-compatible assembly language, ; incorporating commentary from original text (corrected where ; necessary) and using original names where possible. ; [Mike Cowlishaw, 2002.01.06] ; ; ACORN MONITOR ; ; --------------------------------------------------------------- ; Monitor variables (RAM $0000-$001F) ; --------------------------------------------------------------- .dseg .org $0000 MAP .block 2 ; M address (Low and high bytes) GAP .block 2 ; Go address PAP .block 2 ; breakPoint address FAP .block 2 ; tape From address TAP .block 2 ; tape To address R0 .block 1 ; Register 0: contains A after Break R1 .block 1 ; Register 1: contains X after Break R2 .block 1 ; Register 2: contains Y after Break R3 .equ * ; Register 3: temporarily P after Break, KEY .block 1 ; contains last pressed key for display REPEAT .block 1 ; MSB=1 sets repeatedly scanned display, ; otherwise single scan EXEC .block 1 ; Execution status of the key processing ; routine [must precede D immediately] D .equ * ; Base address of the eight displayed ; memory locations R4 .block 1 ; Register 4: temporarily PCH after Break, R5 .block 1 ; Register 5: temporarily PCL after Break, R6 .block 1 ; Register 6: temporarily 01 after Break, R7 .block 1 ; Register 7: temporarily S after Break, .block 4 ; Last 4 displayed memory locations P .block 1 ; Single level of storage for previous ; data at break point COL .block 1 ; Column of key currently being processed TX .equ * ; Temporary storage for X (in DISPLAY) or TY .block 1 ; Y (various places) RECAL .block 1 ; Contains PC recalculation factor for ; break [Note: not set by monitor] USERNMI .block 2 ; Address of User's NMI program USERIRQ .block 2 ; Address of User's IRQ program ; --------------------------------------------------------------- ; Processor stack (RAM $0100-$01FF) ; --------------------------------------------------------------- .org $0100 STACK .block 256 ; The stack page ; --------------------------------------------------------------- ; First RAM/IO PIO ; --------------------------------------------------------------- .org $0E20 X1PIA .block 1 ; 20 A Programmable I/O X1PIB .block 1 ; 21 B Programmable I/O X1ADDR .block 1 ; 22 A Data direction register X1BDDR .block 1 ; 23 B Data direction register ; --------------------------------------------------------------- ; First RAM/IO RAM ($0E80-$0EFF; not used by monitor) ; --------------------------------------------------------------- .org $0E80 .block 128 ; --------------------------------------------------------------- ; Monitor code in PROM ($FE00-$FFFF) ; --------------------------------------------------------------- .cseg .org $FE00 ; --------------------------------------------------------------- ; QUAD: DISPLAY THE 4 BYTES AT X-3,X-2, X-1 & X IN THAT ; ORDER ON THE DISPLAY ; --------------------------------------------------------------- QUAD ldy #$06 ; - LOOP COUNTER STILL lda 0,X ; - GET THE BYTE POINTED TO BY X jsr DHEXTD ; - USE DOUBLE HEX TO DISPLAY ; ROUTINE dex ; - NEXT X dey ; - NEXT Y POSITION dey ; bpl STILL ; - FALL INTO DISPLAY WHEN ; FINISHED - Y WAS POSITION & ; ALSO LOOP COUNTER ; --------------------------------------------------------------- ; DISPLAY: STROBE KEYBOARD, MULTIPLEX DISPLAY, RETURN WITH KEY ; INFORMATION ; --------------------------------------------------------------- DISPLAY stx TX ; - SAVE X!!!! RESCAN ldx #$07 ; - SCAN 8 DIGITS, NO MATTER WHAT stx X1ADDR ; - SET UP DATA DIRECTION ; REGISTER SCAN ldy #$00 ; - CLEAR Y FOR LATER USE lda D,X ; - GET DISPLAY DATA FROM THE ; ZERO PAGE MEMORY sta X1PIB ; - & PUT IT ONTO SEGMENTS stx X1PIA ; - SET DIGIT DRIVE ON AND THE KEY ; COLUMNS lda X1PIA ; - GET KEY DIGIT BACK and #$3F ; - REMOVE SURPLUS TOP BITS bit EXEC ; - CHECK STATUS ='1' MEANS NOT ; PROCESSING A KEY bpl BUTTON ; - BUT 0 MEANS THAT WE ARE ; [the next instruction is not in the ROM] ; bvs DELAY ; - THUS CAN BE BLOWN TO AN ; ; ESCAPE FROM THE DISPLAY ; ; ROUTINE ALTOGETHER ON STATUS ; ; C0 AT THE MOMENT IT IGNORES ; ; KEYS IF GIVEN THIS STATUS cmp #$38 ; - CHECK FOR ALL 1'S ROW INPUT ; FROM KEYBOARD = SET COPY IF SO bcs DELAY ; - IF ALL 1's THEN NO KEY HAS BEEN ; PRESSED stx COL ; - STORE THE PRESSED KEY'S ; COLUMN INFORMATION lda #$40 ; - SET STATUS TO "WE ARE ; PROCESSING A KEY" KEYCLEAR sta EXEC ; ; [change here from published monitor; the LDA Indirect ; has been added to increase the delay time] DELAY lda (0,X) ; - (new) TIME PADDING ONLY dey ; - Y WAS ZERO SO HERE IS A 256x12uS ; DELAY (~ 3ms) bne DELAY ; - Y WILL BE ZERO ON EXIT dex ; bpl SCAN ; - IF X WAS STILL +VE, CONTINUE ; THIS SCAN lda REPEAT ; - IF WE SHOULD CONTINUE ; SCANNING THEN TOP BIT IS SET bmi RESCAN ; - CONTINUE SCANNING bpl OUTPUT ; - IF TOP BIT IS ZERO, THEN USE THIS ; DATA AS THE KEY ITSELF BUTTON cpx COL ; - ARE WE ON THE SAME KEY'S ; COLUMN? bne DELAY ; - NO cmp #$38 ; - HAS A KEY ACTUALLY BEEN ; PRESSED? bcc PRESSED ; - YES lda #$80 ; - NO, THEN CLEAR THE EXECUTION ; STATUS - THE KEY HAS BEEN ; PRESSED & RELEASED bne KEYCLEAR ; - ALWAYS BRANCH PRESSED cmp EXEC ; - A KEY HAS BEEN PRESSED beq DELAY ; - BUT IT HAS ALREADY BEEN ; EXECUTED sta EXEC ; - SET IT AS BEING EXECUTED eor #$38 ; - JIGGERY POKERY TO ENCODE THE ; ROW INPUTS TO BINARY OUTPUT and #$1F ; - ALSO ENSURE THE KEY IN REPEAT ; WAS OF REASONABLE SIZE cmp #$10 ; - A HEX KEY OR NOT? CARRY CLEAR ; IF HEX sta KEY ; - PUT THE KEY IN A TEMP LOCATION ; FOR FURTHER USE (BY "MODIFY") ldx TX ; - RETRIEVE X sty X1PIB ; - TURN THE SEGMENT DRIVES OFF rts ; - AND RETURN ; --------------------------------------------------------------- ; MHEXTD: MEMORY HEX TO DISPLAY: DISPLAY A MEMORY BYTE ON ; RIGHT OF DISPLAY ; --------------------------------------------------------------- MHEXTD lda (0,X) ; - MEMORY HEX TO DISPLAY = GET A ; BYTE FROM MEMORY ; --------------------------------------------------------------- ; RDHEXTD: RIGHT DIGITS HEX TO DISPLAY: DISPLAY A ON RIGHT OF ; DISPLAY ; --------------------------------------------------------------- RDHEXTD ldy #$06 ; - RIGHT (OF DISPLAY) DOUBLE HEX ; TO DISPLAY : SET Y TO RIGHT OF ; DISPLAY bne DHEXTD ; - AND USE DHEXTD ; --------------------------------------------------------------- ; QHEXTD1: QUAD HEX TO DISPLAY; DISPLAY (X) AND (X+1) BYTES ON ; DISPLAYS 1, 2, 3 & 4 ; --------------------------------------------------------------- QHEXTD1 ldy #$03 ; - SET Y TO USE POSNS 1,2,3 & 4 ; --------------------------------------------------------------- ; QHEXTD2: DISPLAY (X) AND (X+1) BYTES ON DISPLAYS Y-2, Y-1, ; Y & Y+1 ; --------------------------------------------------------------- QHEXTD2 lda 0,X ; - GET THE DATA jsr DHEXTD ; - AND USE DHEXTD dey ; dey ; - HAVING DECREMENTED THE ; POSITION lda 1,X ; - GET THE HIGH BYTE OF THE DATA ; & USE DHEXTD ; --------------------------------------------------------------- ; DHEXTD: DOUBLE HEX TO DISPLAY: DISPLAY A ON DISPLAYS Y & ; Y+1 ; --------------------------------------------------------------- DHEXTD iny ; - DOUBLE HEX TO DISPLAY : FIRST ; HEX ON RIGHTEST POSITION pha ; - SAVE A jsr HEXTD ; - USE HEX TO DISPLAY dey ; - GET Y BACK TO CORRECT ; POSITION pla ; - RETRIEVE A lsr A ; lsr A ; lsr A ; lsr A ; - ORIENTED FOR OTHER HEX DIGIT ; --------------------------------------------------------------- ; HEXTD: HEX TO DISPLAY: DISPLAY BOTTOM 4 BITS OF A ON ; DISPLAY Y ; --------------------------------------------------------------- HEXTD sty TY ; - HEX TO DISPLAY = SAVE Y and #$0F ; - REMOVE SURPLUS BITS FROM A tay ; - & PUT IT IN Y LDA FONT,Y ; - GET THE 7 SEGMENT FORM ldy TY ; - RETRIEVE Y sta D,Y ; - AND POSITION THE 7 SEG FORM ON ; THE DISPLAY rts ; ; --------------------------------------------------------------- ; QDATFET: QUAD DATA FETCH: FETCH AN ADDRESS INTO (X) & (X+1) ; --------------------------------------------------------------- QDATFET jsr QHEXTD1 ; - QUAD DATA FETCH - DISPLAY OLD ; DATA jsr DISPLAY ; - GET KEY bcs RETURN ; - NON HEX RETURN ldy #$04 ; - LOOP COUNTER asl A ; asl A ; asl A ; asl A ; - DIGIT IN A IN CORRECT PLACE SHIFTIN asl A ; - MULTI SHIFT TO GET DIGIT INTO ; MEMORY rol 0,X ; - INDEXED rol 1,X ; (high byte) dey ; bne SHIFTIN ; - KEEP SHIFTING IN beq QDATFET ; - GO AND DO IT ALL AGAIN ; --------------------------------------------------------------- ; COM16: COMPARE 16: INCREMENT (X+6) & (X+7) AND COMPARE ; WITH (X+8) & (X+9) ; --------------------------------------------------------------- COM16 inc $06,X ; - INCREMENT & COMPARE 16 BIT ; NOS - INCREMENT LOWER bne NOINC ; - NO HIGH INCREMENT inc $07,X ; ; --------------------------------------------------------------- ; NOINC: COMPARE 16 WITHOUT INCREMENT: COMPARE (X+6) & (X+7) ; WITH (X+8) & (X+9) ; --------------------------------------------------------------- NOINC lda $06,X ; - LOW BYTE EQUALITY TEST cmp $08,X ; bne RETURN ; - NO NEED TO DO HIGH BYTE lda $07,X ; - HIGH BYTE EQUALITY TEST cmp $09,X ; RETURN rts ; ; --------------------------------------------------------------- ; PUTBYTE: PUT A TO TAPE, DO 1 START BIT & 1 STOP BIT, NO ; PARITY ; --------------------------------------------------------------- PUTBYTE ldy #$40 ; - PUT BYTE TO TAPE - CONFIGURE ; I/O PORT sty X1ADDR ; ldy #$07 ; - LOOPCOUNTER sty X1PIA ; - AND SEND THE START BIT ror A ; ror A ; - BACK A UP A COUPLE OF BITS AGAIN jsr WAIT ; - WAIT TO SEND OUT RESET BIT ror A ; - SENDING ORDER IS BIT 0 -> BIT 7 sta X1PIA ; - SEND BIT dey ; bpl AGAIN ; - KEEP GOING jsr WAIT ; - WAIT FOR THAT BIT TO END sty X1PIA ; - SEND STOP BIT : Y IS FF ; --------------------------------------------------------------- ; WAIT: WAIT FOR CASSETTE TIMING ; --------------------------------------------------------------- WAIT jsr HALFWAIT ; - 300 BAND WAITING TIME - IN TWO ; PARTS ; --------------------------------------------------------------- ; HALFWAIT: HALF THE WAIT ; --------------------------------------------------------------- HALFWAIT sty TY ; - 1/2 THE WAITING TIME - SAVE Y ldy #$48 ; - 72 X 5uS DELAY WAIT1 dey ; - PART ONE OF THE WAIT bne WAIT1 ; WAIT2 dey ; - Y WAS ZERO ON ENTRY - 256 x 5uS bne WAIT2 ; DELAY ldy TY ; - RETRIEVE Y rts ; ; --------------------------------------------------------------- ; GETBYTE: GET BYTE FROM TAPE TO A, WAIT FOR A START BIT, ; CENTRE TIMING ; --------------------------------------------------------------- GETBYTE ldy #$08 ; - GET BYTE FROM TAPE - LOAD ; COUNTER START bit X1PIA ; - WAIT FOR 1 -> 0 TRANSISITON - bmi START ; A START BIT jsr HALFWAIT ; - WAIT HALF THE TIME, SO ; SAMPLING IN THE CENTRE INPUT jsr WAIT ; - FULL WAIT TIME BETWEEN ; SAMPLES asl X1PIA ; - GET SAMPLE INTO CARRY ror A ; - AND INTO A dey ; bne INPUT ; - KEEP GOING beq WAIT ; - USE WAIT TO GET OUT ONTO ; THE STOP BIT HIGH ; --------------------------------------------------------------- ; RESET: ENTRY TO MONITOR ; --------------------------------------------------------------- RESET ldx #$FF ; - MAIN PROGRAM txs ; - INITIALIZE STACK stx X1BDDR ; - AND B DATA DIRECTION REGISTER stx REPEAT ; - MULTI-SCAN DISPLAY MODE INIT ldy #$80 ; - THE FAMILIAR DOT ON THE ; DISPLAY ldx #$09 ; - ALL EIGHT DISPLAYS AND ; INITIALIZE EXEC ROUND sty REPEAT,X ; - Y USED FOR AMUSEMENT dex ; bne ROUND ; - X ZERO ON EXIT, SO UP & DOWN ; IMMEDIATELY VALID ($00 -> MAP) ; --------------------------------------------------------------- ; RESTART: RE-ENTRY TO RUNNING MONITOR ; --------------------------------------------------------------- RESTART jsr DISPLAY ; - MARK RETURN TO MONITOR POINT ; DISPLAY DISPLAY & GET KEY REENTER bcc INIT ; - HEX KEY GETS THE DOTS BACK SEARCH and #$07 ; - REMOVE ANY STRAY BITS ; (EFFECTIVELY SUBTRACT $10) cmp #$04 ; bcc FETADD ; - KEYS OF VALUE LESS THAN 4 ; NEED AN ADDRESS beq LOAD ; - KEY 4 IS THE LOAD KEY TEST6 cmp #$06 ; beq UP ; - KEY 6 IS UP bcs DOWN ; - & KEY 7 IS DOWN ; "RETURN" command RETURN2 lda R0 ; - MUST BE KEY 5 - GET A BACK ldx R1 ; - GET X BACK ldy R2 ; - GET Y BACK rti ; - GET P & PC BACK & CONTINUE ; FROM WHERE YOU WERE ; "UP" command UP inc 0,X ; - 16 BIT INDEXED INCREMENT bne ENTERM ; inc 1,X ; bcs ENTERM ; - A BRANCH ALWAYS: THE CARRY ; WAS SET BY THE COMPARE AT TEST6 ; "DOWN" command DOWN lda 0,X ; - 16 BIT INDEXED DECREMENT bne NODEC ; dec 1,X ; NODEC dec 0,X ; ENTERM jsr QHEXTD1 ; - NOW DISPLAY THE VALUE jmp MODIFY ; - AND GET INTO THE MODIFY ; SECTION FETADD sty D+6 ; - CLEAR DISPLAYS 6 sty D+7 ; - & 7 - Y WAS ZERO ON EXIT FROM ; DISPLAY asl A ; - DOUBLE A tax ; - THE ZERO PAGE ADDRESSES MAP, ; GAP, PAP & FAP eor #$F7 ; - FIX UP DIGIT 0 COMMAND SYMBOL sta D ; jsr QDATFET ; - FETCH THE ADDRESS, INTO MAP, ; GAP, PAP OR FAP cpx #$02 ; - CHECK X TO FIND OUT WHICH ; COMMAND WE'RE DOING bcs N1 ; - MUST BE 2,4 OR 6 - AS 0 IS .. ; "MODIFY" command MODIFY jsr MHEXTD ; - DISPLAY THE MEMORY jsr DISPLAY ; - AND GET KEY bcs SEARCH ; - IF NOT HEX DO OVER lda (0, X) ; - HEX SO GET OLD INFO asl A ; asl A ; asl A ; asl A ; - MOVED ALONG ora KEY ; - AND PUT IN NEW INFO sta (0, X) ; - AND PUT IT BACK jmp MODIFY ; - THEN KEEP DOING IT N1 bne N2 ; - MUST BE 4 OR 6 AS 2 IS .. ; "GO" command GO jmp (GAP) ; - THE VERY SIMPLE GO N2 cpx #$04 ; - IS IT 4 OR 6? beq POINT ; - WELL IT'S NOT 4 ; "STORE" command STORE ldx #TAP ; - SO IT MUST BE 6 - X NOW POINTS ; TO TAP stx D ; - GIVE PROMPT jsr QDATFET ; - AND GET 2ND STORE INFO ldx #$04 ; - LOOP COUNT ADDRESS lda FAP-1,X ; - SEND ADDRESSES TO TAPE jsr PUTBYTE ; dex ; bne ADDRESS ; - X NEATLY ZEROED ON EXIT DATAS lda (FAP,X) ; - DATA SEND - GET INFO FROM ; MEMORY jsr PUTBYTE ; - AND SEND IT TO TAPE jsr COM16 ; - Increment and SEE IF FINISHED bne DATAS ; - NO beq WAYOUT ; - YES ; "LOAD" command LOAD ldx #$04 ; - LOOP COUNTER ADDRSL jsr GETBYTE ; - RESCUE ADDRESSES FROM TAPE sta FAP-1,X ; - PUT THEM IN FAP & TAP, THOUGH ; IT COULD BE ELSEWHERE dex ; bne ADDRSL ; - X NEATLY ZEROED AGAIN DATAL jsr GETBYTE ; - GET DATA FROM TAPE sta (FAP,X) ; - AND STORE IT IN MEMORY sta X1PIB ; - AND ON THE DISPLAY SO IT CAN BE ; SEEN jsr COM16 ; - Increment and SEE IF FINISHED bne DATAL ; - NO beq WAYOUT ; - YES ; "POINT" command POINT lda (0,X) ; - SET/CLEAR BREAK POINT - GET ; DATA FROM ADDRESSED MEMORY beq SET ; - IF ZERO BREAK POINT HAS ; ALREADY BEEN SET = MUST CLEAR ; IT sta P ; - NOT ZERO SO SAVE THE ; INFORMATION lda #$00 ; - AND PUT IN A BREAK POINT beq OUT ; SET lda P ; - WAS SET SO GET OLD ; INFORMATION BACK OUT sta ($00,X) ; - INSERT BREAK POINT OR OLD ; INFORMATION jsr MHEXTD ; - NOW READ IT OUT AGAIN TO ; REVEAL ROM WAYOUT jmp RESTART ; - GO BACK & DO IT ALL OVER AGAIN NMI jmp (USERNMI) ; - INDIRECTION ON NMI IRQ jmp (USERIRQ) ; - INDIRECTION ON IRQ ; --------------------------------------------------------------- ; BREAK: ENTRY TO MONITOR FROM BRK INSTRUCTION, DISPLAY CPU ; --------------------------------------------------------------- BREAK sta R0 ; - WHEN THE IRQ/BREAK VECTOR ; POINTS HERE THEN DISPLAY ; EVERYTHING - SAVE A stx R1 ; - SAVE X sty R2 ; - SAVE Y pla ; - GET P OFF STACK pha ; - PUT IT BACK FOR FUTURE USE sta R3 ; - STORE P IN REGISTER 3 ldx #R3 ; - SET X TO POINT AT REGISTERS ; 3 -> 0 FOR QUAD lda #$FF ; - KILL POSSIBILITY OF DISPLAY ; BEING ON SINGLE SCAN sta REPEAT ; jsr QUAD ; - USE QUAD TO WRITE OUT A X Y P ; (and wait for key) tsx ; - GET STACK POINTER stx R7 ; iny ; - Y ZERO SINCE QUAD ENDED WITH ; DISPLAY SO THIS FORMS 01 sty R6 ; cld ; - CLEAR DECIMAL MODE FOR BINARY ; SUBTRACT - DOESN'T AFFECT ; USER SINCE P IS STACKED lda STACK+2,X ; - GET PCL OFF STACK sec ; sbc RECAL ; - CORRECT IT BY AMOUNT IN RECAL sta STACK+2,X ; - PUT IT BACK ON STACK sta R5 ; - AND STORE IT FOR QUAD lda STACK+3,X ; - PCH OFF STACK sbc #$00 ; - REST OF TWO BYTE SUBTRACTION sta STACK+3,X ; - PUT IT BACK ON STACK sta R4 ; - AND STORE IT FOR QUAD ldx #R7 ; - POINT X AT THESE REGISTERS - ; QUAD WILL DESTROY THEM jsr QUAD ; - QUAD WRITES OUT PC SP ; (and waits for key) jmp REENTER ; - AND THE WHOLE SHEBARG STARTS ; OVER AGAIN ; --------------------------------------------------------------- ; FONT: SEVEN SEGMENT PICTURES OF THE HEX DIGITS ; --------------------------------------------------------------- ; [the bits are: 0(MSB)=d.p; 1=centre; 2=left-top; 3=left-bot; ; 4=bot; 5=right-bot; 6=right-top; 7=top;] FONT .byte $3F,$06,$5B,$4F ; '0'-'3' .byte $66,$6D,$7D,$07 ; '4'-'7 .byte $7F,$6F,$77,$7C ; '8','9','A','b' .byte $58,$5E,$79,$71 ; 'c','d','E','F' NMIVEC .word NMI ; - POINT TO THE ADDED INDIRECTION RSTVEC .word RESET ; - POINT TO THE RESET ENTRY POINT IRQVEC .word IRQ ; - POINT TO THE ADDED INDIRECTION .end