Last night I got string output working. But what if I want to make string output generic? I want to write a STOUT (STring OUT) subroutine that can take the address of any null-terminated string and print it to the console, but there’s a problem: The 6502 is an 8-bit machine, so passing a 16-bit address as an argument takes some wrangling.
To get around this, I’ve designated two locations in the precious Zero
Page (arbitrarily choosing $20
and $21
) to store the locations of the
low byte and high byte of the string’s address, respectively.
;;; ----------------------------------------------------------------------
;;; Memory Definitions
;;; ----------------------------------------------------------------------
;;; Zero Page
STRLO = $20 ; Low byte of STOUT STRING
STRHI = $21 ; Hi byte of STOUT STRING
Now I can write my STOUT subroutine. The change here is that I’m going to use the Indirect Indexed addressing mode to look up the characters of the string from the zero-page addresses, instead of the Absolute,X addressing that I used in my last post.
;;; ----------------------------------------------------------------------
;;; Print the null-terminated string located at STRLO,STRHI
;;; ----------------------------------------------------------------------
STOUT: LDY #$00 ; Initialize string pointer
@loop: LDA (STRLO),Y ; Get character
BEQ @done ; If char == 0, we're done
JSR COUT ; Otherwise, print it
INY ; Increment pointer
BNE @loop ; Continue
@done: RTS ; Return
But calling this subroutine is still a little weird. Each time I want to print a string, I need to put the low byte of the string’s address in $20, the high byte in $21, and then call STOUT. It’s just slightly unwieldy, so… enter our first assembler macro!
The ca65 assembler supports putting a series of instructions together into a macro call, sort of like an inline function in C, so that code can be re-used without the overhead of a subroutine call. I’ve created a macro named STR that takes a 16-bit address and prints the string to the console.
;;;
;;; STR <ADDR>
;;;
;;; Print out the null-terminated string located at address <ADDR>
;;;
;;; Modifies: Accumulator, STRLO, STRHI
;;;
.macro STR ADDR
LDA #<ADDR ; Grab the low byte of the address
STA STRLO
LDA #>ADDR ; ... and the high byte
STA STRHI
JSR STOUT ; then call STOUT.
.endmacro
Great. Now all I have to do is call STR to print my “HELLO, 6502 WORLD!” string over and over again.
LOOP: STR HELLO
JMP LOOP
HELLO: .byte "HELLO, 6502 WORLD! ",0
With this slight diversion out of the way, next up will be console input, which I promised to talk about today. But that’s a subject for another post.
Comments