; ; coolsystem v0.1 ; ; visit https://hackaday.io ; ; filename: mega8.asm ; ; Author wants this file to be public domain, you can use it as you wish ; ; use this command to compile ; avra -l mega8_asm_list.txt mega8.asm ; .NOLIST .INCLUDE "/usr/share/avra/m8def.inc" .LIST ; let us assign handy names to registers .DEF rinterrupt = R2 ; SREG backup, no nested interrupts .DEF rret = R3 ; return value .DEF rkeys = R4 ; small keyboard readout .DEF rcounter = R5 .DEF rkeysspi = R6 ; spi readout .DEF rkeysspi2 = R7 .DEF rspierror = R8 .DEF rspirecv = R9 .DEF rspirecv2 = R10 .DEF rmp = R16 ; register multi-purpose .DEF rarg = R17 .DEF rarg2 = R18 .DEF rarg3 = R19 .DEF rarg4 = R20 .DEF rflags = R21 .DEF rspi = R22 .DEF rspimore = R23 ; assign bitmask for pins .EQU SMALL_KBD_D = 0b00000001 .EQU SMALL_KBD_L = 0b00000010 .EQU SMALL_KBD_C = 0b00000100 .EQU SMALL_KBD_IN = 0b00001000 ; OK, let us put some variables .DSEG .ORG SRAM_START v: ; table with variables keysmod: .BYTE 1 keys3: .BYTE 1 keys2: .BYTE 1 keys1: .BYTE 1 keys0: .BYTE 1 keys_timer1: .BYTE 1 keys_timer2: .BYTE 1 leds: .BYTE 1 leds_timer: .BYTE 1 ; OK, let us put interrupt table here at flash begin .CSEG .ORG $0000 rjmp init_routine reti reti reti reti reti reti reti reti rjmp keyboard_timer rjmp spi_complete reti reti reti reti reti reti reti reti ; OK, here will be various routines. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; init_routine (reset) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; init_routine: ldi rmp, 0b11111111 ; all pull-up resistors enable out PORTB, rmp out PORTC, rmp out PORTD, rmp ldi rmp, LOW(RAMEND) ; Init stack out SPL, rmp ldi rmp, HIGH(RAMEND) out SPH, rmp ldi rmp, 0b11000000 ; enable SPI interrupt out SPCR, rmp ldi rmp, 0b00010000 ; MISO output out DDRB, rmp ldi rmp, 5 mov rspierror, rmp clr rspi clr rspimore clr rkeys ldi rmp, 0b01000000 mov rkeysspi, rmp ldi rmp, 0b00010000 mov rkeysspi2, rmp rcall spi_complete ; send padding ldi rmp, 0b00000100 ; timer0 clock/256 (see mega8 datasheet on page 72) out TCCR0, rmp ldi rmp, 1 << TOIE0 ; timer 8-bit overflow interrupt enable out TIMSK, rmp ldi rmp, 1 << SE ; Enable sleep instruction out MCUCR, rmp clr rflags sei nop ldi rmp, SMALL_KBD_C | SMALL_KBD_D | SMALL_KBD_L ; small keyboard pins out DDRD, rmp ldi YL, LOW(v) ldi YH, HIGH(v) clr rmp std Y+keys0-v, rmp std Y+keys1-v, rmp std Y+keys2-v, rmp std Y+keys3-v, rmp sleep_loop: mov rmp, rflags andi rmp, 0b00000001 ; check kbd flag breq sleep_nokbd rcall keyboard_scan andi rflags, 0b11111110 ; clear kbd flag sleep_nokbd: rcall spi_recv_read tst rflags brne sleep_loop sleep nop rjmp sleep_loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; spi_complete (interrupt) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; spi_complete: in rinterrupt, SREG push rmp tst rspi brne spi_rspi out SPDR, rkeysspi mov rmp, rkeysspi mov rkeysspi, rkeysspi2 mov rkeysspi2, rmp rjmp spi_input spi_rspi: out SPDR, rspi ; send custom byte mov rspi, rspimore clr rspimore spi_input: mov rspirecv2, rspirecv in rspirecv, SPDR ori rflags, 0b00001000 ; set spirecv flag ;lds rmp, leds ;inc rmp ;sts leds, rmp pop rmp out SREG, rinterrupt reti ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; keyboard_timer (interrupt) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; keyboard_timer: in rinterrupt, SREG ori rflags, 0b00000001 ; set kbd flag out SREG, rinterrupt reti ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; keyboard_scan (it can be used as interrupt) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; keyboard_scan: in rinterrupt, SREG push rmp push rarg push rarg2 push rarg3 push rarg4 push rret push YL push YH ; let us scan small keyboard in rmp, DDRD ori rmp, SMALL_KBD_D | SMALL_KBD_L | SMALL_KBD_C ; outputs andi rmp, ~SMALL_KBD_IN ; input out DDRD, rmp in rmp, PORTD andi rmp, ~(SMALL_KBD_C | SMALL_KBD_L | SMALL_KBD_IN) ; 595 LEDs latch low, clock low out PORTD, rmp ldi rarg, SMALL_KBD_D ; load bitmask ldi rarg2, SMALL_KBD_C ldi rarg3, 0 ; no input pin yet ldi rarg4, 0b00000001 rcall transfer_be in rmp, PORTD andi rmp, ~SMALL_KBD_D ; data low out PORTD, rmp rcall spi_recv_read ldi rmp, 8 mov rret, rmp ; use rret as a counter clr rkeys rotate_loop: ldi rarg2, SMALL_KBD_D ; pulse data wire high to latch second 595 rcall pulse_pin ldi rarg2, SMALL_KBD_C ; pulse clock high to shift the pointer one rcall pulse_pin in rmp, PIND andi rmp, SMALL_KBD_IN clc ; clear carry breq rotate_nosec sec ; set carry rotate_nosec: ror rkeys ; rotate right through carry dec rret brne rotate_loop ; rkeys contains fresh readout ; let us process keyboard state ldi YL, LOW(v) ldi YH, HIGH(v) ; pointer to variables ldd rmp, Y+keys2-v ; load with displacement or rmp, rkeys std Y+keys3-v, rmp ldd rmp, Y+keys1-v or rmp, rkeys std Y+keys2-v, rmp ldd rmp, Y+keys0-v or rmp, rkeys std Y+keys1-v, rmp std Y+keys0-v, rkeys ldi rarg, 0b01000000 ldi rarg2, 0b01010000 ; spi template mov rmp, rkeys andi rmp, 0b00001111 or rarg, rmp mov rmp, rkeys ; prepare spi msg lsr rmp lsr rmp lsr rmp lsr rmp or rarg2, rmp mov rret, rarg ; make parity bit mov rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ; XOR 6 bits clr rret inc rret eor rmp, rret ; flip parity bit bst rmp, 0 ; bit store T bld rarg, 6 ; bit load from T mov rret, rarg2 ; make another parity bit mov rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ror rret eor rmp, rret ; XOR 6 bits clr rret inc rret eor rmp, rret ; flip parity bit bst rmp, 0 ; bit store T bld rarg2, 6 ; bit load from T cli mov rmp, rkeysspi bst rmp, 4 brts keyboard_swap mov rkeysspi, rarg mov rkeysspi2, rarg2 sei rjmp keyboard_reti keyboard_swap: mov rkeysspi2, rarg mov rkeysspi, rarg2 sei keyboard_reti: ldi rarg2, SMALL_KBD_C ldi rarg , SMALL_KBD_D ldd rarg4, Y+leds-v rcall reverse_byte rcall transfer_be ; output LEDs ldi rarg2, SMALL_KBD_L ; latch LEDs rcall pulse_pin pop YH pop YL pop rret pop rarg4 pop rarg3 pop rarg2 pop rarg pop rmp out SREG, rinterrupt reti ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; transfer_be (feed data to the shift register, MSB first, clock must be already low) ; args: ; rarg - bitmask for shift register MOSI data pin for PORTD ; rarg2 - bitmask for shift clock pin ; rarg3 - bitmask for MISO data pin ; rarg4 - data do be shifted out ; ; returns: ; rret - data received (MSB first) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; transfer_be: ldi rmp, 8 mov rcounter, rmp transfer_loop: in rmp, PORTD com rarg and rmp, rarg com rarg rol rarg4 ; rotate left through carry brcc transfer_zero or rmp, rarg transfer_zero: out PORTD, rmp nop nop nop nop nop nop nop nop nop nop rcall pulse_pin dec rcounter brne transfer_loop ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; pulse_pin (note: pin must be already low) ; args: ; rarg2 - PORTD pin bitmask ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pulse_pin: nop nop nop nop in rmp, PORTD or rmp, rarg2 out PORTD, rmp nop nop nop nop nop nop nop nop nop nop nop nop in rmp, PORTD com rarg2 and rmp, rarg2 out PORTD, rmp com rarg2 nop nop nop nop ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; debug_wait (large delay) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; debug_wait: push rmp push rarg push rarg2 clr rarg clr rmp debug_wait_loop: nop nop nop nop dec rmp brne debug_wait_loop dec rarg brne debug_wait_loop pop rarg2 pop rarg pop rmp ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; debug_light ; args: ; rarg4 - LEDs pattern ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; debug_light: push rarg push rarg2 ldi rarg, SMALL_KBD_D ldi rarg2, SMALL_KBD_C ldi rarg3, 0 ; rarg4 is set ori rarg4, 0b10000000 rcall transfer_be ldi rarg2, SMALL_KBD_L ; latch LEDs rcall pulse_pin rcall debug_wait ldi rarg2, SMALL_KBD_C clr rarg4 rcall transfer_be ldi rarg2, SMALL_KBD_L ; latch LEDs rcall pulse_pin rcall debug_wait pop rarg2 pop rarg ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; reverse_byte (from big-endian to low-endian) ; args: ; rarg4 - byte to be reversed ; ret: ; rarg4 - reversed byte ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; reverse_byte: push rret mov rret, rarg4 ldi rmp, 8 reverse_byte_loop: ror rret rol rarg4 dec rmp brne reverse_byte_loop pop rret ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; spi_recv_read (call this function more often) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; spi_recv_read: in rmp, SREG push rmp cli tst rspirecv breq spi_recv_nothing sts leds, rspirecv clr rspirecv clr rspirecv2 spi_recv_nothing: pop rmp out SREG, rmp ; sei ret