0

我正在关注兰迪·海德(randy hyde)使用6502汇编语言的书中的A totorial,在第14章第7节中有一部分他写了“jsr incrtn”问题是他没有创建一个名为incrtn的子例程这里是完整的代码,


    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

谁能帮我?

更新,如果有人想知道如何以更短的方式打印文本,这里有一些工作代码


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

2 回答 2

4

在处理器有堆栈或足够的寄存器之前,通常通过“内联”参数传递来传递参数,即在JSR或调用类型指令之后传递参数,通过将值或指针直接放在调用指令之后,要求被调用者返回以恢复呼叫者时跳过它们。

如您所知,调用指令捕获返回地址(不知何故,某处:可能在堆栈上,也可能是子程序的代码内存!),并且该返回地址可用于获取放置在调用指令之后的参数。返回地址递增获取连续的参数,完成后需要再递增一次(1或2)才能返回调用者的实际代码,而不是返回到内联参数,当然是数据,而不是代码。

海德为此使用的风格PRTSTR就是这种内联参数机制。

这是 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

鉴于 6502 具有调用堆栈和寄存器,使用内联机制进行调用有点倒退,但肯定已经完成。(内联参数传递机制早于我们在当今现代处理器中所拥有的调用堆栈和大量寄存器。)

对于堆栈但寄存器很少,一些具有大量参数的调用将通过将参数放入堆栈来完成,而其他调用则通过传入可用寄存器来完成。

在纯汇编代码中,您可以为每个函数创建自定义调用约定。只有 C 和其他高级语言的出现才需要可以在编译器中编码的规范、常规和可预测的调用约定。


我不得不承认我不理解三个JSR INCRTN指令中的第一个。(我理解最后两个——它们跳过了指向字符串的指针(后面的内联参数JSR),并且由于指针是两个字节,地址需要增加 2 个字节——但三个中的第一个在我看来是错误的。) 6502 上没有ADR操作码,所以我不得不假设它是一个汇编程序伪操作码,形成一个指向标签的 2 字节指针。 

我的假设在 14-6 中得到了海德的一定程度的证实,它说:

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

以上是一个 7 字节的序列(即 1 用于JSR操作码,2 用于SASIGN(的操作数JSR),以及 2 用于ADR DESTADR SOURCE)。


因此, 16 位变量 atRTNADRRTNADR + 1需要在代码执行时加一JSR INCRTN


我相信这个代码序列可以通过更直接地使用索引寄存器作为指针来改进,而不是使用间接内存和索引寄存器 Y 中的整数。

此外,由于我无法理解第一个JSR INCRTN,而第二个被调用了两次,它还不如简单地增加 2 而不是 1,两次。

于 2019-12-13T19:59:22.683 回答
0

此 INCRTN 子例程应如下所示:

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

这将对 RTNADR 对象执行 16 位增量。

需要在 A 中输入值但更灵活的替代版本是使用以下内容:

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

这会将一个 8 位值(在 A 中提供)添加到 16 位 RTNADR 对象。

我提供的第一段代码与您给出的示例完全一样,而第二段代码允许更快的性能,需要添加两个以上的字节。

在您给出的代码中,您可以替换加载字符串地址并推进返回地址的代码,如下所示:

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

这比原始代码执行速度快几个周期(JSR 和 RTS 各需要 6 个周期,而 INC 使用 5 个,因为 RTNADR 在零页上)并且不添加任何代码。在处理 6502 时,节省的每个字节和执行周期都会提高性能。

于 2020-01-26T21:13:37.970 回答