; vim: sts=4 sw=8 et ai fdm=marker ft=asm ; ; fnordlicht firmware ; ; for additional information please ; see http://koeln.ccc.de/prozesse/running/fnordlicht ; ; (c) by Alexander Neumann ; and Lars Noschinski ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License version 2 as ; published by the Free Software Foundation. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; ; For more information on the GPL, please go to: ; http://www.gnu.org/copyleft/gpl.html ; CONSTANTS {{{ ; ================================================================================== ; Various script handlers, if 0 or above 128 (bit7 set), script execution is disabled .equ ScriptHandlerDisabled = 0 ; Script execution disabled .equ ScriptHandlerMem = 1 ; Execute scripts in RAM .equ ScriptHandlerFlash = 2 ; Execute scripts in Flash .equ ScriptHandlerWait = 3 ; Wait till a given event occurs .equ ScriptHandlerSleep = 4 ; Sleeps a given number of cycles .equ ScriptSignalStop = 5 ; if set, signals the handler to don't execute ; any further instructions in this cycle ; (5 because of the symmetric list). fnord. ; Opcodes .equ OpcodeNop = 0x00 .equ OpcodeFadeChannel = 0x10 .equ OpcodeFadeChannels = 0x20 .equ OpcodeJump = 0x30 .equ OpcodeSetChannel = 0x40 .equ OpcodeSleep = 0x50 .equ OpcodeWait = 0x60 .equ OpcodeClear = 0x70 ; }}} .dseg ; table: ScriptInfoTable {{{ ; ============================================================================= .equ ScriptChannels = 3 ; number of concurrent scripts .equ ScriptStackSize = 6 ; in byte .equ ScrRecHandler = 0 ; byte .equ ScrRecPos = 1 ; word .equ ScrRecStackOffset = 3 ; byte .equ ScrRecFlags = 4 ; byte .equ ScrRecStackBase = 5 ; stack .equ ScrRecSize = 5+ScriptStackSize ScriptInfoTable: .byte ScrRecSize*ScriptChannels ; }}} .cseg ; table: OperatorJumpTable {{{ ; ================================================================================== ; Operator order must be consistend with the Opcode* constants above OperatorJumpTable: rjmp do_op_nop rjmp do_op_fade_channel rjmp do_op_fade_channels rjmp do_op_jump rjmp do_op_set_channel rjmp do_op_sleep rjmp do_op_wait rjmp do_op_clear ; }}} ; table: ScriptHandlerJumpTable {{{ ; ================================================================================== ; Handler order must be consistend with the ScriptHandler* constants above ScriptHandlerJumpTable: ret ; script disabled rjmp handler_mem rjmp handler_flash rjmp handler_wait rjmp handler_sleep ; }}} ; MACROS {{{ ; ================================================================================== ; macro: RESTORE_HANDLER {{{ ; ================================================================================== ; Restores current handler from stack ; ; uses: @0, @1, @2 ; @0 is not modified, but must point to the script record. ; ; @0 @1 @2 ; call: SAVE_HANDLER ScriptRecord TmpPointer TmpReg ; ; @0, @1 must be Y or Z, Tm .macro MACRO_RESTORE_HANDLER ldd @2, @0+ScrRecStackOffset ; Load stack pointer to @2 register mov @1L, @2 subi @1L, 3 ; decrement stack pointer offset by 3 std @0+ScrRecStackOffset, @1L ; store new offset movw @1H:@1L, @0H:@0L adiw @1H:@1L, ScrRecStackBase ; stack base offset add @1L, @2 ; stack data offset clr @2 adc @1H, @2 MACRO_STACK_POP @1, @2 ; Restore old stack handler std @0+ScrRecPos+1, @2 MACRO_STACK_POP @1, @2 std @0+ScrRecPos, @2 MACRO_STACK_POP @1, @2 std @0+ScrRecHandler, @2 .endmacro ; }}} ; macro: SAVE_HANLDER {{{ ; ================================================================================== ; Saves current handler to stack ; ; uses: @0, @1, @2 ; @0 is not modified, but must point to the script record. ; ; @0 @1 @2 ; call: SAVE_HANDLER ScriptRecord TmpPointer TmpReg ; ; @0, @1 must be Y or Z, Tm .macro MACRO_SAVE_HANDLER ldd @2, @0+ScrRecStackOffset ; Load stack pointer to @1 register mov @1L, @2 ; copy stack pointer offset adiw @1H:@1L, 3 ; increment stack pointer offset by 3 std @0+ScrRecStackOffset, @1L ; store new offset movw @1H:@1L, @0H:@0L adiw @1H:@1L, ScrRecStackBase ; stack base offset add @1L, @2 ; stack data offset clr @2 adc @1H, @2 ldd @2, @0+ScrRecHandler ; Save old handler to stack MACRO_STACK_PUSH @1, @2 ldd @2, @0+ScrRecPos MACRO_STACK_PUSH @1, @2 ldd @2, @0+ScrRecPos+1 MACRO_STACK_PUSH @1, @2 .endmacro ; }}} ; macro: MACRO_SET_HANDLER {{{ ; ================================================================================== ; Sets a new script handler ; ; @0 @1 @3:@2 ; call: SET_HANDLER ScriptRecord, NewHandler, HandlerPos ; ; @0 must be Y or Z .macro MACRO_SET_HANDLER std @0+ScrRecHandler, @1 std @0+ScrRecPos, @2 std @0+ScrRecPos+1, @3 .endmacro ; }}} ; }}} ; function: execute_scripts {{{ ; ================================================================================== ; Calls execute_script for each script. execute_scripts: ldi YL, low(LedStatusTable) ; construct script flags from led flags ldi YH, high(LedStatusTable) ; flags are given as w1 to execute_script clr w1 ; clear w1, this will hold the led flags ldi w2, PWMChannels ; w2 is loop counter register es_readflags_loop: ldd w3, Y+LedRecFlags ; load flags sbrc w3, LedFlagTargetReached ; test if target has been reached on this channel ori w1, (1 << (PWMChannels-1)) ; if true: set bit in w1 andi w3, !(1< 0: tell the current handler to not execute ; any further instructions in this cycle subi w2,1 sbci w3,0 clr w4 ; sleep still > 0: install handler cp w2,w4 cpc w3,w4 breq do_op_sleep_return MACRO_SAVE_HANDLER Y, Z, w4 ; Save old handler ldi w4, ScriptHandlerSleep MACRO_SET_HANDLER Y, w4, w2, w3 ; activate sleep handler do_op_sleep_return: ret ; }}} ; function: do_op_wait {{{ ; ========================== ; we get the event(s) to wait for at w2 ; ; do_op_wait: ldd w3, Y+ScrRecFlags ; load flags mov w1, w3 ; copy flags and w1, w2 ; check for match brne do_op_wait_direct_match ; if match, jump away MACRO_SAVE_HANDLER Y, Z, w1 ldi w1, ScriptHandlerWait ; load handler offset clr w3 ; empty w3 MACRO_SET_HANDLER Y, w1, w2, w3 ; activate wait handler ldi w1,(1<