Какие методы использования потока управления существуют в 6502?
Я пытаюсь понять поток управления в 6502 сборке.
Скажем, у меня есть следующий код:
ControlFlow:
lda mem
cmp #1
bne .sub_one
cmp #2
bne .sub_two
.sub_one:
; sub routine one goes here
jmp .done ; <-------------- without this jmp, .sub_two will execute
.sub_two:
; sub routine two goes here
.done:
rts
Лично я хотел бы, чтобы оператор switch или какая-то другая структура потока управления. JMP выше касается и меня. Кажется, что есть лучший способ обработки нескольких случаев без такого типа кода спагетти.
7 ответов
На самом деле лучшего способа нет, но может быть улучшение, например, вызов FlowControl в качестве подпрограммы и возврат с RTS.
Это основной поток.
jsr ControlFlow
; main routine continues here
Вот подпрограмма.
ControlFlow:
lda mem
cmp #1
bne .sub_one
cmp #2
bne .sub_two
cmp #3
bne .sub_three
; case else here
rts
.sub_one:
; sub routine one goes here
rts
.sub_two:
; sub routine two goes here
rts
.sub_three:
; sub routine three goes here
rts
если подпрограммы слишком длинные, вам нужно использовать JMP, как упоминалось ранее.
.jump_to_sub_one
jmp .sub_one
.jump_to_sub_two
jmp .sub_two
.jump_to_sub_three
jmp .sub_three
ControlFlow:
lda mem
cmp #1
bne .jump_to_sub_one
cmp #2
bne .jump_to_sub_two
cmp #3
bne .jump_to_sub_three
; case else here
rts
.sub_one:
; sub routine one goes here
rts
.sub_two:
; sub routine two goes here
rts
.sub_three:
; sub routine three goes here
rts
Вот как это делается, и, к сожалению, лучшего способа нет. Это относится ко многим языкам ассемблера, если не ко всем.
Таблицы переходов могут быть полезны, если количество дел достаточно велико. Слева есть шаблон (непроверенный) для перехода к метке, который помещает правильный адрес в стек и возвращает. Справа есть подпрограмма на основе diff to jsr, которая будет продолжаться на ярлыке _out:
после возвращения из каждой подпрограммы. Логика переноса инвертирована на 6502, что означает, что перенос будет установлен, если (Acc >= Imm).
; goto label[n] vs. call label[n]
lda variable
cmp #MAX_PLUS_ONE
bcs _out
tax
lda table_hi, X
pha vs. sta jsrcmd+2
lda table_lo, X
pha vs. sta jsrcmd+1
rts vs. jsrcmd: jsr 1000 ; self modify
_out:
6502 обеспечивает следующее для управления потоком программ, т.е. изменяет регистр ПК.
- JMP абсолют
- JMP косвенный
- Bxx относительные инструкции
- JSR абсолют с более поздним RTS
- BRK или другой IRQ с более поздним RTI (или RTS, если вы тянете
.P
со стека) - Загрузка двух значений в стек, а затем RTS/RTI
- Аппаратный сброс вызывает скачок через вектор сброса
Вот и все. Если вы хотите что-то более сложное, вам нужно создать его, используя один или несколько из вышеперечисленных.
Одним из способов реализации оператора switch является создание таблицы указателей на все подпрограммы, участвующие в операторе switch. Разделите их в соответствии с младшими байтами процедур и затем старшими байтами:
switchtab_lo .db >routine1, >routine2, >routine3
switchtab_hi .db <routine1, <routine2, <routine3
(Я никогда не помню, если> означает младший или старший байт, и разные ассемблеры могут иметь разный синтаксис)
и затем, предполагая, что значение, на которое вы хотите переключиться, находится в.X, и это vector
это два байта, которые не начинаются в конце страницы (чтобы избежать косвенной ошибки JMP), и вы убедились, что это допустимое значение:
lda switchtab_lo,X
sta vector
lda switchtab_hi,X
sta vector+1
jmp (vector)
Это утомительно делать каждый раз, когда вам нужно переключиться, но именно поэтому были изобретены языки высокого уровня.
lda mem
asl
sta jump+1
jump jmp (vector)
;should be page aligned
vector
!word func1, func2, func3, func4
если список векторов не выровнен, необходимо добавить индекс *2 ко всему векторному адресу, медленнее, но с большей эффективностью использования памяти.
другие варианты будут:
lda mem
asl
clc
adc mem ;we assume it does not overflow, so carry stays cleared
sta branch+1 ;mem * 3
branch bcc *
jmp func1
jmp func2
jmp func3
jmp ...
CMOS 6502 (т.е. 65c02) также имеет режим адресации JMP(abs,X), так что вы можете взять вход в A, сдвинуть влево на 1 бит с ASL A (потому что каждый адрес в таблице занимает два положения), затем перенесите его в X и выполните JMP(Addr_Table,X). Гораздо проще Это одно из многих дополнений кода операции, сделанных в версии CMOS. (Версия CMOS также исправила все ошибки версии NMOS).
У меня есть статья об использовании макросов для создания структур программы в сборке 6502, по адресу http://wilsonminesco.com/StructureMacros/index.html. Я сделал это и для PIC, и там есть ссылки на исходный код для обоих. Дальнейшие дополнения будут, когда я закончу проект, над которым я работаю.
OP - идеальная ситуация для IF...ELSE...END_IF. Когда требуется больше случаев, таблица переходов работает хорошо, если числа являются последовательными, и вам не нужно проверять пределы, чтобы избежать непрямого прыжка из-за пределов таблицы и сбоя. В противном случае оператор CASE работает хорошо. http://forum.6502.org/viewtopic.php?f=2&t=2311&start=15 - это вторая страница такого обсуждения, и я покажу там, как можно проверить отдельные случаи, диапазон чисел или разброс. чисел, все в том же заявлении CASE. У меня еще нет RANGE_OF и SET_OF как 6502 макросов сборки. Это те два, которые у меня есть только в Форт.
Цель этих макросов, конечно, состоит в том, чтобы получить лучший контроль над кодом, сделав его намного более понятным, что вы делаете, и избавившись от массы меток и переходов, которые обычно характеризуют ассемблерный код. Макросы позволяют вам полностью контролировать каждую часть кода, заложенную ассемблером, но вам не нужно постоянно смотреть на внутренние детали. В большинстве случаев это абсолютно нулевой штраф в памяти программы или в скорости выполнения. Вы получаете производительность сборки со многими преимуществами языков более высокого уровня. Код становится быстрее разрабатываться, становится более свободным от ошибок и более легким в обслуживании, а это означает, что легче вернуться позже и выяснить, что вы сделали, когда решили добавить функцию или что-то изменить.
Я не знаю, как бы вы сделали это на 6502, но switch
Они часто компилируются в таблицы переходов.