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 RTSLee 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 NULLMike 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.