6502 randy hyde subroutine problem "JSR INCRTN"

273 views Asked by At

I am following the A totorial in the book using 6502 assembly language by randy hyde there is a part in chapter 14, section 7 where he writes "jsr incrtn" the problem is that he has not created a subroutine called incrtn here is the full code,


    PRTSTR:
     STA ASAVE
     STY YSAVE
     PLA ;GET RETURN ADDRESS FROM
     STA RTNADR ;THE 6502 STACK
     PLA
     STA RTNADR+$1
     ;
     JSR INCRTN ;INCREMEOT THE RETURN ADDRESS
     LDY #$0
     LDA (RTNADR),Y ;GET L.O. ADDRESS OF STRING
     STA ZPAGE
     INY
     LDA (RTNADR),Y ;GET H.O.ADDRESS OF STRING
     STA ZPAGE+$1
     ;
     JSR INCRTN ;MOVE RTNADR PAST THE ADDRESS
     JSR INCRTN ;BYTES
     ;
     ;
     ; AT THIS POINT, ZPAGE POINTS TO THE STRING WHICH
     ; IS SUPPOSED TO BE OUTPUT
     ;
     DEY ;RESET Y REG TO ZERO
     LDA (ZPAGE),Y ;GET THE LENGTH OF THE STRING
     STA LENGTH ;AND STORE IT IN "LENGTH"
     PRTS1 INY ;MOVE TO THE NEXT CHARACTER
     CPY LENGTH ;ARE WE THROUGH YET?
     BEQ PRTS2
     ;
     LDA (ZPAGE),Y ;GET THIS CHARACTER
     JSR COUT ;AND OUTPUT
     JMP PRTS1 ;MOVE TO NEXT CHAR AND REPEAT
     ;
     PRTS2 LDA ASAVE ;RESTORE THE REGISTERS
     LDY YSAVE
     JMP (RTNADR) ;SIMULATE AN RTS
     ;
     ;
     ;
     ASAVE EPZ $0 ;ZERO PAGE WORKSPACE
     YSAVE EPZ ASAVE+$1
     ZPAGE EPZ YSAVE+$1
     RTNADR EPZ ZPAGE+$2
     COUT EQU $FDED ;COUT ROUTINE
     END

can anyone help me?

Update, if anyone wants to know how to print text the shorter way here is some working code


           LDX #$0
  LOOP     INX 
           LDA STRING,X 
           JSR $FDF0 
           CPX STRING 
           BLT LOOP 
           RTS  
  STRING   STR "hello world"
         END
2

There are 2 answers

1
Erik Eidt On

Before processors had a stack or sufficient registers, parameters were often passed via "inline" parameter passing, which is to pass parameters after the JSR or call-type instruction, by placing values or pointers directly succeeding the call instruction, with the requirement that the callee skip over them when returning to resume the caller.

As you know, a call instruction captures a return address (somehow, somewhere: maybe on the stack or maybe the code memory of the subroutine!), and that return address can be used to fetch parameters that are placed after the call instruction.  The return address is incremented to obtain successive parameters, and when done, needs to be incremented (by 1 or 2) one more time to return to actual code of the caller instead of returning to the inline parameters, which are, of course, data, not code.

The style Hyde is using for this PRTSTR is this inline parameter mechanism.

Here is the usage of this PRTSTR:

    ...
STRING STR "HELLO THERE"
    ...
START
    JSR PRTSTR    # call to print string
    ADR STRING    # pointer parameter passed "inline" within code, 
                  # this is data, to be used and then skipped over by print string
    ...           # actual return location to resume code in START

Given that the 6502 had both a call stack and registers, it is a bit of a throwback to use the inline mechanism for calling, but certainly was done.  (The inline parameter passing mechanism predates call stacks and lots of registers as we have in today's modern processors.)

With a stack but few registers, some calls with numerous parameters would have been done placing parameters onto the stack, while others were done by passing in available registers.

In pure assembly code, you can create a custom calling convention for each function.  Only the advent of C and other high level languages creates the requirement for canonical, regular, and predictable calling conventions that can be codified in the compiler.


I have to admit that I don't understand the first of the three JSR INCRTN instructions.  (I understand the last two — they skip the pointer to string (the inline parameter following the JSR), and since pointers are two bytes, the address needs to increment by 2 bytes — but the very first of the three looks erroneous to me.)  There is no ADR opcode on 6502, so I have to assume it is an assembler pseudo opcode that forms a 2 byte pointer to the label. 

My assumption is somewhat confirmed by Hyde in 14-6 where it says:

    JSR SASIGN ;STRING ASSIGNMENT
    ADR DEST ;DEST = SOURCE
    ADR SOURCE

that the above is a 7 byte sequence (i.e. 1 for the JSR opcode, 2 for SASIGN (the operand of JSR), and 2 each for ADR DEST and ADR SOURCE).


Thus, the 16-bit variable at RTNADR and RTNADR + 1 needs to be incremented by one when the code does JSR INCRTN.


I believe this code sequence could be much improved by using an index register more directly as a pointer, instead of using memory indirect along with an integer in index register Y.

Further, as I cannot fathom the first JSR INCRTN, and the second is called twice, it might as well simply increment by 2 instead of by 1, twice.

0
G David On

This INCRTN subroutine should look something like this:

INCRTN:
    INC RTNADR      ; Add 1 to RTNADR
    BNE INCRTNEX
    INC RTNADR+$01  ; If the pointer passes a page boundary,
INCRTNEX:           ; advance to the next page.
    RTS

This performs a 16-bit increment on the RTNADR object.

An alternate version that would require entering with a value in A, but would be more flexible, is to use the following:

ADD2RTN:
    CLC
    ADC RTNADR
    STA RTNADR
    BCC ADD2RTNX
    INC RTNADR+$01
ADD2RTNX:
    RTS

This adds an 8-bit value (provided in A) to the 16-bit RTNADR object.

The first segment of code I've provided does exactly what the sample you've given would, while the second allows for much faster performance of more than two bytes need to be added.

In the code you have given, you could replace the code that loads the string address and advances the return address as follows:

LDY #$01
LDA (RTNADR),Y
STA ZPAGE
INY
LDA (RTNADR),Y
STA ZPAGE+$1
LDA #03
JSR ADD2RTN
;
LDY #0        ; Manually reset Y to 0 instead of using DEY twice to save time

This executes a few cycles faster than the original code (JSR and RTS each require 6 cycles and INC uses 5, since RTNADR is on the zero page) and doesn't add any code. When dealing with the 6502, every byte and execution cycle saved improves performance.