6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Jun 30, 2024 4:39 pm

All times are UTC




Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Thu Mar 02, 2006 11:22 pm 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
I got bored. So I started browsing the net randomly, and happened upon 6502.org's source listing for PRIMM. Here are yet more implementations, this time including a version for the 65816 as well. Note that these are untested, and serve more as intellectual entertainment for me.

Code:
; Yet more PRIMM implementations (albeit UNTESTED at this point).
; This one has the advantage of not requiring so much zero-page access,
; and therefore results in faster output.

DPL = $xx
DPH = DPL+1

;Put the string following in-line until a NULL out to the console

PRIMM:
        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)


        ldy     #1              ; Note: actually we're pointing one short
PRMORE: lda     (DPL),y         ; Get the next string character
        beq     PRDONE          ; break loop when done
        jsr     CHROUT          ; send data to output device
        iny                     ; update the pointer
        bne     PRMORE          ; if not, we're pointing to next character
        inc     DPH             ; account for page crossing
        jmp     PRMORE         

PRDONE: iny                     ; Advance beyond NUL byte
        bne     PRD1            ; page crossing overhead
        inc     DPH
PRD1:   iny                     ; Account for JSR's negative bias
        bne     PRD2
        inc     DPH
PRD2:   tya
        clc
        adc     DPL
        sta     DPL
        lda     DPH
        adc     #$00
        sta     DPH
        jmp     (DPH)

; PRIMM for the 65816 16-bit native mode interface
; Assumes 16-bit registers on entry.
;
; String must (a) never cross a bank boundary, (b) be shorter
; than 65534 characters, and (c) must be NUL-terminated.
;
; Labels have L_ prefix for routines that are accessed with the JSL
; instruction; S_ for normal JSR instructions (Long vs. Short addresses).


DPB = DPH+1

L_PrImm:
    php
    phb
    sep #P_M        ; Switch accumulator size to 8 bits.
    lda 5,s         ; Set data bank to the caller's code bank
    pha
    plb

    ldy #$0001
L_more:
    lda (3,s),y
    beq L_done
    jsr S_CharOut   ; The 65816-native version of CHROUT.
    iny
    bra L_more

L_done:
    rep #P_M        ; Switch back to 16-bit accumulator
    iny
    clc
    tya
    adc 3,s
    sta 3,s
    plb
    plp
    rtl


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Mar 10, 2006 3:13 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
The strings CAN cross bank boundaries, since (3,S),Y can cross the Bank B (i.e. the DBR register) boundary. (However, the 3,S portion of that is taken from Bank 0 -- i.e. the bank where the stack is located -- and it does wrap at the bank 0 boundary. It's kind of like LDA ($FF),Y on the 6502 and 65C02 -- the $FF part wraps on page zero, since the high byte of the pointer is at $00, not $100, but the ,Y part can cross pages.) For example:

Code:
PEA #$1212
PLB
PLB
PEA #$FFFF
LDY #1
LDA (1,S),Y


Is like LDA $130000 (i.e. bank $13) rather than LDA $120000.


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 06, 2007 7:37 pm 
Offline

Joined: Mon Aug 06, 2007 1:42 pm
Posts: 5
kc5tja wrote:
I got bored. So I started browsing the net randomly, and happened upon 6502.org's source listing for PRIMM. Here are yet more implementations, this time including a version for the 65816 as well. Note that these are untested, and serve more as intellectual entertainment for me.


You had the right idea for the 6502 code (saving on zero page access), but it does have a couple bugs. First, Y is incremented one too many times after the null is reached, and the relative JMP uses the wrong target (DPH instead of DPL).

Here is a corrected version, which should indeed be faster on the inner loop than the Ross Archer version in the code repository:

Code:

;DPL can be any zero page location except $FF (obviously)

DPL = $xx
DPH = DPL+1

;Put the string following in-line until a NULL out to the console

PRIMM:
        pla                     ; Get the low part of "return" address
        sta     DPL
        pla                     ; Get the high part of "return" address
        sta     DPH
        ldy     #1              ; Account for return address offset
PRMORE: lda     (DPL),y         ; Get the next string character
        beq     PRDONE          ; don't print the final NULL
        jsr     CHAROUT         ; write it out
        iny
        bne     PRMORE          ; back around
        inc     DPH
        bne     PRMORE          ; if high byte wraps, it'd be trouble

PRDONE: tya
        sec                     ; intentionally set carry (add 1 to skip null)
        adc     DPL
        sta     DPL
        bcc     PRD1
        inc     DPH             ; account for page crossing
PRD1:   jmp     (DPL)           ; return to byte following final NULL



Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Aug 06, 2007 9:07 pm 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
My code advances beyond the NUL byte of the string. So far as I can tell, yours does not. This is why Y is off-by-1 from your point of view. However, your solution uses SEC to get a free "+1" on the address fixup addition, so the end result should be the same.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Aug 06, 2007 9:51 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
I'll play.

kc5tja wrote:
My code advances beyond the NUL byte of the string. So far as I can tell, yours does not. This is why Y is off-by-1 from your point of view. However, your solution uses SEC to get a free "+1" on the address fixup addition, so the end result should be the same.


Why fix it at all? why not just keep the low byte in Y,
push the address back on the stack and return?

Code:
PRIMM:
        pla                     ; Get the low part of "return" address
        tay                     ; (data start address)
        pla                     ; Get the high part of "return" address
        sta     DPH             ; (data start address)

        lda     #00
        sta     DPL

PRMORE:
        iny                     ; Note: actually we're pointing one short
        bne     OUT             ; first pass here fixes that
        inc

OUT:
        lda     (DPL),y         ; Get the next string character
        beq     PRDONE          ; break loop when done
        jsr     CHROUT          ; send data to output device
        jmp     PRMORE         

PRDONE:
        lda     DPH
        pha
        tya
        pha
        rts


(no, I didn't test it to see if it actually works) ;)

note that you only cross the page boundary once
(for each page crossing) this way. (lda(DPL),y will always be 5 cycles)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Aug 06, 2007 10:02 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
ack!

Code:
PRMORE:
        iny                     ; Note: actually we're pointing one short
        bne     OUT             ; first pass here fixes that
        inc


left out a label

Code:
PRIMM:
        pla                     ; Get the low part of "return" address
        tay                     ; (data start address)
        pla                     ; Get the high part of "return" address
        sta     DPH             ; (data start address)

        lda     #00
        sta     DPL

PRMORE:
        iny                     ; Note: actually we're pointing one short
        bne     OUT             ; first pass here fixes that
        inc     DPH

OUT:
        lda     (DPL),y         ; Get the next string character
        beq     PRDONE          ; break loop when done
        jsr     CHROUT          ; send data to output device
        jmp     PRMORE         

PRDONE:
        lda     DPH
        pha
        tya
        pha
        rts


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Aug 07, 2007 3:23 am 
Offline

Joined: Mon Aug 06, 2007 1:42 pm
Posts: 5
kc5tja wrote:
My code advances beyond the NUL byte of the string. So far as I can tell, yours does not. This is why Y is off-by-1 from your point of view. However, your solution uses SEC to get a free "+1" on the address fixup addition, so the end result should be the same.


Let's trace the code on an example call with a null string:

$1000 JSR PRIMM
$1003 .BYTE 0
$1004 <- execution should resume here

Code:
   ; [JSR pushes return address - 1]

PRIMM:
        pla                     ; [A = $02]

        sta     DPL             ; [DPL = $02]
        pla                     ; [A = $10]
        sta     DPH             ; [DPL = $10]

        ; [pointer at DPL is now $1002]                       

        ldy     #1              ; [Y = $01]
PRMORE: lda     (DPL),y       

        ; [$1002 + Y (1) = $1003, the null byte]

        beq     PRDONE          ; [branches]

        jsr     CHROUT         
        iny                   
        bne     PRMORE 
        inc     DPH         
        jmp     PRMORE         

PRDONE: iny                     ; [Y is now $02]
        bne     PRD1
        inc     DPH
PRD1:   iny                     ; [Y is now $03]
        bne     PRD2
        inc     DPH
PRD2:   tya                     ; [A is now $03]
        clc
        adc     DPL             ; [$03 + $02 = $05]
        sta     DPL             ; [DPL is now $05]
        lda     DPH
        adc     #$00
        sta     DPH             ; [DPH is still $10]

        ; [pointer at DPL is now $1005]

        jmp     (DPL)



Thus, where execution should have resumed at $1004, it jumps to $1005 instead. If you follow the code through on any string, the result is the same.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Aug 07, 2007 3:44 am 
Offline

Joined: Mon Aug 06, 2007 1:42 pm
Posts: 5
bogax wrote:
Why fix it at all? why not just keep the low byte in Y,
push the address back on the stack and return?

<snip>

note that you only cross the page boundary once
(for each page crossing) this way. (lda(DPL),y will always be 5 cycles)


That's a good solution; it's faster and uses fewer bytes of code. Well done, bogax.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Aug 07, 2007 4:26 am 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
AH HA, I see what you're saying now. Whoops.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC


Who is online

Users browsing this forum: Martin A and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: