6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 6:33 pm

All times are UTC




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Thu Feb 29, 2024 12:25 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
The following is some 6800 code to find a line of code in the memory buffer of a BASIC interpreter. The lines of the program are stored contiguously, starting with header containing a two-byte line number in packed BCD followed by the line in plain text. Lines are separated by carriage return characters.

The code can be entered at FNDLIN with the line number in memory at NUMBER+1 and NUMBER+2 or at FINDLN with the number in registers A and B.

I have run out of registers for a straightforward translation to the 6502. The code must be usable on an NMOS 6502, so Y is dedicated for use with the (addr),Y addressing mode not to mention that incrementing Y is the quickest way to access the second byte. CPX cannot be used with that addressing mode and there is no instruction to exchange the contents of A and X.

The obvious solution is to push A onto the stack and temporarily TXA for the comparison. However, pulling A back off of the stack destroys the condition codes and nullifies the comparison.

The only solution I can see is to store the least significant byte in a temporary variable and load it for comparison. Which is also problematic because I am out of free zero page locations. I will have to find a less often used variable to move out of the zero page.

Is there a clever technique I am not seeing?
Code:
                          00323 * TRY TO FIND LINE
                          00324
 02A5 96 64           [3] 00325 FNDLIN   LDAA   NUMBER+2
 02A7 D6 63           [3] 00326          LDAB   NUMBER+1
 02A9 CE 0D4F         [3] 00327 FINDLN   LDX    #STORSP   SETUP POINTER
 02AC BC 0D4D         [5] 00328 FINDL1   CPX    ENDSTR    END OF STORAGE?
 02AF 26 02 (02B3)    [4] 00329          BNE    FINDL4
 02B1 5C              [2] 00330 FINDL2   INCB
 02B2 39              [5] 00331          RTS              RETURN
 02B3 E1 00           [5] 00332 FINDL4   CMPB   0,X       CHECK M.S. DIGITS
 02B5 22 0A (02C1)    [4] 00333          BHI    FINDL6
 02B7 26 F8 (02B1)    [4] 00334          BNE    FINDL2
 02B9 A1 01           [5] 00335          CMPA   1,X       CHECK L.S, DIGITS
 02BB 22 04 (02C1)    [4] 00336          BHI    FINDL6
 02BD 26 F2 (02B1)    [4] 00337          BNE    FINDL2
 02BF 5F              [2] 00338          CLRB             CEAR FLAG
 02C0 39              [5] 00339          RTS              RETURN
 02C1 8D 03 (02C6)    [8] 00340 FINDL6   BSR    FNDCRT    GO FIND C.R,
 02C3 08              [4] 00341          INX              BUMP THE POINTER
 02C4 20 E6 (02AC)    [4] 00342          BRA    FINDL1    REPEAT
                          00343
                          00344 * FIND A C,R, IN STORAGE
                          00345
 02C6 36              [4] 00346 FNDCRT   PSHA             SAVE A
 02C7 86 0D           [2] 00347          LDAA   #$D
 02C9 08              [4] 00348 FNDVAL   INX              BUMP THE POINTER
 02CA A1 00           [5] 00349          CMPA   0,X       TEST FOR C.R.
 02CC 26 FB (02C9)    [4] 00350          BNE    FNDVAL
 02CE 32              [4] 00351          PULA             RESTORE A
 02CF 39              [5] 00352          RTS              RETURN


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 29, 2024 3:49 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1927
Location: Sacramento, CA, USA
ZP is definitely your friend here. The 6502 is nearly crippled without it, unless you resort to self-modifying code. When I ported VTL-2 from the 6800 to the 6502 I observed that Frank always stored the mandatory space between the line number and the associated program text. My optimization was to store the line length in that spot so I could quickly find the next line without searching byte by byte for the end-of-line marker. I then slightly modified the LIST routine to print a space instead of the length byte. I don't know what your interpreter expects from FNDLIN, but I do my best to describe my entry and exit conditions in my code, which I've revised a couple of times in the last 11 years:
Code:
;-----------------------------------------------------;
; Find the first/next stored program line >= {#}
; entry:   (cc): start search at program beginning
;          (cs): start search at next line
;                ({@} -> beginning of current line)
; used by: edit:, findln:
; uses:    prgm, {@ # & (}
; exit:    (cs): {@} = {&}, x:a and {(} invalid, y = 2
;          (cc): {@} -> beginning of found line, y = 2,
;                x:a = {(} = actual found line number
; 52 bytes
find:
    lda  #<prgm     ;
    ldx  #>prgm     ;
    ldy  #2         ;
    bcc  find1st    ; (cc): search begins at first line
    ldx  at+1       ;
    clc             ;
findnxt:
    lda  at         ; {@} -> next line (or {&} if
    adc  (at),y     ;   there is no next line ...)
    bcc  find5      ;
    inx             ;
find1st:
    stx  at+1       ;
find5:
    sta  at         ;
    cpx  ampr+1     ; {@} >= {&} (end of program)?
    bcc  find6      ;
    cmp  ampr       ;
    bcs  findrts    ;   yes: line not found (cs)
find6:
    lda  (at)       ;
    sta  lparen     ; {(} = current line number
    cmp  pound      ;
    dey             ;
    lda  (at),y     ;
    iny             ;
    sta  lparen+1   ;
    sbc  pound+1    ; if {(} < {#} then try the next
    bcc  findnxt    ;   program line
    clc             ; found line (cc)
    lda  lparen     ;
    ldx  lparen+1   ;
findrts:
    rts             ;
at, pound, ampr and lparen are all 16-bit ZP variables. It is highly unlikely that this will transfer cleanly to your use case (I just noticed that lda (at) is a 'c02 instruction), but it might help nudge you in the right direction.

[Edit: I tracked down the older NMOS version of the same. It's not as tight:
Code:
;-----------------------------------------------------;
; Find the first/next stored program line >= {#}
; entry:   (cc): start search at program beginning
;          (cs): start search at next line
;          ({@} -> beginning of current line)
; used by: skp2:, findln:
; uses:    prgm, {@ # & (}
; exit:    (cs): {@}, x:a and {(} undefined, y = 2
;          (cc): {@} -> beginning of found line, y = 2,
;                x:a = {(} = actual found line number
; 62 bytes
find:
    ldx  /prgm
    lda  #prgm
    bcc  find1st    ; cc: search begins at first line
    ldx  at+1
    ldy  #2
findnxt:
    lda  at
    cmp  ampr
    lda  at+1
    sbc  ampr+1     ; {@} >= {&} (end of program)?
    bcs  findrts    ;   yes: search failed (cs)
find3:
    lda  at
    adc  (at),y     ;   no: {@} -> next line
    bcc  find5
    inx
find1st:
    stx  at+1
find5:
    sta  at
    ldy  #0
    lda  (at),y
    sta  lparen     ; {(} = current line number
    cmp  pound      ;   (invalid if {@} >= {&}, but
    iny             ;   we'll catch that later...)
    lda  (at),y
    sta  lparen+1
    sbc  pound+1    ; if {(} < {#} then try the next
    iny             ;   program line
    bcc  findnxt
    lda  at         ; {@} >= {&} (end of program)?
    cmp  ampr       ;   yes: search failed (cs)
    lda  at+1       ;   no: search succeeded (cc)
    sbc  ampr+1
    lda  lparen
    ldx  lparen+1
findrts:
    rts
]

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 29, 2024 6:49 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
barrym95838 wrote:
My optimization was to store the line length in that spot so I could quickly find the next line without searching byte by byte for the end-of-line marker. I then slightly modified the LIST routine to print a space instead of the length byte.

That is a great idea since this code is used by "everybody" to find a line of code, including GOTO and GOSUB.

While you guys were talking about the smallest Tiny BASIC, I was working on paraphrasing the TSC Micro BASIC Plus to the 6502.

https://ia903409.us.archive.org/34/item ... asic_a.pdf

The manual states that the maximum line length is 72 characters. I do not know yet whether that is merely the size of the input buffer or there are length limits somewhere else in the interpreter. This was originally a tape BASIC. There is no provision to save or load programs. The user has to exit to the system's ROM monitor and save/load form there, then warm start back into BASIC. The format of the program in memory is identical to what a text editor may produce, with lines separated by carriage return characters.

So I am not using the optimization of incrementing register Y while looking for the end of the line, then doing some address arithmetic once it is found. Incrementing a 6502 pointer is ugly at best, but that is what I will do for now.

This is my code so far. I paraphrase the code first; there will be time to improve it later when everything is working.
Code:
                          01039 ;
                          01040 ; Try to find line
                          01041 ;
 20D2                     01042 FndLin:
 20D2 A6 29           [3] 01043          ldx    Number+2
 20D4 A5 28           [3] 01044          lda    Number+1
                          01045
 20D6                     01046 FindLn:
 20D6 85 07           [3] 01047          sta    Temp
                          01048
 20D8 A0 E8           [2] 01049          ldy    #<StorSp  ; Setup pointer
 20DA 84 04           [3] 01050          sty    XReg
 20DC A0 23           [2] 01051          ldy    #>StorSp
 20DE 84 05           [3] 01052          sty    XReg+1
                          01053
 20E0                     01054 FindL1:
 20E0 A4 05           [3] 01055          ldy    XReg+1    ; End of storage?
 20E2 CC 23E7         [4] 01056          cpy    EndStr+1
 20E5 D0 09 (20F0)  [2/3] 01057          bne    FindL4
                          01058
 20E7 A4 04           [3] 01059          ldy    XReg
 20E9 CC 23E6         [4] 01060          cpy    EndStr
 20EC D0 02 (20F0)  [2/3] 01061          bne    FindL4
                          01062
 20EE                     01063 FindL2:
 20EE E8              [2] 01064          inx
                          01065
 20EF 60              [6] 01066          rts              ; Return
                          01067
 20F0                     01068 FindL4:
 20F0 A0 00           [2] 01069          ldy    #0        ; Check M.S. digits
 20F2 A5 07           [3] 01070          lda    Temp
 20F4 D1 04         [5/6] 01071          cmp    (XReg),Y
 20F6 90 F6 (20EE)  [2/3] 01072          blo    FindL2
 20F8 D0 0B (2105)  [2/4] 01073          bne    FindL6
                          01074
 20FA C8              [2] 01075          iny              ; Check L.S. digits
 20FB 8A              [2] 01076          txa
 20FC D1 04         [5/6] 01077          cmp    (XReg),Y
 20FE 90 EE (20EE)  [2/4] 01078          blo    FindL2
 2100 D0 03 (2105)  [2/3] 01079          bne    FindL6
                          01080
 2102 A2 00           [2] 01081          ldx    #0        ; Clear flag
                          01082
 2104 60              [6] 01083          rts              ; Return
                          01084
 2105                     01085 FindL6:
 2105 20 2111         [6] 01086          jsr    FndCRt    ; Go find C.R,
                          01087
 2108 E6 04           [5] 01088          inc    XReg      ; Bump the pointer past CR
 210A D0 02 (210E)  [2/3] 01089          bne    2f
                          01090
 210C E6 05           [5] 01091          inc    XReg+1
                          01092
 210E                     01093 2:
                          01094
 210E 4C 20E0         [3] 01095          jmp    FindL1    ; Repeat
                          01096
                          01097 ;
                          01098 ; FIND A C,R, IN STORAGE
                          01099 ;
 2111                     01100 FndCRt:
                          01101 ; pha ; Save A -- No longer need this
 2111 A9 0D           [2] 01102          lda    #$D
                          01103
 2113                     01104 FndVal:
 2113 E6 04           [5] 01105          inc    XReg      ; Bump the pointer
 2115 D0 02 (2119)  [2/3] 01106          bne    2f
                          01107
 2117 E6 05           [5] 01108          inc    XReg+1
                          01109
 2119                     01110 2:
                          01111
 2119 A0 00           [2] 01112          ldy    #0        ; Test for C.R.
 211B D1 04         [5/6] 01113          cmp    (XReg),Y
 211D D0 F4 (2113)  [2/3] 01114          bne    FndVal
                          01115
                          01116 ; pla ; Restore A -- No longer need this
                          01117
 211F 60              [6] 01118          rts              ; Return


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 29, 2024 7:12 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
I just spotted the simple optimization of moving the loading of zero into register Y out of the loop at the bottom...


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2024 12:18 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
I am starting to find a few instances of self-modifying code. These should not be directly paraphrased since they are workarounds of the limitation of a single index register. Better code can be written on the 6502 using better techniques.


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 22 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: