Skip to content

Instantly share code, notes, and snippets.

@DaveCTurner
Created April 28, 2018 17:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaveCTurner/3fdf2483244db91a511ede03cd772b7e to your computer and use it in GitHub Desktop.
Save DaveCTurner/3fdf2483244db91a511ede03cd772b7e to your computer and use it in GitHub Desktop.
;
; 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