6502 проблема подпрограммы randy hyde "JSR INCRTN"
Я слежу за A totorial в книге, использующей язык ассемблера 6502 от Randy Hyde, есть часть в главе 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
2 ответа
До того, как у процессоров был стек или достаточное количество регистров, параметры часто передавались с помощью "встроенной" передачи параметров, которая должна передавать параметры после JSR
или инструкции типа вызова, помещая значения или указатели непосредственно после инструкции вызова, с требованием, чтобы вызываемый объект пропустил их при возврате, чтобы возобновить вызов.
Как вы знаете, инструкция вызова фиксирует адрес возврата (каким-то образом где-то: может быть, в стеке или, может быть, в памяти кода подпрограммы!), И этот адрес возврата может использоваться для выборки параметров, которые помещаются после инструкции вызова. Адрес возврата увеличивается для получения следующих друг за другом параметров, и когда это сделано, его необходимо увеличить (на 1 или 2) еще раз, чтобы вернуться к фактическому коду вызывающего объекта вместо возврата к встроенным параметрам, которые, конечно же, являются данными., а не код.
Стиль, который использует для этого Хайд PRTSTR
это механизм встроенных параметров.
Вот использование этого 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 байта - но самый первый из трех мне кажется ошибочным.) ADR
код операции на 6502, поэтому я должен предположить, что это псевдо-код операции ассемблера, который формирует 2-байтовый указатель на метку.
Мое предположение отчасти подтверждается Хайдом в 14-6, где он говорит:
JSR SASIGN ;STRING ASSIGNMENT
ADR DEST ;DEST = SOURCE
ADR SOURCE
что это 7-байтовая последовательность (т.е. 1 для JSR
код операции, 2 для SASIGN
(операнд JSR
), и по 2 для ADR DEST
а также ADR SOURCE
).
Таким образом, 16-битная переменная в RTNADR
а также RTNADR + 1
должен быть увеличен на единицу, когда код JSR INCRTN
.
Я считаю, что эту кодовую последовательность можно значительно улучшить, если использовать индексный регистр более непосредственно в качестве указателя, вместо использования косвенной памяти вместе с целым числом в индексном регистре Y.
Далее, поскольку я не могу понять первый JSR INCRTN
, а второй вызывается дважды, с таким же успехом он может увеличиваться на 2 вместо 1 дважды.
Эта подпрограмма 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
Это выполняет 16-битное приращение объекта RTNADR.
Альтернативная версия, которая потребовала бы ввода значения в 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 каждый сохраненный байт и цикл выполнения улучшают производительность.