Thursday, October 10, 2024

ALPP 03-06 -- Hexadecimal Output on the 6801, without General Divide

Hexadecimal Output on the 6801,
without General Divide

(Title Page/Index)

 

In the last chapter, I discussed the theory behind hexadecimal output and implemented it for you on the 6800.  

There's very little to change from the 6800 code, and I can't think of anything to discuss, so I'll just leave code here for you to paste in and test. 

Here's the hexadecimal output code and test routine:

* simple 8-bit hexadecimal output for 6801
* using parameter stack,
* with test frame
* Joel Matthew Rees, October 2024
*
	OPT	6801
	EXP	rt_rig01_6801.asm
****************
* Program code:
*
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
*
* Mask off and convert the nybble in B to ASCII numeric,
* including hexadecimals
OUTH4	ANDB	#$0F	; mask it off
	ADDB	#ASC0	; Add the ASCII for '0'
	CMPB	#ASC9	; Greater than '9'?
	BLS	OUTH4D	; no, output as is.
	ADDB	#ASCXGAP	; Adjust it to 'A' - 'F'
OUTH4D	CLRA
	JSR	PPSHD
	JSR	OUTC
	RTS
*
* 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	OUTH4
	LDX	PSP
	LDAB	1,X
	BSR	OUTH4
	LDX	PSP
	INX
	INX
	STX	PSP
	RTS
*
HEADLN	FCB	CR,LF	; Put message at beginning of line
	FCC	"Outputting $5A in binary and hex: "	; 
	FCB	CR,LF,NUL	; Put the binary output on a new line
CHEX	FCC	": $"
	FCB	NUL
*
*
*
*
PGSTRT	LDX	#HEADLN
	JSR	PPSHX
	JSR	OUTS
	LDD	#$5A	; byte to output
	JSR	PPSHD
	JSR	OUTB8
	LDX	#CHEX
	JSR	PPSHX
	JSR	OUTS	
	LDD	#$5A	; byte to output
	JSR	PPSHD
	JSR	OUTHX8
	JSR	OUTNWLN
	RTS
*
	END	ENTRY

And here's the framework rigging -- bringing in the 6801 bit output routines, slightly optimized --

Optimized.

You may have missed it in the 6800 code while were were focused on shifting bits, but now you might have noticed. And realized I've already been playing a similar game with X. Isn't that cheating?

I don't want to talk about it. 

Not here, not in code where the way we're thrashing X gets in the way of seeing what we're doing with X. Let's look at this again in the 6809 code. 

* A simple run-time framework inclusion for 6801
* providing parameter stack and local base
* Version 00.00.01
* Joel Matthew Rees, October 2024
*
* Essential control codes
LF	EQU	$0A	; line feed
CR	EQU	$0D	; carriage return
NUL	EQU	0
*
SCRLIN	EQU	$40	; More than a screen full of lines.
*
* Essential monitor ROM routines
XOUTCH	EQU	$F018
*
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.
*
* 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
*
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.

Look through the code, compare it with the 6800 code, save both files with appropriate names, and assemble it as you did the previous with asm68c:

asm68c -l2 outhx8_6801.asm >outhx8_6801.list

Remember that you'll need to use EXORsim6801 instead of straight EXORsim, and invoke it with the --6801 option:. 

./exor --6801 --mon

Again, try to move the hexadecimal output to the rigging and test it before you move on to the 6809 code. If you can get it to work, great. If you can't, I'll have it in place when we start working on character input, so you can grab it from there. But do give it a try.

When you're done, let's move on to the hexadecimal routines for the 6809.


(Title Page/Index)

 


No comments:

Post a Comment