;to SET cursor
JSR PLOT ;aka $FFF0
LDY #3 ;first real text char
;is at string posn 2
;(INY:INY does same)
PRIM2 =* ;top of the loop
LDA (PTRSTR),Y ;get char from string
BEQ PRIM3 ;exit when 0 reached
JSR CHROUT ;print the char in .A
INY ;increment index/len
BNE PRIM2 ;branch to loop top
;(256 chars max!)
So, you see that with just a bit of additional code we made ourselves a
very nice string printing routine! The tricky part (not too bad, though)
is possessing the knowledge that the Kernal PLOT function has mixed-up
cursor coordinates with respect to registers X and Y, but we will assume
that as good assembly language programmers you took the time to look up the function before using it, right?
CLEANUP TIME
The final thing we have to do is get the program counter "fixed up" so that
we can return to the main program. Once again we will make a few changes
to the C-128 method in order to save some space and time. If you choose
not to add the PlotString mod you won't need to get the stack pointer with
TSX as we did not modify .X anywhere in the original PRIMM-only function.
We will take our length counter (in .Y), add it to the pointer located at $BC-$BD, and place it back on the stack where the original return address
is located. This has the net effect of adding the number of string
characters to the program counter - skipping the string (and the optional screen coordinates) altogether. And finally, make absolutely certain you
pull the registers from the stack in the REVERSE order. Failure to do this could result in a hard-to-find bug later. Also, since this function
modifies the stack to get at the program counter so you won't be able to
JMP to it. Instead, remember to always JSR.
C-128 Version
PRIM3 =* ;all done printing
TYA ;put the char count in .A
TSX ;get the stack pointer
INX ;increment the index
INX ;four times to account
INX ;for pushed A/X/Y
INX ;and 6502 stack return
;address peculiarity
CLC
ADC $BC ;add count to string
;location (-1)
STA $0100,X ;and store it where
;return address is
LDA #$00 ;LDA #$00 accounts for
;word-sized addr
ADC $BD ;do the hi byte
INX
STA $0100,X ;and store it
PLA ;pull the registers
TAX ;in REVERSE order
PLA
TAY
PLA
RTS ;return to main program
Our Version
PRIM3 =* ;all done printing
TYA ;put char count in .A
TSX ;only necessary if
;PLOT mod is used
CLC
ADC PTRSTR ;add count to string
;location (-1)
STA RETADDR,X ;and store it where
;return address is
LDA #$00 ;LDA #$00 accounts
;for word-sized addr
ADC PTRSTR+1 ;do the hi byte
STA RETADDR+1,X ;and store it
PLA ;all done, so restore
TAY ;the modified regs
PLA ;in the REVERSE order
TAX
PLA
RTS ;cpu will add a byte
;with RTS
And that's that.
BONUS: 65816 VERSION
Look below to see how we might do the same thing on the SuperCPU. Notice
how much shorter and less complicated the function becomes. I have
included a few directives and instructions to pre-set the environment for native, 16-bit mode, although later we will make some register size
adjustments in order to accommodate 8-bit Kernal requirements. Although
not included in this sample, it is very easy to extend this function to
accept strings of any length up to 65536 bytes.
.65816
.al (longa)
.xl (longi)
ptrstr = $bc
PLOT = $fff0
CHROUT = $ffd2
;-----
;SETUP ;this can be done earlier
;-----
clc
xce ;65816 native mode
rep #%00110000 ;16-bit A/XY
jsr PlotString
.byte X,Y ;replace X (0-39),
;Y (0-24) with the
;actual screen
;coordinates you need
.text "Hello World"
.byte 0
rts
PlotString =*
pha ;save .A on the stack
phx ;save .X on the stack
phy ;save .Y on the stack
lda 1,s ;get return address
;(stack relative
;addressing)
sta ptrstr ;store into DP
;(Direct Page) ptr
sep #%00010000 ;go to 8-bit XY
lda (ptrstr) ;get x/y-coordinates
;both at once (DP
;Indirect addressing)
tay ;Kernal PLOT requires
;x-coordinate in .Y
xba ;swap A/B registers
tax ;Kernal PLOT requires
;y-coordinate in .X
clc ;Kernal PLOT requires
;carry=0 to SET crsr
jsr PLOT ;aka $FFF0
sep #%00100000 ;go to 8-bit A
ldy #2 ;string length count
;first real text char
;is at string posn 2
prim2 =* ;top of the loop
lda (ptrstr),y ;get char from string
beq prim3 ;exit when 0 reached
jsr CHROUT ;print the char in .A
iny ;increment index/len
bne prim2 ;branch to loop top
;(256 chars max!)
prim3 =* ;all doneprinting
rep #%00110000 ;go to 16-bit A/XY
tya ;copy string len to A
clc
adc 1,s ;add return address
;stored on stack
;(stack relative
;addressing)
sta 1,s ;store new, adjusted
;return addr on stack
;(stack relative
;addressing)
ply ;all done, so
plx ;restore A/X/Y
pla ;in the REVERSE order
rts ;back to main program
;cpu will add a byte
;to the return addr
;with rts
IN CONCLUSION
While thinking of ways to present today's topic I consulted several
different sources to see how others have approached this in the past,
primarily because my own projects have managed (over time) to take a very straight-forward function such as PRIMM and turn it into something very different from the code you see here, and it has all been based on my own
needs in different situations. You will find the same will happen to you
as you find new ways to expand and enhance your own efforts. These days I
work almost exclusively on the SuperCPU, and with that comes liberties (primarily in the form of a greatly enhanced instruction set) which tend to spoil the assembly language programmer. So something like this became
somewhat of a challenge as I had to think only in terms of 8-bit 6502. I
was amazed to see the number of ways this function has been written - some good, some not as good. In the end I had to dig deep into the bowels of an
old hard drive to find an untarnished source copy from a long time ago, one which I wasn't sure would even work as written! I had to type the whole
thing into CBM prg Studio to make sure it would do what it is supposed to
do. To my surprise, the only register you can push onto the stack is the Accumulator! Well, okay, it wasn't quite that bad, but it was sort of fun
to work in 8-bits again.
I hope you can make use of this technique - it's a fast (and efficient) way
to get a screen up and running. While nothing earth-shaking, PRIMM does demonstrate how to access the stack and manipulate the program counter.
Also, I included the bonus 65816 code for those who are interested in programming the SuperCPU. The VICE SuperCPU emulator is impressive and a
lot of fun - give it a try! Arthur Jordison's CBM prg Studio now includes
a 65816 assembler which makes writing programs for the SuperCPU a breeze.
We have an entire section devoted to SuperCPU coding over at
www.Melon64.com - and all are welcome. The admin allows uploads, so you
are free to exhibit your code!
See you next month...
Please send errors, omissions, or suggestions to
bert@winc64.com or on
Lemon64, username satpro, or at www.melon64.com, username satpro.
=====================================
--- MBSE BBS v1.0.01 (GNU/Linux-i386)
* Origin: Dragon's Lair ---:- bbs.vk3heg.net -:--- (39:901/280)