Keyboard Input Routines
and Character Coded Output
on the 6801
Hopefully, you've read through, assembled, and tested the 6800 code, and
understand what the program is doing.
* simple 8-bit hexadecimal output for 6801
* using parameter stack,
* with test frame
* Joel Matthew Rees, October 2024
*
OPT 6801
EXP rt_rig03_6801.asm
****************
* Program code:
*
* Essential monitor ROM parameters
* ECHO suppress -- set non-zero before each call to XINCH/XINCHN
* to suppress echo for that call only.
* Not the same address as 6809 EXBUG09.
AECHO EQU $FF53 ; ECHO suppress per-call
*
* Essential monitor ROM routines
XINCH EQU $F012 ; wait for key, return in A, preserves B, X, see AECHO
*
* Wait for input, return character on PSP (and in A:B)
* Call INCHNE for no echo.
INCHNE INC AECHO ; XINCH always clears it.
INCHAR BSR INCHV
TAB
CLRA
JSR PPSHD
RTS
*
INCHV JMP XINCH ; returns with character in A
*
*
PROMPT FCB CR,LF ; Put message at beginning of line
FCC "Type any key, Q to quit. " ;
FCB CR,LF ; Put the prompt on a new line
FCB NUL
KEYCOL FCC "KEY:"
FCB NUL
COLBIN FCB ASCCOL,NUL
COLHEX FCC ": $"
FCB NUL
*
* Duplicate 16-bit word on P stack
PDUP LDX PSP
LDD 0,X
DEX
DEX
STX PSP
STD 0,X
RTS
*
*
*
*
PGSTRT LDX #PROMPT
JSR PPSHX
JSR OUTS
JSR INCHNE ; Hold off echo
JSR PDUP
LDX #KEYCOL
JSR PPSHX
JSR OUTS
JSR OUTC ; output character
JSR PDUP
LDX #COLBIN
JSR PPSHX
JSR OUTS
JSR OUTB8
JSR PDUP ; need it for testing
LDX #COLHEX
JSR PPSHX
JSR OUTS
JSR OUTHX8
JSR OUTNWLN
JSR PPOPD ; balance stack
CMPB #ASCQ
BNE PGSTRT
RTS
*
END ENTRY
Again, we've added the keyboard input routine with two entry points, with and
without echo, and the PDUP routine, this time using LDD and STD.
You'll want to compare the 6801 code here with the 6800 code, of course. You'll notice that the differences are mostly at the low-level.
* A simple run-time framework inclusion for 6801
* providing parameter stack and local base
* Version 00.00.03
* Joel Matthew Rees, October 2024
*
* Essential control codes
LF EQU $0A ; line feed
CR EQU $0D ; carriage return
NUL EQU 0
*
* Other essential ASCII codes
ASC0 EQU '0 ; Some assemblers won't handle 'c constants well.
ASC9 EQU '9
ASCA EQU 'A
ASCXGAP EQU ASCA-ASC9-1 ; Gap between '9' and 'A' for hexadecimal
ASCCOL EQU ':
ASCQ EQU 'Q
*
SCRLIN EQU $40 ; More than a screen full of lines.
*
NATWID EQU 2 ; 2 bytes in the CPU's natural integer
*
*
ORG $80 ; MDOS and EXbug docs say it should be okay here.
ENTRY JMP START
NOP ; Just want even addressed pointers for no reason.
*
* These are the page zero context variables that must be
* saved and restored on process context switch.
* They must never be accessed except in leaf routines:
PSP RMB 2 ; parameter stack pointer
LBP RMB 2 ; local static variable base pointer
XWORK RMB 2 ; for stashing X during other very short operations
*
SSAVE RMB 2 ; a place to keep S so we can return clean
* End of page zero context variables.
*
*
ORG $2000 ; MDOS says this is a good place for usr stuff
LOCBAS EQU * ; here pointer, local static base starts here.
NOENTRY JMP START
NOP
RMB 64 ; room for something
RMB 2 ; a little bumper space
* Not much here
*
SSTKLIM RMB 31 ; 16 levels of call, max
SSTKBAS RMB 1 ; 6800 is post-dec (post-store-decrement) push
RMB 2 ; a little bumper space
PSTKLIM RMB 64 ; 16 levels of call at two parameters per call
PSTKBAS RMB 2 ; bumper space -- parameter stack is pre-dec
*
*
INITRT LDX #PSTKBAS ; Set up the run-time environment
STX PSP
LDX #LOCBAS
STX LBP
PULX ; pop return address to X
STS SSAVE ; Save what the monitor gave us.
LDS #SSTKBAS ; Move to our own stack
JMP 0,X ; return via X
*
*
*********************
* Low-level library:
*
* Only alters X
PPOPX LDX PSP
LDX 0,X ; get value for X
PSHX ; push it out of the way
LDX PSP
INX
INX
STX PSP
PULX ; and bring it back.
RTS
*
* Trashes A,B;
* X points to X value just pushed -- PSP top of stack -- at end
PPSHX PSHX ; push X out of the way
PULA
PULB ; Falls through:
*
* X points to PSP top of stack at end
PPSHD LDX PSP
DEX
DEX
STX PSP
STD 0,X
RTS
*
* X points to PSP top of stack at end
PPOPD LDX PSP
LDD 0,X
INX
INX
STX PSP
RTS
*
* Keep this around in comments,
* for reference for when we want to referance tables in code.
* Load a constant from the instruction stream into A:B,
* continue execution after the constant.
* This is not self-modifying code, even though it feels like a trick
* and is playing with the return stack and instruction stream
* in ways we wouldn't think we wanted to think we should.
* Call it a "necessary" bit of run-time syntactic sugar.
*
* Use it like this:
* JSR LD16I ; load D immediate
* FDB $1234 ; "immediate" 16-bit value to load
* JSR SOMEWHERE ; or some other executable code.
*
*LD16I TSX ; point to top of return address stack
* LDX 0,X ; point into the instruction stream
* LDAA 0,X ; high byte from instruction stream
* LDAB 1,X ; low byte from instruction stream
* INS ; drop the return address we don't need
* INS
* JMP 2,X ; return to the byte after the constant.
*
* Essential monitor ROM routine
XOUTCH EQU $F018 ; output ACCM A to debug terminal, preserves B,X
*
* Output an 8-bit byte in hexadecimal,
* byte as a 16-bit parameter on PSP.
OUTHX8 LDX PSP
LDAB 1,X ; get the byte
LSRB
LSRB
LSRB
LSRB
BSR OUTRAD
LDX PSP
LDAB 1,X
ANDB #$0F ; mask it off
BSR OUTRAD
INX
INX
STX PSP
RTS
*
* Convert the value in B to ASCII numeric,
* including hexadecimals and up to base 36 ('Z'+1)
OUTRAD ADDB #ASC0 ; Add the ASCII for '0'
CMPB #ASC9 ; Greater than '9'?
BLS OUTRADD ; no, output as is.
ADDB #ASCXGAP ; Adjust it to 'A' to 'Z'
OUTRADD CLRA
JSR PPSHD
JSR OUTC
RTS
*
* Output the 8-bit number on the stack in binary (base two).
* For consistency, we are passing the byte as the low-order byte
* of a 16-bit word.
OUTB8 LDX PSP ; parameter is at 0,X (low byte at 1,X)
LDAB #8 ; 8 bits (B is preserved in BIOS
OUTB8L LSL 1,X ; Get the leftmost bit.
BCS OUTB81
OUTB80 LDAA #'0
BRA OUTB8B
OUTB81 LDAA #'1
OUTB8B JSR OUTCV
DECB ; count
BNE OUTB8L ; loop if not Zero
INX ; drop parameter bytes
INX
STX PSP
RTS
*
OUTNWLN LDAA #CR ; driver level code to output a new line
BSR OUTCV
LDAA #LF
BSR OUTCV
RTS
*
OUTC JSR PPOPD ; get the character in B
TBA ; put it where XOUTCH wants it.
BSR OUTCV ; output A via monitor ROM
RTS
*
* Preserves B, X.
OUTCV JMP XOUTCH ; driver code for outputting a character
*
OUTS JSR PPOPX ; get the string pointer
OUTSL LDAA 0,X ; get the byte out there
BEQ OUTDN ; if NUL, leave
BSR OUTCV ; use the same call OUTC uses.
INX ; point to the next
BRA OUTSL ; next character
OUTDN RTS
*
WAKETRM LDAB #SCRLIN
WAKETML JSR OUTNWLN
DECB
BNE WAKETML
RTS
*
******************************
* intermediate-level library:
*
* input parameters:
* 16-bit left, right
* output parameter:
* 16-bit sum
ADD16 LDX PSP
LDD 2,X ; left
ADDD 0,X ; right
STD 2,X ; sum
INX ; adjust parameter stack
INX
STX PSP
RTS
*
* input parameters:
* 16-bit left, right
* output parameter:
* 16-bit difference
SUB16 LDX PSP
LDD 2,X ; left
SUBD 0,X ; right
STD 2,X ; difference
INX ; adjust parameter stack
INX
STX PSP
RTS
*
*
************************************
* Start run-time, call program.
* Expects program to define PGSTRT:
*
START JSR INITRT
JSR WAKETRM
*
JSR PGSTRT
*
DONE LDS SSAVE ; restore the monitor stack pointer
NOP ; remember to set a breakpoint here!
NOP ; landing pad
NOP
NOP
LDX $FFFE ; alternatively, get reset vector
JMP 0,X ; and reboot through it
*
* Anyway, if running in EXORsim,
* Ctrl-C should bring you back to EXORsim monitor,
* but not necessarily to your program in a runnable state.
Save, assemble, remember to use the --6801 option for EXORsim6801, paste in the srecord object, play with it. Compare it with the 6800 source.
Again, to reduce what you have to think about getting it running before you start playing with it, here's the srecord object:
S10700807E21610177
S109008400000000000072
S105008A000070
S11320007E216101000000000000000000000000CB
S113201000000000000000000000000000000000BC
S113202000000000000000000000000000000000AC
S1132030000000000000000000000000000000009C
S109204000000000000096
S11320460000000000000000000000000000000086
S11320560000000000000000000000000000000076
S11320660000000000000000000000000000000066
S11320760000000000000000000000000000000056
S11320860000000000000000000000000000000046
S11320960000000000000000000000000000000036
S10720A60000000032
S11320AACE20A8DF84CE2000DF86389F8A8E206562
S10520BA6E00B2
S11020BCDE84EE003CDE840808DF84383941
S10620C93C32336F
S10C20CCDE840909DF84ED00390A
S10C20D5DE84EC000808DF843904
S11320DEDE84E601545454548D0DDE84E601C40F9F
S10A20EE8D050808DF8439A9
S11320F5CB30C1392302CB074FBD20CCBD212739B5
S1132105DE84C60868012504863020028631BD2197
S10C21152E5A26F00808DF843973
S10C211E860D8D0C860A8D08392A
S10A2127BD20D5178D01391D
S106212E7EF01824
S1102131BD20BCA60027058DF40820F73959
S10C213EC640BD211E5A26FA39DF
S1102147DE84EC02E300ED020808DF8439B9
S1102154DE84EC02A300ED020808DF8439EC
S1092161BD20AABD213ED1
S1062167BD21B7DC
S10E216A9E8A01010101FEFFFE6E00D1
S10E21757CFF538D06164FBD20CC39B3
S10621807EF012D8
S11321830D0A5479706520616E79206B65792C2072
S11321935120746F20717569742E200D0A004B450C
S10C21A3593A003A003A202400E4
S10E21ACDE84EC000909DF84ED00393B
S11321B7CE2183BD20C9BD2131BD2175BD21ACCE42
S11321C721A1BD20C9BD2131BD2127BD21ACCE210F
S11321D7A6BD20C9BD2131BD2105BD21ACCE21A895
S11321E7BD20C9BD2131BD20DEBD211EBD20D5C105
S10721F75126BD3973
S90321FBE0
Do play around with the source and assemble and run it on your own.
On, on, to the 6809.
No comments:
Post a Comment