Может, вы сочтете это бредом и начнете опять гонять меня поганой метлой за ассемблер, но я все равно решил это выложить. Может кому пригодится. Итак, Switch-Case технология мне не понравилась сразу. Если состояний автомата много, то код просто неприлично раздувается. Тогда на помощь приходит индексный переход. Насколько я слышал, компиляторы это и делают при оптимизации. Накидывая в очередной раз что-то вроде этого:
FSM:
ldiy _KEYS_FLAGS
ldd FLAGS,Y+DISP_KEYS_FLAGS
clr ZH
ldd ZL,Y+DISP_KEYS
cpi ZL,_QUANT_KEYS_STATES
brsh Process_Errors_KBD_DRV
subi ZL,LOW(-Table_KBD_DRV)
sbci ZH,HIGH(-Table_KBD_DRV)
shiftlwz
lpm r17,Z+
lpm r16,Z
movw ZH:ZL,r17:r16
icall
std Y+DISP_KEYS_FLAGS,FLAGS
ret
Process_Errors_KBD_DRV:
cli
ldi r16,_ERROR_KBD_DRV
rjmp Process_Errors
Table_KBD_DRV:
.db tab_h(Keys_Init)
.db tab_h(Keys_None)
.db tab_h(Keys_Down)
.db tab_h(Keys_Wait_Up)
.db tab_h(Keys_Up)
Я подумал, может преврать это в макрос или в подпрограмму? Но недавно я вспомнил, как ReAl подсказал использовать
ldd\std вместо lds\sts. Прикинул и накидал один обработчик автоматов вместо нескольких. На первый взгляд, выигрыш слабый,
но, заменяются все куски кода "Process_Errors_KBD_DRV" на один "Process_Errors_FSM". И все подпрограммы индексного перехода
объединяются в одну подпрограмму. А если автоматов много, то выигрыш очень существенный. Во всяком случае, лично я увидел
в этом смысл.
.dseg
;==================================================================================================
_PROC_FSM: ; Состояние автомата.
.byte 1
_QUANT_STATES_FSM: ; Количество состояний автомата.
.byte 1
_TABLE_FSM_H: ; Таблица обработчиков состояний автомата.
.byte 1
_TABLE_FSM_L: ; Таблица обработчиков состояний автомата.
.byte 1
_ERROR_FSM: ; Номер ошибки автомата.
.byte 1
;==================================================================================================
.cseg
;==================================================================================================
.macro Init_FSM
ldix _PROC_FSM
lds r16,@0 ; Состояние автомата.
st X+,r16
ldiz @1*2
ldi CNT,4
rcall _Init_FSM
.endmacro
_Init_FSM:
lpm r16,Z+
st X+,r16
dec CNT
brne _Init_FSM
sbiw XL,5
ret
;==================================================================================================
Process_FSM:
clr ZH
ld ZL,X+
ld r16,X+
cp ZL,r16
brsh Process_Errors_FSM
ld r17,X+
ld r16,X+
add ZL,r16
adc ZH,r17
shiftlwz
lpm r17,Z+
lpm r16,Z
movw ZH:ZL,r17:r16
ijmp
Process_Errors_FSM:
cli
adiw XL,2
ld r16,X
rjmp Process_Errors
; Пример:
.include "fsm.inc"
.include "kbd_drv.inc"
Def_Num_Err _ERROR_KBD_DRV
Def_Num_Err _ERROR_KEYS
.equ DEBOUNC_DELAY = 20
.dseg
_KEYS_FLAGS:
.byte 1
.equ KEYS_PRESSED_FLG = 0
.equ DEFINE_KEYS_FLG = 1
_KEYS:
.byte 1
.equ _QUANT_KEYS_STATES = 5
.equ _KEYS_INIT = 0
.equ _KEYS_NONE = 1
.equ _KEYS_DOWN = 2
.equ _KEYS_WAIT_UP = 3
.equ _KEYS_UP = 4
_KEYS_PREV:
.byte 1
_KEYS_CURRENT:
.byte 1
_KEYS_CODE:
.byte 1
.equ DISP_KEYS_FLAGS = 0
.equ DISP_KEYS = 1
.equ DISP_KEYS_PREV = 2
.equ DISP_KEYS_CURRENT = 3
.equ DISP_KEYS_CODE = 4
.cseg
KBD_DRV:
Scan_Keys
Init_FSM _KEYS,Table_KBD_DRV_FSM
rcall Process_FSM
sbrc FLAGS,DEFINE_KEYS_FLG
rcall Define_Keys
std Y+DISP_KEYS_FLAGS,FLAGS
ret
Keys_Init:
Init_KBD_DRV
Set_State _KEYS,_KEYS_NONE
Keys_None:
Set_State _KEYS,_KEYS_DOWN
Keys_NONE_End:
ret
Keys_DOWN:
Set_State _KEYS,_KEYS_WAIT_UP
ret
Keys_Not_DOWN:
Set_State _KEYS,_KEYS_NONE
Keys_DOWN_End:
ret
Keys_WAIT_UP:
Set_State _KEYS,_KEYS_UP
Keys_WAIT_UP_END:
ret
Keys_UP:
Set_State _KEYS,_KEYS_NONE
ret
Keys_Not_UP:
Set_State _KEYS,_KEYS_WAIT_UP
Keys_UP_End:
ret
Table_KBD_DRV_FSM:
.db _QUANT_KEYS_STATES,tab_h(Table_KBD_DRV),_ERROR_KBD_DRV
Table_KBD_DRV:
.db tab_h(Keys_Init)
.db tab_h(Keys_None)
.db tab_h(Keys_Down)
.db tab_h(Keys_Wait_Up)
.db tab_h(Keys_Up)
Table_Keys_Code:
.db 0b00000001, 0
.db 0b00000010, 1
.db 0b00000100, 2
.db 0b00001000, 3
.db 0b00010000, 4
.db 0b00100000, 5
.db 0b01000000, 6
.db 0b10000000, 7
Table_Proc_Codes_Keys_FSM:
.db _QUANT_KEYS_CODE,tab_h(Table_Proc_Codes_Keys),_ERROR_KEYS
Table_Proc_Codes_Keys:
.db tab_h(Process_Enter)
.db tab_h(Process_Ecs)
.db tab_h(Process_Up)
.db tab_h(Process_Down)
.db tab_h(Process_Plus)
.db tab_h(Process_Minus)