X-Git-Url: https://git.gir.st/Chiptunes.git/blobdiff_plain/f6ef1520594c76acd92c8a6048d0afb1672737a6..ea40b11f590e27e0fec3b3435007eb2177d85dc1:/foo.S diff --git a/foo.S b/foo.S index d02b3de..55718df 100644 --- a/foo.S +++ b/foo.S @@ -24,11 +24,13 @@ /* I/O REGISTERS */ OCR0AL = 0x26 DDRB = 0x01 +PORTB = 0x02 PUEB = 0x03 SPL = 0x3D SPH = 0x3E CCP = 0x3C CLKPSR = 0x36 +WDTCSR = 0x31 SMCR = 0x3A TCCR0A = 0x2E TCCR0B = 0x2D @@ -36,48 +38,55 @@ TIMSK0 = 0x2B TIFR0 = 0x2A .section .data +data: .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58 .section .text +.org 0x0000 ; RESET interrupt + RJMP main +.org 0x0008 ; TIM0_OVF interrupt + RJMP sample mod3: ; mod3(Mh.Ml) -> t #define tmp _ - ADD (Ml, Mh) - CLR (Mh) - ADC (Mh, zero, carry) //Mh only holds the carry bit - MOV (tmp, Ml) - SWAP (tmp) - ANDI (tmp, 0x0f) - SWAP (Mh) - OR (tmp, Mh) - ANDI (Ml, 0x0f) - ADD (Ml, tmp) - MOV (tmp, Ml) - LSR (tmp) - LSR (tmp) - ANDI (Ml, 0x03) - ADD (Ml, tmp) - MOV (tmp, Ml) - LSR (tmp) - LSR (tmp) - ANDI (Ml, 0x03) - ADD (Ml, tmp) - CPI (Ml, 3) - BRPL (skip) - SUBI (Ml, 3) - skip:; + ADD Ml, Mh + CLR Mh + ADC Mh, zero + MOV tmp, Ml + SWAP tmp + ANDI tmp, 0x0f + SWAP Mh + OR tmp, Mh + ANDI Ml, 0x0f + ADD Ml, tmp + MOV tmp, Ml + LSR tmp + LSR tmp + ANDI Ml, 0x03 + ADD Ml, tmp + MOV tmp, Ml + LSR tmp + LSR tmp + ANDI Ml, 0x03 + ADD Ml, tmp + CPI Ml, 3 + BRPL skip + SUBI Ml, 3 + skip: RET #undef tmp ; definitions to mul-tree readable: +#define a1 x +#define a2 _ .macro always _bit ; nop; for when a test() is not necessary (see tree) .endm .macro never _bit ; nop; for when a test() is not necessary (see tree) .endm .macro test _bit,_jmpto - SBRC t, _bit - RJMP _jmpto + SBRC t, \_bit + RJMP \_jmpto .endm .macro shift16 LSR a2 @@ -88,45 +97,41 @@ mod3: ; mod3(Mh.Ml) -> t .endm .macro shift0 ; nop; last shift is common .endm -.macro add_shift16 +.macro add16 ADD a1, i0 ADC a2, i1 - shift16 -.endm -.macro add_shift8 ; ditto with carrying - ADD a1, i0 - shift8 .endm -.macro add_shift0 ; last shift is common +.macro add8 ; ditto with carrying ADD a1, i0 .endm +#undef a2 +#undef a1 g: ; g(i, t) -> t #define tmp _ - ANDI (t, 0x07) - MOV (tmp, i2) - ANDI (tmp, 3) - TST (tmp) - CPSE (tmp, zero) - SUBI (t, -8) + ANDI t, 0x07 + MOV tmp, i2 + ANDI tmp, 3 + CPSE tmp, zero + SUBI t, -8 #undef tmp + ;TODO: check correctness! #define tmp _ - MOV (tmp, t) //NOTE: must move value away from `t`, as that is also hi(X) - tmp = data[tmp];/* - LDI Xhi, hi8(data) + MOV tmp, t ; NOTE: must move value away from `t`, as that is also hi(X) + LDI Xhi, hi8(data) ; hi(data) always zero, but still need to clear the register LDI Xlo, lo8(data) ADD Xlo, tmp ;<-- the offset (formerly `t`) into data[] - ADC Xhi, zero - LD tmp, X */ - MOV (t, tmp) + ;ADC Xhi, zero ; data == 0x40 t <= 0x10, so can never overflow + LD tmp, X + MOV t, tmp #undef tmp #define a1 x #define a2 _ #define a0 t - CLR (a2) - CLR (a1) + CLR a2 + CLR a1 /* decision tree multiplication saves cycles and (hopefully) reduces code size _xxx? @@ -142,260 +147,297 @@ g: ; g(i, t) -> t ... ... ... ... ... ... ... | | | | | | | B0 58 84 8C 69 75 9D */ - test (0, m____1) + test 0, m____1 m____0: shift16 - never (1) + never 1 m___00: shift16 - test (2, m__100) + test 2, m__100 m__000: shift16 - test (3, m_1000) + test 3, m_1000 m_0000: shift16 - always (4) - add_shift16 - always (5) - add_shift8 - never (6) + always 4 + add16 $ shift16 + always 5 + add8 $ shift8 + never 6 shift8 - always (7) - add_shift0 - RJMP (end_mul) // calc'd 0xb0 + always 7 + add8 $ shift0 + RJMP end_mul ; calc'd 0xb0 - m_1000: add_shift16 - always (4) - add_shift16 - never (5) + m_1000: add16 $ shift16 + always 4 + add16 $ shift16 + never 5 shift8 - always (6) - add_shift8 - never (7) + always 6 + add8 $ shift8 + never 7 shift0 - RJMP (end_mul) // calc'd 0x58 + RJMP end_mul ; calc'd 0x58 - m__100: add_shift16 - test (3, m_1100) + m__100: add16 $ shift16 + test 3, m_1100 m_0100: shift16 - RJMP (upper_8) //'ll calc 0x84 - - m_1100: add_shift16 - upper_8: /* used twice, so deduplicated */ - never (4) + RJMP upper_8 ;'ll calc 0x84 +;TODO: combine shift16 above with add_shift16 below to save progmem + m_1100: add16 $ shift16 + upper_8: ; used twice, so deduplicated + never 4 shift16 - never (5) + never 5 shift8 - never (6) + never 6 shift8 - always (7) - add_shift0 - RJMP (end_mul) // calc'd 0x8c + always 7 + add8 $ shift0 + RJMP end_mul ; calc'd 0x8c - m____1: add_shift16 - never (1) + m____1: add16 $ shift16 + never 1 m___01: shift16 - test (2, m__101) + test 2, m__101 m__001: shift16 - always (3) - m_1001: add_shift16 - never (4) + always 3 + m_1001: add16 $ shift16 + never 4 shift16 - always (5) - add_shift8 - always (6) - add_shift8 - never (7) + always 5 + add8 $ shift8 + always 6 + add8 $ shift8 + never 7 shift0 - RJMP (end_mul) // calc'd 0x69 + RJMP end_mul ; calc'd 0x69 - m__101: add_shift16 - test (3, m_1101) + m__101: add16 $ shift16 + test 3, m_1101 m_0101: shift16 - always (4) - add_shift16 - always (5) - add_shift8 - always (6) - add_shift8 - never (7) + always 4 + add16 $ shift16 + always 5 + add8 $ shift8 + always 6 + add8 $ shift8 + never 7 shift0 - RJMP (end_mul) // calc'd 0x75 + RJMP end_mul ; calc'd 0x75 - m_1101: add_shift16 - always (4) - add_shift16 - never (5) + m_1101: add16 $ shift16 + always 4 + add16 $ shift16 + never 5 shift8 - never (6) + never 6 shift8 - always (7) - add_shift0 - // calc'd 0x9d + always 7 + add8 $ shift0 + ; calc'd 0x9d end_mul: - LSR (a1) //final shift is a common operation for all + LSR a1 ;final shift is a common operation for all - MOV (t, a1) //TODO: use a1 in main() directly + MOV t, a1 ;;TODO: use a1 in main() directly #undef a0 #undef a1 #undef a2 - RET //TODO: replace CALL/RET with IJMP? (requires undoing goto-mul-hack) + RET ; TODO: replace CALL/RET with IJMP? + +main: ; setup routine + CLR zero + CLR i0 + CLR i1 + CLR i2 + CLR i3 + CLR acc ; we output a dummy sample before the actual first one + + #define one _ + LDI one, 1 + LDI x, 0x5f ; RAMEND + OUT SPL, x ; init stack ptr + OUT SPH, zero ; -"- + OUT PUEB, zero ; disable pullups + LDI x, 0x05 + OUT DDRB, x ; PORTB0:pwm, PORTB2:debug + LDI x, 0xd8 + OUT CCP, x ; change protected ioregs + OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz) + OUT WDTCSR, zero; turn off watchdog ;;TODO: incomplete - see datasheet pg48 + ; OUT SMCR, 2 ; sleep mode 'power down' ('idle' (default) has faster response time) -main: - CLR (zero) - CLR (i0) - CLR (i1) - CLR (i2) - CLR (i3) - //TODO: setup stack pointer, portb, clock, sleep mode, timer0 + ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler + LDI x, 0x81 + OUT TCCR0A, x + LDI x, 0x09 + OUT TCCR0B, x + OUT TIMSK0, one ; enable tim0_ovf + OUT TIFR0, one ; TODO: why? + SEI + #undef one RJMP sample -sample: //TODO: this will probably become the timer0 overflow interrupt handler - MOV (n, i2) - LSL (n) - LSL (n) + +loop: + SLEEP ; wait for interrupt + RJMP loop + +sample: + ; potential TODO: softcounter in r25 to only update duty cicle every n iterations + ; potential TODO: save/restore status register (SREG=0x3f) (only if something in mainloop) + + OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime + SBI PORTB, 2 ; to measure runtime + + MOV n, i2 + LSL n + LSL n #define tmp _ - MOV (tmp, i1) - SWAP (tmp) - ANDI (tmp, 0x0f) - LSR (tmp) - LSR (tmp) - OR (n, tmp) + MOV tmp, i1 + SWAP tmp + ANDI tmp, 0x0f + LSR tmp + LSR tmp + OR n, tmp #undef tmp - MOV (s, i3) - LSR (s) - ROR (s) - ANDI (s, 0x80) + MOV s, i3 + LSR s + ROR s + ANDI s, 0x80 #define tmp _ - MOV (tmp, i2) - LSR (tmp) - OR (s, tmp) + MOV tmp, i2 + LSR tmp + OR s, tmp #undef tmp - //voice 1: - MOV (t, n) - RCALL g(); - SWAP (t) - ANDI (t, 1) - MOV (acc, t) + ; voice 1: + MOV t, n + RCALL g + SWAP t + ANDI t, 1 + MOV acc, t - //voice 2: + ; voice 2: #define tmp _ - MOV (tmp, i2) - LSL (tmp) - LSL (tmp) - LSL (tmp) - MOV (t, i1) - SWAP (t) - ANDI (t, 0xf) - LSR (t) - OR (t, tmp) + MOV tmp, i2 + LSL tmp + LSL tmp + LSL tmp + MOV t, i1 + SWAP t + ANDI t, 0xf + LSR t + OR t, tmp #undef tmp - EOR (t, n) - RCALL g(); - LSR (t) - LSR (t) - ANDI (t, 3) - AND (t, s) - ADD (acc, t) + EOR t, n + RCALL g + LSR t + LSR t + ANDI t, 3 + AND t, s + ADD acc, t - //voice 3: - MOV (Ml, i2) - SWAP (Ml) - ANDI (Ml, 0xf0) - LSL (Ml) + ; voice 3: + MOV Ml, i2 + SWAP Ml + ANDI Ml, 0xf0 + LSL Ml #define tmp _ - MOV (tmp, i1) - LSR (tmp) - LSR (tmp) - LSR (tmp) - OR (Ml, tmp) + MOV tmp, i1 + LSR tmp + LSR tmp + LSR tmp + OR Ml, tmp #undef tmp - MOV (Mh, i3) - SWAP (Mh) - ANDI (Mh, 0xf0) - LSL (Mh) + MOV Mh, i3 + SWAP Mh + ANDI Mh, 0xf0 + LSL Mh #define tmp _ - MOV (tmp, i2) - LSR (tmp) - LSR (tmp) - LSR (tmp) - OR (Mh, tmp) + MOV tmp, i2 + LSR tmp + LSR tmp + LSR tmp + OR Mh, tmp #undef tmp - RCALL mod3(); - ADD (t, n) - RCALL g(); - LSR (t) - LSR (t) - ANDI (t, 3) - MOV (x, s) - INC (x) + RCALL mod3 + ADD t, n + RCALL g + LSR t + LSR t + ANDI t, 3 + MOV x, s + INC x #define tmp _ - MOV (tmp, x) - LSR (tmp) - LSR (tmp) - ADD (tmp, x) - ROR (tmp) - LSR (tmp) - ADD (tmp, x) - ROR (tmp) - LSR (tmp) - ADD (tmp, x) - ROR (tmp) - LSR (tmp) - AND (t, tmp) + MOV tmp, x + LSR tmp + LSR tmp + ADD tmp, x + ROR tmp + LSR tmp + ADD tmp, x + ROR tmp + LSR tmp + ADD tmp, x + ROR tmp + LSR tmp + AND t, tmp #undef tmp - ADD (acc, t) + ADD acc, t - //voice 4: - MOV (Ml, i2) - SWAP (Ml) - ANDI (Ml, 0xf0) - LSL (Ml) - LSL (Ml) + ; voice 4: + MOV Ml, i2 + SWAP Ml + ANDI Ml, 0xf0 + LSL Ml + LSL Ml #define tmp _ - MOV (tmp, i1) - LSR (tmp) - LSR (tmp) - OR (Ml, tmp) + MOV tmp, i1 + LSR tmp + LSR tmp + OR Ml, tmp #undef tmp - MOV (Mh, i3) - SWAP (Mh) - ANDI (Mh, 0xf0) - LSL (Mh) - LSL (Mh) + MOV Mh, i3 + SWAP Mh + ANDI Mh, 0xf0 + LSL Mh + LSL Mh #define tmp _ - MOV (tmp, i2) - LSR (tmp) - LSR (tmp) - OR (Mh, tmp) + MOV tmp, i2 + LSR tmp + LSR tmp + OR Mh, tmp #undef tmp - RCALL mod3(); - SUB (t, n) - NEG (t) - SUBI (t, -8) - RCALL g(); - LSR (t) - ANDI (t, 3) - INC (s) + RCALL mod3 + SUB t, n + NEG t + SUBI t, -8 + RCALL g + LSR t + ANDI t, 3 + INC s #define tmp _ - MOV (tmp, s) - LSR (tmp) - ADD (tmp, s) - ROR (tmp) - LSR (tmp) - LSR (tmp) - ADD (tmp, s) - ROR (tmp) - ADD (tmp, s) - ROR (tmp) - LSR (tmp) - LSR (tmp) - AND (t, tmp) + MOV tmp, s + LSR tmp + ADD tmp, s + ROR tmp + LSR tmp + LSR tmp + ADD tmp, s + ROR tmp + ADD tmp, s + ROR tmp + LSR tmp + LSR tmp + AND t, tmp #undef tmp - ADD (acc, t) + ADD acc, t + + SWAP acc ; acc<<4, to be passed to OCR0AL - SWAP acc - OUT OCR0AL, acc SUBI i0, -1 SBCI i1, -1 SBCI i2, -1 SBCI i3, -1 - rjmp sample //TODO: -> RETI + CBI PORTB, 2 ; end runtime measurement + ;TODO: to reduce jitter: clear pending tim0_ovf (TIFR0[TOV0] <- 1) ? + RETI ; reenables interrupts