Print Immediate (PRIMM) from
Commodore 128 KERNAL ROM
[Up to Source Code Repository]
Print Immediate (PRIMM)
This clever routine lets you inline data to be printed in your assembly code like this:
JSR PRIMM .BYTE "This will be printed!",$00 RTSTo use it, insert your text to be printed immediately after the call to PRIMM, terminated by a null byte. When the routine is finished the program counter will be pointing the instruction immediately following the text, in this case RTS. The program is presented here in three different versions, all fully tested and functionally equivalent. Each version requires two bytes in the zero-page, any two consecutive bytes will do. Change the call to CHAROUT to your character printing subroutine, the byte to be printed will be loaded into the accumulator prior to the call.
The version below was obtained from disassembling the Commodore 128 KERNAL ROM. It can also be found in a few other Commodore computers such as the Plus/4 and 16 but is surprisingly absent in the VIC-20 and C64.
PRIMM: PHA
TYA
PHA
TXA
PHA
TSX
INX
INX
INX
INX
LDA $0100,X
STA $BC
INX
LDA $0100,X
STA $BD
INC $BC
BNE PRIM1
INC $BD
PRIM1 LDY #$00 ; set index
PRIM2 LDA ($BC),Y ; get byte from string
BEQ PRIM3 ; exit if null (end of text)
JSR CHAROUT ; else display character
INY ; increment index
BNE PRIM2 ; loop (exit if 256th character)
PRIM3 TYA ; copy index
TSX
INX
INX
INX
INX
CLC
ADC $BC
STA $0100,X
LDA #$00
ADC $BD
INX
STA $0100,X
PLA
TAX
PLA
TAY
PLA
RTS
Lee Davison was not impressed with the efficiency of the above routine so
he condensed it into this version.
PRIMM: PHA ; save A TYA ; copy Y PHA ; save Y TXA ; copy X PHA ; save X TSX ; get stack pointer LDA $0104,X ; get return address low byte (+4 to ; correct pointer) STA $BC ; save in page zero LDA $0105,X ; get return address high byte (+5 to ; correct pointer) STA $BD ; save in page zero LDY #$01 ; set index (+1 to allow for return ; address offset) PRIM2: LDA ($BC),Y ; get byte from string BEQ PRIM3 ; exit if null (end of text) JSR CHAROUT ; else display character INY ; increment index BNE PRIM2 ; loop (exit if 256th character) PRIM3: TYA ; copy index CLC ; clear carry ADC $BC ; add string pointer low byte to index STA $0104,X ; put on stack as return address low byte ; (+4 to correct pointer, X is unchanged) LDA #$00 ; clear A ADC $BD ; add string pointer high byte STA $0105,X ; put on stack as return address high byte ; (+5 to correct pointer, X is unchanged) PLA ; pull value TAX ; restore X PLA ; pull value TAY ; restore Y PLA ; restore A RTSThis alternate version was provided by Ross Archer.
DPL = $00
DPH = $01
;Put the string following in-line until a NULL out to the console
PUTSTRI pla ; Get the low part of "return" address
; (data start address)
sta DPL
pla
sta DPH ; Get the high part of "return" address
; (data start address)
; Note: actually we're pointing one short
PSINB ldy #1
lda (DPL),y ; Get the next string character
inc DPL ; update the pointer
bne PSICHO ; if not, we're pointing to next character
inc DPH ; account for page crossing
PSICHO ora #0 ; Set flags according to contents of
; Accumulator
beq PSIX1 ; don't print the final NULL
jsr CHAROUT ; write it out
jmp PSINB ; back around
PSIX1 inc DPL ;
bne PSIX2 ;
inc DPH ; account for page crossing
PSIX2 jmp (DPL) ; return to byte following final NULL
Mike Barry provided another version. This one requires 65C02 opcodes but is shorter and preserves the Y register.
DPL = $fd
DPH = $fe
primm:
pla ; get low part of (string address-1)
sta DPL
pla ; get high part of (string address-1)
sta DPH
bra primm3
primm2:
jsr COUT ; output a string char
primm3:
inc DPL ; advance the string pointer
bne primm4
inc DPH
primm4:
lda (DPL) ; get string char
bne primm2 ; output and continue if not NUL
lda DPH
pha
lda DPL
pha
rts ; proceed at code following the NUL
Last page update: May 7, 2019.