#assembly #calling-convention #6502 #c64
Вопрос:
Я новичок в ассемблерах, так что вот простой вопрос:
Мои пользовательские подпрограммы изменяют регистры X
, Y
, и. A
Они манипулируют ими, чтобы получить желаемые результаты. Хорошая ли идея помещать эти значения в стек при запуске процедуры и восстанавливать их до RTS
этого ?
Я имею в виду, что таким образом я могу писать процедуры, которые можно вызывать из любого места, не нарушая «состояние» или не влияя на другие процедуры. Но можно ли использовать стек таким образом? Или есть лучший способ сделать это?
Комментарии:
1. Все зависит от того, какое соглашение о вызовах вы хотите реализовать. Если вы определяете, что ваша функция перезаписывает определенный регистр, и вы можете записать всех вызывающих абонентов, чтобы учесть это, вы можете сделать выбор в любом случае.
2. На старых машинах обычно используется пользовательское и потенциально различное соглашение о вызове для каждой функции, основанное как на ее сигнатуре (параметры и возвращаемое значение), так и на внутренней реализации-это практично при программировании в сборке и становится сложнее в C.
3. В C64 обычно имеет смысл использовать фиксированные адреса на нулевой странице для передачи некоторых параметров, особенно 16-разрядных адресов.
Ответ №1:
Но можно ли использовать стек таким образом? Или есть лучший способ сделать это?
Абсолютно; BASIC делает это все время, как и многие процедуры в ядре.
Но, на это нет правильного ответа, все сводится, по крайней мере, к скорости, портативности и стилю.
- Если вы часто используете стек, есть некоторые соображения по скорости. Ваш типичный
pha
txa
pha
tya
pha
в начале, а затем обратный (pla
tay
pla
tax
pla
) съедает 3 байта вашего стека и добавляет за некоторое время цикла из-за операций 2 х 5 - Вы могли бы использовать нулевую страницу, но это отнимает некоторую переносимость между разными машинами; VIC-20, C64, C128, адреса бесплатных нулевых страниц могут отличаться на разных платформах. И ваша подпрограмма не может быть вызвана «более одного раза» без первого выхода (например, без рекурсии), потому что, если она вызывается, пока она активна, она перезапишет нулевую страницу новыми значениями. Но вам не нужно использовать нулевую страницу…
- …потому что вы можете просто создать свои собственные ячейки памяти как часть своего кода:
myroutine = * ; do some stuff.. rts mymem =* .byt 0, 0, 0
- недостатком этого является то, что ваша процедура может быть вызвана только «один раз», в противном случае последующие вызовы перезапишут ваши области хранения (например, рекурсия запрещена!!, та же проблема, что и раньше!)
- Вы могли бы написать свой собственный мини-стек
put_registers =* sei ; turn off interrupts so we make this atomic sty temp ldy index sta a_reg,y stx x_reg,y lda temp sta y_reg,y inc index cli rts get_registers =* sei ; turn off interrupts so we make this atomic dec index ldy index lda y_reg,y sta temp lda a_reg,y ldx x_reg,y ldy temp cli rts a_reg .buf 256 x_reg .buf 256 y_reg .buf 256 index .byt 0 temp .byt 0
- Это имеет дополнительное преимущество в том, что теперь у вас есть 3 виртуальных стека (по одному для каждого из
.A
,.X
,.Y
), но за определенную плату (не совсем быстрая процедура). И поскольку мы используемSEI
иCLI
, вам, возможно, придется переосмыслить это, если вы делаете это из обработчика прерываний. Но это также сохраняет «истинный» стек чистым и более чем в три раза увеличивает ваше доступное пространство.