Лучший способ написания подпрограмм на ассемблере 6502?

#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 , вам, возможно, придется переосмыслить это, если вы делаете это из обработчика прерываний. Но это также сохраняет «истинный» стек чистым и более чем в три раза увеличивает ваше доступное пространство.