Created
April 28, 2018 17:33
-
-
Save DaveCTurner/3fdf2483244db91a511ede03cd772b7e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; | |
; twinklies.asm | |
; | |
.include "./m328Pdef.inc" | |
.dseg | |
coltable: | |
.byte 64 | |
rngtable: | |
.byte 22 | |
rnghistory: | |
.byte 18 | |
.eseg | |
lastColTableIndex: | |
.byte 1 | |
.cseg | |
.def nextPixelColourIndex = r19 | |
.def indexL = r24 | |
.def indexH = r25 | |
.def colTableIndex = r1 | |
.equ colTableCount = 7 | |
start: | |
; set up stack | |
ldi r16, low(RAMEND) | |
out SPL, r16 | |
ldi r16, high(RAMEND) | |
out SPH, r16 | |
; read colTableIndex from EEPROM | |
readLastColTableIndex: | |
sbic EECR, EEPE | |
rjmp readLastColTableIndex | |
ldi r16, low(lastColTableIndex) | |
out EEARL, r16 | |
ldi r16, high(lastColTableIndex) | |
out EEARH, r16 | |
sbi EECR, EERE | |
in r16, EEDR | |
cpi r16, colTableCount | |
brcs colTableIndexInRange | |
clr r16 | |
colTableIndexInRange: | |
mov colTableIndex, r16 | |
rcall init_coltable | |
; initialise rngtable | |
ldi XL, low(rngtable) | |
ldi XH, high(rngtable) | |
ldi indexL, $15 ; 0x15 = 21 = 7 * 3 values to copy | |
init_rngtable_loop: | |
st X+, indexL | |
dec indexL | |
brne init_rngtable_loop | |
clr nextPixelColourIndex | |
; set PORTD0 as output and PORTD1 as input with an internal pull-up | |
ldi r16, $01 | |
out DDRD, r16 | |
sbi PORTD, 1 | |
sendFrame: | |
; record start states of the first 6 RNGs | |
ldi XL, low(rngtable) | |
ldi XH, high(rngtable) | |
ldi YL, low(rnghistory) | |
ldi YH, high(rnghistory) | |
ldi indexL, $12 ; 0x12 = 18 = 6 * 3 values to copy | |
saveHistoryLoop: | |
ld r16, X+ | |
st Y+, r16 | |
dec indexL | |
brne saveHistoryLoop | |
; count down from 0x0320 (=800) pixels | |
ldi indexH, $02; <-------------------- start timing the frame from here | |
ldi indexL, $20 | |
sendPixel: | |
; arrive here from the per-pixel RJMP loop, i.e. | |
; having already spent 2 cycles since the last bit. | |
; bit length is 40 cycles: | |
; (. = low ^ = high ? == high-when-1-but-low-when-0) | |
; |.3.|...12...|^2^|^^^5^^^|^1^|?1?|?? 8 ??|?1?|?1?|.2.|.4.| | |
; | | | | | | | | | | |^^^ RET | |
; | | | | | | | | | |^^^ CBI to end pulse if 1 | |
; | | | | | | | | |^^^ BRCC to check for end of 1 bit | |
; | | | | | | | |^^^ LSL to shift to the next bit | |
; | | | | | | |^^^^^^^^ free time C | |
; | | | | | |^^^ skipped CBI to end pulse if 0 | |
; | | | | |^^^ SBRS to check for end of 0 bit | |
; | | | |^^^^^^^ free time B | |
; | | |^^^ SBI to start pulse | |
; | |^^^^^^^^ free time A | |
; |^^^ RCALL | |
; *** Look up the colour of the next pixel | |
; nextPixelColourIndex will be 0/4/8/12 indicating colour of next pixel | |
ldi YL, low(coltable) ; 1cy | |
ldi YH, high(coltable) ; 1cy | |
add YL, nextPixelColourIndex ; 1cy | |
brcc coltable_no_overflow ; 1cy (in case coltable spans a 0x100 boundary) | |
inc YH ; 1cy | |
coltable_no_overflow: | |
; Y points to the RGB colour values | |
; send the red byte (in r0) and update the RNGs | |
ld r0, Y+ ; 2cy | |
rcall sendbit31 ; send bit R0 (31cy) | |
wdr ; 1cy | |
; initialise X to point at the RNG states table | |
ldi XL, low(rngtable) ; 1cy | |
ldi XH, high(rngtable) ; 1cy | |
clr nextPixelColourIndex ; 1cy | |
rcall sendbit36 ; send bit R1 (37cy) | |
rcall sendbitRNG ; send bit R2 (40cy) | |
rcall sendbitRNG ; send bit R3 (40cy) | |
rcall sendbitRNG ; send bit R4 (40cy) | |
rcall sendbitRNG ; send bit R5 (40cy) | |
rcall sendbitRNG ; send bit R6 (40cy) | |
rcall sendbitRNG ; send bit R7 (40cy) | |
; green byte | |
ld r0, Y+ ; 2cy | |
rcall sendbit38 ; send bit G0 (38cy) | |
rcall sendbitRNG ; send bit G1 (40cy) | |
; initialise X to point at the RNG states table | |
ldi XL, low(rngtable) ; 1cy | |
ldi XH, high(rngtable) ; 1cy | |
rcall sendbitGetNextPixel_f0_38 ; send bit G2 (38cy) | |
rcall sendbitGetNextPixel_f0_40 ; send bit G3 (40cy) | |
rcall sendbitGetNextPixel_f1_40 ; send bit G4 (40cy) | |
rcall sendbitGetNextPixel_f2_40 ; send bit G5 (40cy) | |
rcall sendbitGetNextPixel_f1_40 ; send bit G6 (40cy) | |
rcall sendbitGetNextPixel_f0_40 ; send bit G7 (40cy) | |
ld r0, Y+ ; 2cy | |
rcall sendbitGetNextPixel_f0_38 ; send bit B0 (38cy) | |
rcall sendbit40 ; send bit B1 (40cy) | |
rcall sendbit40 ; send bit B2 (40cy) | |
rcall sendbit40 ; send bit B3 (40cy) | |
rcall sendbit40 ; send bit B4 (40cy) | |
rcall sendbit40 ; send bit B5 (40cy) | |
rcall sendbit40 ; send bit B6 (40cy) | |
sbiw indexH:indexL, 1 ; 2cy | |
breq lastBitOfFrame ; 1cy if more pixels to go | |
rcall sendbit37 ; send bit B7 (37cy) | |
rjmp sendPixel ; 2cy | |
lastBitOfFrame: ; breq took 2cy | |
rcall sendbit36 ; send bit B7 (36cy) | |
; set start states of the last 6 RNGs | |
ldi XL, low(rngtable) | |
ldi XH, high(rngtable) | |
ldi YL, low(rnghistory) | |
ldi YH, high(rnghistory) | |
ldi indexL, $12 ; 0x12 = 18 = 6 * 3 values to copy | |
adiw X, 3 | |
loadHistoryLoop: | |
ld r16, Y+ | |
st X+, r16 | |
dec indexL | |
brne loadHistoryLoop | |
; loop for 200 = 0x00C8 iterations = 800 cycles = 50us | |
ldi indexH, $00 | |
ldi indexL, $C8 | |
resetLoop: | |
sbiw indexH:indexL, 1 ; 2cy | |
brne resetLoop ; 2cy if still to loop | |
sbic PIND, 1 | |
rjmp sendFrame | |
; button is pressed (PORTD1 is clear) - debounce and load next colour table | |
; loop for ~800,000 cycles = 50ms, as 20 loops of 40,000 cycles | |
ldi r16, 20 | |
debounceOuterLoop: | |
; loop for 10,000 = 0x2710 iterations = 40,000 cycles | |
ldi indexH, $27 | |
ldi indexL, $10 | |
debounceInnerLoop: | |
sbiw indexH:indexL, 1 ; 2cy | |
brne debounceInnerLoop ; 2cy if still to loop | |
dec r16 | |
brne debounceOuterLoop | |
debounceWaitForRelease: | |
sbis PIND, 1 | |
rjmp debounceWaitForRelease | |
rcall next_coltable | |
rjmp sendFrame | |
; ---------------------------------------------------------- | |
; Load the next colour table (falls through to init_coltable) | |
next_coltable: | |
inc colTableIndex | |
mov r16, colTableIndex | |
cpi r16, colTableCount | |
brne next_coltable_exit | |
clr colTableIndex | |
next_coltable_exit: | |
; save colTableIndex to EEPROM | |
sbic EECR, EEPE | |
rjmp next_coltable_exit | |
ldi r16, low(lastColTableIndex) | |
out EEARL, r16 | |
ldi r16, high(lastColTableIndex) | |
out EEARH, r16 | |
out EEDR, colTableIndex | |
sbi EECR, EEMPE | |
sbi EECR, EEPE | |
; ---------------------------------------------------------- | |
; Initialise the colour table by copying the colours from | |
; flash. The index of the table to use is in r16. | |
init_coltable: | |
ldi XL, low(coltable) | |
ldi XH, high(coltable) | |
ldi ZL, low(coltable_const<<1) | |
ldi ZH, high(coltable_const<<1) | |
mov r16, colTableIndex | |
coltable_offset_loop: | |
adiw Z, $20 ; | |
adiw Z, $20 ; colour table is 64 bytes | |
dec r16 | |
brpl coltable_offset_loop | |
sbiw Z, $20 ; | |
sbiw Z, $20 ; off-by-one | |
adiw X, $20 | |
adiw X, $20 | |
ldi r16, 0 | |
st -X, r16 | |
ldi indexL, $3f ; 0x3f = 63 more bytes to copy | |
init_coltable_loop: | |
lpm r16, Z+ | |
st -X, r16 | |
dec indexL | |
brne init_coltable_loop | |
ret | |
; ---------------------------------------------------------- | |
; Send the current bit while contributing to the calculation | |
; of the colour of the next pixel from frame 0 | |
sendbitGetNextPixel_f0_40: | |
; {{{ start of 12 cycles of free time | |
nop ; 1cy | |
nop ; 1cy | |
sendbitGetNextPixel_f0_38: | |
ld r16, X ; 2cy | |
adiw X, $03 ; 2cy | |
; set bits in nextPixelColourIndex to indicate next-pixel-colour | |
cpi RNGB0, $ba ; 1cy | |
brne not_colour_1_f0 ; 1cy | |
ori nextPixelColourIndex, $04 ; 1cy | |
not_colour_1_f0: | |
cpi RNGB0, $6e ; 1cy | |
brne not_colour_2_f0 ; 1cy | |
ori nextPixelColourIndex, $08 ; 1cy | |
not_colour_2_f0: | |
; }}} end of 12 cycles of free time | |
; start pulse | |
sbi PORTD, 0 ; 2cy | |
rjmp pulseStarted ; 2cy | |
; ---------------------------------------------------------- | |
; Send the current bit while contributing to the calculation | |
; of the colour of the next pixel from frame 1 | |
sendbitGetNextPixel_f1_40: | |
; {{{ start of 12 cycles of free time | |
nop ; 1cy | |
nop ; 1cy | |
sendbitGetNextPixel_f1_38: | |
ld r16, X ; 2cy | |
adiw X, $03 ; 2cy | |
; set bits in nextPixelColourIndex to indicate next-pixel-colour | |
cpi RNGB0, $ba ; 1cy | |
brne not_colour_1_f1 ; 1cy | |
ori nextPixelColourIndex, $14 ; 1cy | |
not_colour_1_f1: | |
cpi RNGB0, $6e ; 1cy | |
brne not_colour_2_f1 ; 1cy | |
ori nextPixelColourIndex, $18 ; 1cy | |
not_colour_2_f1: | |
; }}} end of 12 cycles of free time | |
; start pulse | |
sbi PORTD, 0 ; 2cy | |
rjmp pulseStarted ; 2cy | |
; ---------------------------------------------------------- | |
; Send the current bit while contributing to the calculation | |
; of the colour of the next pixel from frame 2 | |
sendbitGetNextPixel_f2_40: | |
; {{{ start of 12 cycles of free time | |
nop ; 1cy | |
nop ; 1cy | |
sendbitGetNextPixel_f2_38: | |
ld r16, X ; 2cy | |
adiw X, $03 ; 2cy | |
; set bits in nextPixelColourIndex to indicate next-pixel-colour | |
cpi RNGB0, $ba ; 1cy | |
brne not_colour_1_f2 ; 1cy | |
ori nextPixelColourIndex, $24 ; 1cy | |
not_colour_1_f2: | |
cpi RNGB0, $6e ; 1cy | |
brne not_colour_2_f2 ; 1cy | |
ori nextPixelColourIndex, $28 ; 1cy | |
not_colour_2_f2: | |
; }}} end of 12 cycles of free time | |
; start pulse | |
sbi PORTD, 0 ; 2cy | |
rjmp pulseStarted ; 2cy | |
sendbitRNG: | |
; send one bit using PWM | |
; and also update a 24-bit LFSR RNG while waiting | |
; bit to transmit is r0(0) and is sent on $00(0) | |
.def toTransmit = r0 | |
; RNG uses bytes X, X+1 & X+2 as the current state | |
; clobbers r16, r17, r18 | |
; adds 3 to X to point it at the next LSFR | |
; shifts r0 right by 1 to shift in the next bit | |
; LSB of r16 is random output | |
; X^24 + X^4 + X^3 + X + 1 is primitive | |
; 16 8 2 1 = 0x1b | |
.def RNGB0 = r16 | |
.def RNGB1 = r17 | |
.def RNGB2 = r18 | |
; {{{ start of 12 cycles of free time | |
nop ; 1cy | |
nop ; 1cy | |
nop ; 1cy | |
; load state into r16 (X^7 .. 1), r17 (X^15 .. X^8), r18 (X^23 .. X^16) | |
ld RNGB0, X+ ; 2cy | |
ld RNGB1, X+ ; 2cy | |
ld RNGB2, X ; 2cy | |
; multiply by X | |
clc ; 1cy | |
rol RNGB0 ; 1cy | |
rol RNGB1 ; 1cy | |
; TBC ... | |
; }}} end of 12 cycles of free time | |
; start pulse | |
sbi PORTD, 0 ; 2cy | |
; {{{ start of 5 cycles of free time | |
; ... continue multiplying by X | |
rol RNGB2 ; 1cy | |
; carry bit is now set if overflowed | |
; store the high-order bytes again | |
st X, RNGB2 ; 2cy | |
st -X, RNGB1 ; 2cy | |
; r17 & r18 can be reused | |
.undef RNGB2 | |
.undef RNGB1 | |
; }}} end of 5 cycles of free time | |
; end pulse if sending a 0 (8 cycles) | |
sbrs toTransmit, 7 ; 1cy if r0(7) == 0 (no skip), 2cy if r0(7) == 1 (skip) | |
cbi PORTD, 0 ; 2cy | |
; == 3 cy if sending a 0, 2cy if sending a 1 | |
; {{{ start of 8 cycles of free time | |
; calculate remainder mod X^24 + X^4 + X^3 + X + 1 | |
.def modulus = r18 | |
ldi modulus, $1b ; 1cy | |
brcc nooverflow24 ; 1cy (2 if true, but skips 1cy, so same either way) | |
eor RNGB0, modulus ; 1cy | |
nooverflow24: | |
st -X, RNGB0 ; 2cy | |
.undef modulus | |
; update X to point to the next LFSR state | |
adiw X, 3 ; 2cy | |
nop ; 1cy | |
; }}} end of 8 cycles of free time | |
; end pulse if sending a 1 (19 cycles) | |
lsl toTransmit ; 1cy | |
brcc sendbitRNG_ret ; 1cy if carry set (no skip, sending a 1), 2cy if carry clear (skip, sending a 0) | |
cbi PORTD, 0 ; 2cy | |
; == 3 cy if sending a 1, 2cy if sending a 0 | |
sendbitRNG_ret: | |
ret ; 2cy | |
sendbit40: | |
; send one bit using PWM | |
; taking 40 cycles | |
; bit to transmit is r0(0) and is sent on $00(0) | |
; {{{ start of 12 cycles of free time | |
nop ; 1 | |
sendbit39: | |
nop ; 2 | |
sendbit38: | |
nop ; 3 | |
sendbit37: | |
nop ; 4 | |
sendbit36: | |
nop ; 5 | |
sendbit35: | |
nop ; 6 | |
sendbit34: | |
nop ; 7 | |
sendbit33: | |
nop ; 8 | |
sendbit32: | |
nop ; 9 | |
sendbit31: | |
nop ; 10 | |
sendbit30: | |
nop ; 11 | |
sendbit29: | |
nop ; 12 | |
sendbit28: | |
; }}} end of 12 cycles of free time | |
; start pulse | |
sbi PORTD, 0 ; 2cy | |
; {{{ start of 5 cycles of free time | |
nop ; 1 | |
nop ; 2 | |
pulseStarted: | |
nop ; 3 | |
nop ; 4 | |
nop ; 5 | |
; }}} end of 5 cycles of free time | |
; end pulse if sending a 0 (8 cycles) | |
sbrs r0, 7 ; 1cy if r0(7) == 0 (no skip), 2cy if r0(7) == 1 (skip) | |
cbi PORTD, 0 ; 2cy | |
; == 3 cy if sending a 0, 2cy if sending a 1 | |
; {{{ start of 8 cycles of free time | |
nop ; 1 | |
nop ; 2 | |
nop ; 3 | |
nop ; 4 | |
nop ; 5 | |
nop ; 6 | |
nop ; 7 | |
nop ; 8 | |
; }}} end of 8 cycles of free time | |
; end pulse if sending a 1 (19 cycles) | |
lsl toTransmit ; 1cy | |
brcc sendbit_ret ; 1cy if carry set (no skip, sending a 1), 2cy if carry clear (skip, sending a 0) | |
cbi PORTD, 0 ; 2cy | |
; == 3 cy if sending a 1, 2cy if sending a 0 | |
sendbit_ret: | |
ret ; 2cy | |
coltable_const: | |
; table of colours - RGB0 values where nextPixelColourIndex is the offset into this array | |
; rrggbb | |
; frame 3, colours 3,2,1,0 (0 is not used) | |
; (frame 3 only occurs when frames 1 & 2 clash) | |
; then frame 2, colours 3,2,1,0 (0 is not used) | |
; then frame 1, colours 3,2,1,0 (0 is not used) | |
; then frame 0, colours 3,2,1,0 (0 is the background colour) | |
.dd $ff9000, $ff9000, $ff0000, $000000, $ff9000, $ff9000, $ff0000, $000000, $6c3a00, $6c3a00, $6c0000, $000000, $271100, $271100, $270000, $000000 \ | |
, $ff00ff, $ff00ff, $00ff00, $000000, $ff00ff, $ff00ff, $00ff00, $000000, $6c006c, $6c006c, $006c00, $000000, $270027, $270027, $002700, $000000 \ | |
, $ffffff, $ffffff, $ffffff, $000000, $ffffff, $ffffff, $ffffff, $000000, $6c6c6c, $6c6c6c, $6c6c6c, $000000, $272727, $272727, $272727, $000000 \ | |
, $ff9000, $ff9000, $ff0000, $000000, $ff9000, $ff9000, $ff0000, $000000, $704216, $704216, $701d16, $000000, $342618, $342618, $342018, $202018 \ | |
, $ff00ff, $ff00ff, $00ff00, $000000, $ff00ff, $ff00ff, $00ff00, $000000, $701d6f, $701d6f, $1d7016, $000000, $34202f, $34202f, $203418, $202018 \ | |
, $ffffff, $ffffff, $ffffff, $000000, $ffffff, $ffffff, $ffffff, $000000, $70706f, $70706f, $70706f, $000000, $34342f, $34342f, $34342f, $202018 \ | |
, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000, $000000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment