diff --git a/src/main/kc/stdlib/c64.kc b/src/main/kc/stdlib/c64.kc index dcc3a6481..b0f11bd7b 100644 --- a/src/main/kc/stdlib/c64.kc +++ b/src/main/kc/stdlib/c64.kc @@ -179,10 +179,16 @@ const byte CIA_TIMER_CONTROL_B_TOD_ALARM_SET = 0b10000000; // The vector used when the KERNAL serves IRQ interrupts const void()** KERNEL_IRQ = $0314; +// The vector used when the KERNAL serves NMI interrupts +const void()** KERNEL_NMI = $0318; // The vector used when the HARDWARE serves IRQ interrupts const void()** HARDWARE_IRQ = $fffe; +// The SID volume +const byte* SID_VOLUME = $d418; + + // The colors of the C64 const byte BLACK = $0; const byte WHITE = $1; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 5b0f01f87..16864a518 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -36,6 +36,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testNmiSamples() throws IOException, URISyntaxException { + compileAndCompare("examples/nmisamples/nmisamples"); + } + @Test public void testEncodingLiteralChar() throws IOException, URISyntaxException { compileAndCompare("encoding-literal-char"); diff --git a/src/test/kc/examples/nmisamples/moments_sample.bin b/src/test/kc/examples/nmisamples/moments_sample.bin new file mode 100644 index 000000000..844a50525 Binary files /dev/null and b/src/test/kc/examples/nmisamples/moments_sample.bin differ diff --git a/src/test/kc/examples/nmisamples/nmisamples.kc b/src/test/kc/examples/nmisamples/nmisamples.kc new file mode 100644 index 000000000..5d680450d --- /dev/null +++ b/src/test/kc/examples/nmisamples/nmisamples.kc @@ -0,0 +1,53 @@ +// NMI Sample Player using the SID volume register +// Code by Scan of Desire (Richard-William Loerakker) +// Sample from ART OF NOISE: MOMENTS IN LOVE + +import "c64" + +const unsigned int SAMPLE_SIZE = 0x6100; +char[SAMPLE_SIZE] SAMPLE = kickasm(resource "moments_sample.bin") {{ .import binary "moments_sample.bin" }}; + +volatile char* sample = SAMPLE; + +void main() { + // Boosting 8580 Digis + // See https://gist.github.com/munshkr/30f35e39905e63876ff7 (line 909) + asm { + lda #$ff + sta $d406 + sta $d40d + sta $d414 + + lda #$49 + sta $d404 + sta $d40b + sta $d412 + } + asm { sei } + *CIA2_INTERRUPT = CIA_INTERRUPT_CLEAR; + *KERNEL_NMI = &nmi; + *CIA2_TIMER_A = 0x88; // speed + *CIA2_INTERRUPT = 0x81; + *CIA2_TIMER_A_CONTROL = 0x01; + asm { cli } +} + +interrupt(hardware_all) void nmi() { + (*BORDERCOL)++; + asm { lda CIA2_INTERRUPT } + *SID_VOLUME = *sample & $0f; + *KERNEL_NMI = &nmi2; + (*BORDERCOL)--; +} + +interrupt(hardware_all) void nmi2() { + (*BORDERCOL)++; + asm { lda CIA2_INTERRUPT } + *SID_VOLUME = *sample >> 4; + sample++; + if (>sample == >(SAMPLE+$6100)) { + sample = SAMPLE; + } + *KERNEL_NMI = &nmi; + (*BORDERCOL)--; +} \ No newline at end of file diff --git a/src/test/ref/examples/nmisamples/nmisamples.asm b/src/test/ref/examples/nmisamples/nmisamples.asm new file mode 100644 index 000000000..66536e907 --- /dev/null +++ b/src/test/ref/examples/nmisamples/nmisamples.asm @@ -0,0 +1,121 @@ +// NMI Sample Player using the SID volume register +// Code by Scan of Desire (Richard-William Loerakker) +// Sample from ART OF NOISE: MOMENTS IN LOVE +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + .label BORDERCOL = $d020 + // CIA #2 Timer A Value (16-bit) + .label CIA2_TIMER_A = $dd04 + // CIA #2 Interrupt Status & Control Register + .label CIA2_INTERRUPT = $dd0d + // CIA #2 Timer A Control Register + .label CIA2_TIMER_A_CONTROL = $dd0e + // Value that disables all CIA interrupts when stored to the CIA Interrupt registers + .const CIA_INTERRUPT_CLEAR = $7f + // The vector used when the KERNAL serves NMI interrupts + .label KERNEL_NMI = $318 + // The SID volume + .label SID_VOLUME = $d418 + .const SAMPLE_SIZE = $6100 + .label sample = 2 +bbegin: + lda #SAMPLE + sta.z sample+1 + jsr main + rts +main: { + // Boosting 8580 Digis + // See https://gist.github.com/munshkr/30f35e39905e63876ff7 (line 909) + lda #$ff + sta $d406 + sta $d40d + sta $d414 + lda #$49 + sta $d404 + sta $d40b + sta $d412 + sei + lda #CIA_INTERRUPT_CLEAR + sta CIA2_INTERRUPT + lda #nmi + sta KERNEL_NMI+1 + lda #0 + sta CIA2_TIMER_A+1 + lda #<$88 + sta CIA2_TIMER_A + // speed + lda #$81 + sta CIA2_INTERRUPT + lda #1 + sta CIA2_TIMER_A_CONTROL + cli + rts +} +nmi2: { + sta rega+1 + stx regx+1 + sty regy+1 + inc BORDERCOL + lda CIA2_INTERRUPT + ldy #0 + lda (sample),y + lsr + lsr + lsr + lsr + sta SID_VOLUME + inc.z sample + bne !+ + inc.z sample+1 + !: + lda.z sample+1 + cmp #>SAMPLE+$6100 + bne b1 + lda #SAMPLE + sta.z sample+1 + b1: + lda #nmi + sta KERNEL_NMI+1 + dec BORDERCOL + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} +nmi: { + sta rega+1 + stx regx+1 + sty regy+1 + inc BORDERCOL + lda CIA2_INTERRUPT + lda #$f + ldy #0 + and (sample),y + sta SID_VOLUME + lda #nmi2 + sta KERNEL_NMI+1 + dec BORDERCOL + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} +SAMPLE: +.import binary "moments_sample.bin" diff --git a/src/test/ref/examples/nmisamples/nmisamples.cfg b/src/test/ref/examples/nmisamples/nmisamples.cfg new file mode 100644 index 000000000..38882fa43 --- /dev/null +++ b/src/test/ref/examples/nmisamples/nmisamples.cfg @@ -0,0 +1,56 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] (byte*) sample#0 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 + to:@2 +@2: scope:[] from @1 + [2] phi() + [3] call main + to:@end +@end: scope:[] from @2 + [4] phi() +main: scope:[main] from @2 + asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } + asm { sei } + [7] *((const byte*) CIA2_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 + [8] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() + [9] *((const word*) CIA2_TIMER_A#0) ← (byte) $88 + [10] *((const byte*) CIA2_INTERRUPT#0) ← (byte) $81 + [11] *((const byte*) CIA2_TIMER_A_CONTROL#0) ← (byte) 1 + asm { cli } + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return +nmi2: scope:[nmi2] from + [14] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) + asm { ldaCIA2_INTERRUPT } + [16] (byte~) nmi2::$0 ← *((byte*) sample#0) >> (byte) 4 + [17] *((const byte*) SID_VOLUME#0) ← (byte~) nmi2::$0 + [18] (byte*) sample#1 ← ++ (byte*) sample#0 + [19] (byte~) nmi2::$1 ← > (byte*) sample#1 + [20] if((byte~) nmi2::$1!=>(const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100) goto nmi2::@1 + to:nmi2::@2 +nmi2::@2: scope:[nmi2] from nmi2 + [21] (byte*) sample#2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 + to:nmi2::@1 +nmi2::@1: scope:[nmi2] from nmi2 nmi2::@2 + [22] (byte*) sample#3 ← phi( nmi2/(byte*) sample#1 nmi2::@2/(byte*) sample#2 ) + [23] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() + [24] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) + to:nmi2::@return +nmi2::@return: scope:[nmi2] from nmi2::@1 + [25] return + to:@return +nmi: scope:[nmi] from + [26] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) + asm { ldaCIA2_INTERRUPT } + [28] (byte~) nmi::$0 ← *((byte*) sample#0) & (byte) $f + [29] *((const byte*) SID_VOLUME#0) ← (byte~) nmi::$0 + [30] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi2() + [31] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) + to:nmi::@return +nmi::@return: scope:[nmi] from nmi + [32] return + to:@return diff --git a/src/test/ref/examples/nmisamples/nmisamples.log b/src/test/ref/examples/nmisamples/nmisamples.log new file mode 100644 index 000000000..96b0ba1ae --- /dev/null +++ b/src/test/ref/examples/nmisamples/nmisamples.log @@ -0,0 +1,1050 @@ +Resolved forward reference nmi to interrupt(HARDWARE_ALL)(void()) nmi() +Resolved forward reference nmi2 to interrupt(HARDWARE_ALL)(void()) nmi2() +Inlined call (byte~) vicSelectGfxBank::$0 ← call toDd00 (byte*) vicSelectGfxBank::gfx +Culled Empty Block (label) @1 +Culled Empty Block (label) @2 +Culled Empty Block (label) @3 +Culled Empty Block (label) @5 +Culled Empty Block (label) @6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) BORDERCOL#0 ← ((byte*)) (number) $d020 + (word*) CIA2_TIMER_A#0 ← ((word*)) (number) $dd04 + (byte*) CIA2_INTERRUPT#0 ← ((byte*)) (number) $dd0d + (byte*) CIA2_TIMER_A_CONTROL#0 ← ((byte*)) (number) $dd0e + (byte) CIA_INTERRUPT_CLEAR#0 ← (number) $7f + (void()**) KERNEL_NMI#0 ← ((void()**)) (number) $318 + (byte*) SID_VOLUME#0 ← ((byte*)) (number) $d418 + to:@4 +@4: scope:[] from @begin + (word) SAMPLE_SIZE#0 ← (number) $6100 + (byte[SAMPLE_SIZE#0]) SAMPLE#0 ← kickasm {{ .import binary "moments_sample.bin" }} + (byte*) sample#0 ← (byte[SAMPLE_SIZE#0]) SAMPLE#0 + to:@7 +main: scope:[main] from @7 + asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } + asm { sei } + *((byte*) CIA2_INTERRUPT#0) ← (byte) CIA_INTERRUPT_CLEAR#0 + (void()*~) main::$0 ← & interrupt(HARDWARE_ALL)(void()) nmi() + *((void()**) KERNEL_NMI#0) ← (void()*~) main::$0 + *((word*) CIA2_TIMER_A#0) ← (number) $88 + *((byte*) CIA2_INTERRUPT#0) ← (number) $81 + *((byte*) CIA2_TIMER_A_CONTROL#0) ← (number) 1 + asm { cli } + to:main::@return +main::@return: scope:[main] from main + return + to:@return +nmi: scope:[nmi] from + (byte*) sample#4 ← phi( @7/(byte*) sample#7 ) + *((byte*) BORDERCOL#0) ← ++ *((byte*) BORDERCOL#0) + asm { ldaCIA2_INTERRUPT } + (number~) nmi::$0 ← *((byte*) sample#4) & (number) $f + *((byte*) SID_VOLUME#0) ← (number~) nmi::$0 + (void()*~) nmi::$1 ← & interrupt(HARDWARE_ALL)(void()) nmi2() + *((void()**) KERNEL_NMI#0) ← (void()*~) nmi::$1 + *((byte*) BORDERCOL#0) ← -- *((byte*) BORDERCOL#0) + to:nmi::@return +nmi::@return: scope:[nmi] from nmi + return + to:@return +nmi2: scope:[nmi2] from + (byte*) sample#5 ← phi( @7/(byte*) sample#7 ) + *((byte*) BORDERCOL#0) ← ++ *((byte*) BORDERCOL#0) + asm { ldaCIA2_INTERRUPT } + (byte~) nmi2::$0 ← *((byte*) sample#5) >> (number) 4 + *((byte*) SID_VOLUME#0) ← (byte~) nmi2::$0 + (byte*) sample#1 ← ++ (byte*) sample#5 + (byte~) nmi2::$1 ← > (byte*) sample#1 + (byte*~) nmi2::$2 ← (byte[SAMPLE_SIZE#0]) SAMPLE#0 + (number) $6100 + (byte~) nmi2::$3 ← > (byte*~) nmi2::$2 + (bool~) nmi2::$4 ← (byte~) nmi2::$1 == (byte~) nmi2::$3 + (bool~) nmi2::$5 ← ! (bool~) nmi2::$4 + if((bool~) nmi2::$5) goto nmi2::@1 + to:nmi2::@2 +nmi2::@1: scope:[nmi2] from nmi2 nmi2::@2 + (byte*) sample#8 ← phi( nmi2/(byte*) sample#1 nmi2::@2/(byte*) sample#2 ) + (void()*~) nmi2::$6 ← & interrupt(HARDWARE_ALL)(void()) nmi() + *((void()**) KERNEL_NMI#0) ← (void()*~) nmi2::$6 + *((byte*) BORDERCOL#0) ← -- *((byte*) BORDERCOL#0) + to:nmi2::@return +nmi2::@2: scope:[nmi2] from nmi2 + (byte*) sample#2 ← (byte[SAMPLE_SIZE#0]) SAMPLE#0 + to:nmi2::@1 +nmi2::@return: scope:[nmi2] from nmi2::@1 + (byte*) sample#6 ← phi( nmi2::@1/(byte*) sample#8 ) + (byte*) sample#3 ← (byte*) sample#6 + return + to:@return +@7: scope:[] from @4 + (byte*) sample#7 ← phi( @4/(byte*) sample#0 ) + call main + to:@8 +@8: scope:[] from @7 + to:@end +@end: scope:[] from @8 + +SYMBOL TABLE SSA +(label) @4 +(label) @7 +(label) @8 +(label) @begin +(label) @end +(byte*) BORDERCOL +(byte*) BORDERCOL#0 +(byte*) CIA2_INTERRUPT +(byte*) CIA2_INTERRUPT#0 +(word*) CIA2_TIMER_A +(word*) CIA2_TIMER_A#0 +(byte*) CIA2_TIMER_A_CONTROL +(byte*) CIA2_TIMER_A_CONTROL#0 +(byte) CIA_INTERRUPT_CLEAR +(byte) CIA_INTERRUPT_CLEAR#0 +(void()**) KERNEL_NMI +(void()**) KERNEL_NMI#0 +(byte[SAMPLE_SIZE#0]) SAMPLE +(byte[SAMPLE_SIZE#0]) SAMPLE#0 +(word) SAMPLE_SIZE +(word) SAMPLE_SIZE#0 +(byte*) SID_VOLUME +(byte*) SID_VOLUME#0 +(void()) main() +(void()*~) main::$0 +(label) main::@return +interrupt(HARDWARE_ALL)(void()) nmi() +(number~) nmi::$0 +(void()*~) nmi::$1 +(label) nmi::@return +interrupt(HARDWARE_ALL)(void()) nmi2() +(byte~) nmi2::$0 +(byte~) nmi2::$1 +(byte*~) nmi2::$2 +(byte~) nmi2::$3 +(bool~) nmi2::$4 +(bool~) nmi2::$5 +(void()*~) nmi2::$6 +(label) nmi2::@1 +(label) nmi2::@2 +(label) nmi2::@return +(byte*) sample +(byte*) sample#0 +(byte*) sample#1 +(byte*) sample#2 +(byte*) sample#3 +(byte*) sample#4 +(byte*) sample#5 +(byte*) sample#6 +(byte*) sample#7 +(byte*) sample#8 + +Adding number conversion cast (unumber) $7f in (byte) CIA_INTERRUPT_CLEAR#0 ← (number) $7f +Adding number conversion cast (unumber) $6100 in (word) SAMPLE_SIZE#0 ← (number) $6100 +Adding number conversion cast (unumber) $88 in *((word*) CIA2_TIMER_A#0) ← (number) $88 +Adding number conversion cast (unumber) $81 in *((byte*) CIA2_INTERRUPT#0) ← (number) $81 +Adding number conversion cast (unumber) 1 in *((byte*) CIA2_TIMER_A_CONTROL#0) ← (number) 1 +Adding number conversion cast (unumber) $f in (number~) nmi::$0 ← *((byte*) sample#4) & (number) $f +Adding number conversion cast (unumber) nmi::$0 in (number~) nmi::$0 ← *((byte*) sample#4) & (unumber)(number) $f +Adding number conversion cast (unumber) 4 in (byte~) nmi2::$0 ← *((byte*) sample#5) >> (number) 4 +Adding number conversion cast (unumber) $6100 in (byte*~) nmi2::$2 ← (byte[SAMPLE_SIZE#0]) SAMPLE#0 + (number) $6100 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) BORDERCOL#0 ← (byte*)(number) $d020 +Inlining cast (word*) CIA2_TIMER_A#0 ← (word*)(number) $dd04 +Inlining cast (byte*) CIA2_INTERRUPT#0 ← (byte*)(number) $dd0d +Inlining cast (byte*) CIA2_TIMER_A_CONTROL#0 ← (byte*)(number) $dd0e +Inlining cast (byte) CIA_INTERRUPT_CLEAR#0 ← (unumber)(number) $7f +Inlining cast (void()**) KERNEL_NMI#0 ← (void()**)(number) $318 +Inlining cast (byte*) SID_VOLUME#0 ← (byte*)(number) $d418 +Inlining cast (word) SAMPLE_SIZE#0 ← (unumber)(number) $6100 +Inlining cast *((word*) CIA2_TIMER_A#0) ← (unumber)(number) $88 +Inlining cast *((byte*) CIA2_INTERRUPT#0) ← (unumber)(number) $81 +Inlining cast *((byte*) CIA2_TIMER_A_CONTROL#0) ← (unumber)(number) 1 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 53280 +Simplifying constant pointer cast (word*) 56580 +Simplifying constant pointer cast (byte*) 56589 +Simplifying constant pointer cast (byte*) 56590 +Simplifying constant integer cast $7f +Simplifying constant pointer cast (void()**) 792 +Simplifying constant pointer cast (byte*) 54296 +Simplifying constant integer cast $6100 +Simplifying constant integer cast $88 +Simplifying constant integer cast $81 +Simplifying constant integer cast 1 +Simplifying constant integer cast $f +Simplifying constant integer cast 4 +Simplifying constant integer cast $6100 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $7f +Finalized unsigned number type (word) $6100 +Finalized unsigned number type (byte) $88 +Finalized unsigned number type (byte) $81 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) $f +Finalized unsigned number type (byte) 4 +Finalized unsigned number type (word) $6100 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in (unumber~) nmi::$0 ← *((byte*) sample#4) & (byte) $f +Inversing boolean not [39] (bool~) nmi2::$5 ← (byte~) nmi2::$1 != (byte~) nmi2::$3 from [38] (bool~) nmi2::$4 ← (byte~) nmi2::$1 == (byte~) nmi2::$3 +Successful SSA optimization Pass2UnaryNotSimplification +Alias candidate removed (volatile)(byte[SAMPLE_SIZE#0]) SAMPLE#0 = (byte*) sample#0 (byte*) sample#7 +Alias (byte*) sample#3 = (byte*) sample#6 (byte*) sample#8 +Successful SSA optimization Pass2AliasElimination +Alias candidate removed (volatile)(byte[SAMPLE_SIZE#0]) SAMPLE#0 = (byte*) sample#0 (byte*) sample#7 +Identical Phi Values (byte*) sample#4 (byte*) sample#7 +Identical Phi Values (byte*) sample#5 (byte*) sample#7 +Identical Phi Values (byte*) sample#7 (byte*) sample#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) nmi2::$5 [40] if((byte~) nmi2::$1!=(byte~) nmi2::$3) goto nmi2::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant right-side identified [13] (void()*~) main::$0 ← & interrupt(HARDWARE_ALL)(void()) nmi() +Constant right-side identified [25] (void()*~) nmi::$1 ← & interrupt(HARDWARE_ALL)(void()) nmi2() +Constant right-side identified [42] (void()*~) nmi2::$6 ← & interrupt(HARDWARE_ALL)(void()) nmi() +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) BORDERCOL#0 = (byte*) 53280 +Constant (const word*) CIA2_TIMER_A#0 = (word*) 56580 +Constant (const byte*) CIA2_INTERRUPT#0 = (byte*) 56589 +Constant (const byte*) CIA2_TIMER_A_CONTROL#0 = (byte*) 56590 +Constant (const byte) CIA_INTERRUPT_CLEAR#0 = $7f +Constant (const void()**) KERNEL_NMI#0 = (void()**) 792 +Constant (const byte*) SID_VOLUME#0 = (byte*) 54296 +Constant (const word) SAMPLE_SIZE#0 = $6100 +Constant (const byte[SAMPLE_SIZE#0]) SAMPLE#0 = kickasm {{ .import binary "moments_sample.bin" }} +Constant (const void()*) main::$0 = &nmi +Constant (const void()*) nmi::$1 = &nmi2 +Constant (const void()*) nmi2::$6 = &nmi +Successful SSA optimization Pass2ConstantIdentification +Constant right-side identified [23] (byte*~) nmi2::$2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 + (word) $6100 +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) nmi2::$2 = SAMPLE#0+$6100 +Successful SSA optimization Pass2ConstantIdentification +Constant right-side identified [23] (byte~) nmi2::$3 ← > (const byte*) nmi2::$2 +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte) nmi2::$3 = >nmi2::$2 +Successful SSA optimization Pass2ConstantIdentification +Constant inlined nmi2::$6 = &interrupt(HARDWARE_ALL)(void()) nmi() +Constant inlined nmi2::$3 = >(const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100 +Constant inlined main::$0 = &interrupt(HARDWARE_ALL)(void()) nmi() +Constant inlined nmi::$1 = &interrupt(HARDWARE_ALL)(void()) nmi2() +Constant inlined nmi2::$2 = (const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting nmi2::@3(between nmi2 and nmi2::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @7 +Adding NOP phi() at start of @8 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:3 + +Created 1 initial phi equivalence classes +Coalesced [23] sample#10 ← sample#2 +Coalesced [28] sample#9 ← sample#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @8 +Culled Empty Block (label) nmi2::@3 +Renumbering block @4 to @1 +Renumbering block @7 to @2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] (byte*) sample#0 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 + to:@2 +@2: scope:[] from @1 + [2] phi() + [3] call main + to:@end +@end: scope:[] from @2 + [4] phi() +main: scope:[main] from @2 + asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } + asm { sei } + [7] *((const byte*) CIA2_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 + [8] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() + [9] *((const word*) CIA2_TIMER_A#0) ← (byte) $88 + [10] *((const byte*) CIA2_INTERRUPT#0) ← (byte) $81 + [11] *((const byte*) CIA2_TIMER_A_CONTROL#0) ← (byte) 1 + asm { cli } + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return +nmi2: scope:[nmi2] from + [14] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) + asm { ldaCIA2_INTERRUPT } + [16] (byte~) nmi2::$0 ← *((byte*) sample#0) >> (byte) 4 + [17] *((const byte*) SID_VOLUME#0) ← (byte~) nmi2::$0 + [18] (byte*) sample#1 ← ++ (byte*) sample#0 + [19] (byte~) nmi2::$1 ← > (byte*) sample#1 + [20] if((byte~) nmi2::$1!=>(const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100) goto nmi2::@1 + to:nmi2::@2 +nmi2::@2: scope:[nmi2] from nmi2 + [21] (byte*) sample#2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 + to:nmi2::@1 +nmi2::@1: scope:[nmi2] from nmi2 nmi2::@2 + [22] (byte*) sample#3 ← phi( nmi2/(byte*) sample#1 nmi2::@2/(byte*) sample#2 ) + [23] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() + [24] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) + to:nmi2::@return +nmi2::@return: scope:[nmi2] from nmi2::@1 + [25] return + to:@return +nmi: scope:[nmi] from + [26] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) + asm { ldaCIA2_INTERRUPT } + [28] (byte~) nmi::$0 ← *((byte*) sample#0) & (byte) $f + [29] *((const byte*) SID_VOLUME#0) ← (byte~) nmi::$0 + [30] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi2() + [31] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) + to:nmi::@return +nmi::@return: scope:[nmi] from nmi + [32] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte*) BORDERCOL +(byte*) CIA2_INTERRUPT +(word*) CIA2_TIMER_A +(byte*) CIA2_TIMER_A_CONTROL +(byte) CIA_INTERRUPT_CLEAR +(void()**) KERNEL_NMI +(byte[SAMPLE_SIZE#0]) SAMPLE +(word) SAMPLE_SIZE +(byte*) SID_VOLUME +(void()) main() +interrupt(HARDWARE_ALL)(void()) nmi() +(byte~) nmi::$0 4.0 +interrupt(HARDWARE_ALL)(void()) nmi2() +(byte~) nmi2::$0 4.0 +(byte~) nmi2::$1 4.0 +(byte*) sample +(byte*) sample#0 1.3333333333333333 +(byte*) sample#1 2.0 +(byte*) sample#2 4.0 +(byte*) sample#3 40.0 + +Initial phi equivalence classes +[ sample#3 sample#1 sample#2 ] +Coalescing volatile variable equivalence classes [ sample#0 ] and [ sample#3 sample#1 sample#2 ] +Added variable nmi2::$0 to zero page equivalence class [ nmi2::$0 ] +Added variable nmi2::$1 to zero page equivalence class [ nmi2::$1 ] +Added variable nmi::$0 to zero page equivalence class [ nmi::$0 ] +Complete equivalence classes +[ sample#0 sample#3 sample#1 sample#2 ] +[ nmi2::$0 ] +[ nmi2::$1 ] +[ nmi::$0 ] +Allocated zp ZP_WORD:2 [ sample#0 sample#3 sample#1 sample#2 ] +Allocated zp ZP_BYTE:4 [ nmi2::$0 ] +Allocated zp ZP_BYTE:5 [ nmi2::$1 ] +Allocated zp ZP_BYTE:6 [ nmi::$0 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// NMI Sample Player using the SID volume register +// Code by Scan of Desire (Richard-William Loerakker) +// Sample from ART OF NOISE: MOMENTS IN LOVE + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label BORDERCOL = $d020 + // CIA #2 Timer A Value (16-bit) + .label CIA2_TIMER_A = $dd04 + // CIA #2 Interrupt Status & Control Register + .label CIA2_INTERRUPT = $dd0d + // CIA #2 Timer A Control Register + .label CIA2_TIMER_A_CONTROL = $dd0e + // Value that disables all CIA interrupts when stored to the CIA Interrupt registers + .const CIA_INTERRUPT_CLEAR = $7f + // The vector used when the KERNAL serves NMI interrupts + .label KERNEL_NMI = $318 + // The SID volume + .label SID_VOLUME = $d418 + .const SAMPLE_SIZE = $6100 + .label sample = 2 + // @begin +bbegin: + jmp b1 + // @1 +b1: + // [1] (byte*) sample#0 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 -- pbuz1=pbuc1 + lda #SAMPLE + sta.z sample+1 + // [2] phi from @1 to @2 [phi:@1->@2] +b2_from_b1: + jmp b2 + // @2 +b2: + // [3] call main + jsr main + // [4] phi from @2 to @end [phi:@2->@end] +bend_from_b2: + jmp bend + // @end +bend: + // main +main: { + // asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } + // Boosting 8580 Digis + // See https://gist.github.com/munshkr/30f35e39905e63876ff7 (line 909) + lda #$ff + sta $d406 + sta $d40d + sta $d414 + lda #$49 + sta $d404 + sta $d40b + sta $d412 + // asm { sei } + sei + // [7] *((const byte*) CIA2_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 -- _deref_pbuc1=vbuc2 + lda #CIA_INTERRUPT_CLEAR + sta CIA2_INTERRUPT + // [8] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() -- _deref_pptc1=pprc2 + lda #nmi + sta KERNEL_NMI+1 + // [9] *((const word*) CIA2_TIMER_A#0) ← (byte) $88 -- _deref_pwuc1=vbuc2 + lda #0 + sta CIA2_TIMER_A+1 + lda #<$88 + sta CIA2_TIMER_A + // [10] *((const byte*) CIA2_INTERRUPT#0) ← (byte) $81 -- _deref_pbuc1=vbuc2 + // speed + lda #$81 + sta CIA2_INTERRUPT + // [11] *((const byte*) CIA2_TIMER_A_CONTROL#0) ← (byte) 1 -- _deref_pbuc1=vbuc2 + lda #1 + sta CIA2_TIMER_A_CONTROL + // asm { cli } + cli + jmp breturn + // main::@return + breturn: + // [13] return + rts +} + // nmi2 +nmi2: { + .label _0 = 4 + .label _1 = 5 + // entry interrupt(HARDWARE_ALL) + sta rega+1 + stx regx+1 + sty regy+1 + // [14] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + // asm { ldaCIA2_INTERRUPT } + lda CIA2_INTERRUPT + // [16] (byte~) nmi2::$0 ← *((byte*) sample#0) >> (byte) 4 -- vbuz1=_deref_pbuz2_ror_4 + ldy #0 + lda (sample),y + lsr + lsr + lsr + lsr + sta.z _0 + // [17] *((const byte*) SID_VOLUME#0) ← (byte~) nmi2::$0 -- _deref_pbuc1=vbuz1 + lda.z _0 + sta SID_VOLUME + // [18] (byte*) sample#1 ← ++ (byte*) sample#0 -- pbuz1=_inc_pbuz1 + inc.z sample + bne !+ + inc.z sample+1 + !: + // [19] (byte~) nmi2::$1 ← > (byte*) sample#1 -- vbuz1=_hi_pbuz2 + lda.z sample+1 + sta.z _1 + // [20] if((byte~) nmi2::$1!=>(const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100) goto nmi2::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #>SAMPLE+$6100 + cmp.z _1 + bne b1_from_nmi2 + jmp b2 + // nmi2::@2 + b2: + // [21] (byte*) sample#2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 -- pbuz1=pbuc1 + lda #SAMPLE + sta.z sample+1 + // [22] phi from nmi2 nmi2::@2 to nmi2::@1 [phi:nmi2/nmi2::@2->nmi2::@1] + b1_from_nmi2: + b1_from_b2: + // [22] phi (byte*) sample#3 = (byte*) sample#1 [phi:nmi2/nmi2::@2->nmi2::@1#0] -- register_copy + jmp b1 + // nmi2::@1 + b1: + // [23] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() -- _deref_pptc1=pprc2 + lda #nmi + sta KERNEL_NMI+1 + // [24] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BORDERCOL + jmp breturn + // nmi2::@return + breturn: + // [25] return - exit interrupt(HARDWARE_ALL) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + // nmi +nmi: { + .label _0 = 6 + // entry interrupt(HARDWARE_ALL) + sta rega+1 + stx regx+1 + sty regy+1 + // [26] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + // asm { ldaCIA2_INTERRUPT } + lda CIA2_INTERRUPT + // [28] (byte~) nmi::$0 ← *((byte*) sample#0) & (byte) $f -- vbuz1=_deref_pbuz2_band_vbuc1 + lda #$f + ldy #0 + and (sample),y + sta.z _0 + // [29] *((const byte*) SID_VOLUME#0) ← (byte~) nmi::$0 -- _deref_pbuc1=vbuz1 + lda.z _0 + sta SID_VOLUME + // [30] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi2() -- _deref_pptc1=pprc2 + lda #nmi2 + sta KERNEL_NMI+1 + // [31] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BORDERCOL + jmp breturn + // nmi::@return + breturn: + // [32] return - exit interrupt(HARDWARE_ALL) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + // File Data +SAMPLE: +.import binary "moments_sample.bin" + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] (byte*) sample#0 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 [ ] ( [ ] ) always clobbers reg byte a +Statement asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } always clobbers reg byte a +Statement [7] *((const byte*) CIA2_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 [ ] ( main:3 [ ] ) always clobbers reg byte a +Statement [8] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() [ ] ( main:3 [ ] ) always clobbers reg byte a +Statement [9] *((const word*) CIA2_TIMER_A#0) ← (byte) $88 [ ] ( main:3 [ ] ) always clobbers reg byte a +Statement [10] *((const byte*) CIA2_INTERRUPT#0) ← (byte) $81 [ ] ( main:3 [ ] ) always clobbers reg byte a +Statement [11] *((const byte*) CIA2_TIMER_A_CONTROL#0) ← (byte) 1 [ ] ( main:3 [ ] ) always clobbers reg byte a +Statement asm { ldaCIA2_INTERRUPT } always clobbers reg byte a +Statement [16] (byte~) nmi2::$0 ← *((byte*) sample#0) >> (byte) 4 [ sample#0 nmi2::$0 ] ( [ sample#0 nmi2::$0 ] ) always clobbers reg byte a reg byte y +Statement [21] (byte*) sample#2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 [ sample#2 ] ( [ sample#2 ] ) always clobbers reg byte a +Statement [23] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() [ ] ( [ ] ) always clobbers reg byte a +Statement [25] return [ ] ( [ ] ) always clobbers reg byte a reg byte x reg byte y +Statement asm { ldaCIA2_INTERRUPT } always clobbers reg byte a +Statement [28] (byte~) nmi::$0 ← *((byte*) sample#0) & (byte) $f [ nmi::$0 ] ( [ nmi::$0 ] ) always clobbers reg byte a reg byte y +Statement [30] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi2() [ ] ( [ ] ) always clobbers reg byte a +Statement [32] return [ ] ( [ ] ) always clobbers reg byte a reg byte x reg byte y +Potential registers zp ZP_WORD:2 [ sample#0 sample#3 sample#1 sample#2 ] : zp ZP_WORD:2 , +Potential registers zp ZP_BYTE:4 [ nmi2::$0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:5 [ nmi2::$1 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:6 [ nmi::$0 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [] 47.33: zp ZP_WORD:2 [ sample#0 sample#3 sample#1 sample#2 ] +Uplift Scope [nmi2] 4: zp ZP_BYTE:4 [ nmi2::$0 ] 4: zp ZP_BYTE:5 [ nmi2::$1 ] +Uplift Scope [nmi] 4: zp ZP_BYTE:6 [ nmi::$0 ] +Uplift Scope [main] + +Uplifting [] best 546 combination zp ZP_WORD:2 [ sample#0 sample#3 sample#1 sample#2 ] +Uplifting [nmi2] best 534 combination reg byte a [ nmi2::$0 ] reg byte a [ nmi2::$1 ] +Uplifting [nmi] best 528 combination reg byte a [ nmi::$0 ] +Uplifting [main] best 528 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// NMI Sample Player using the SID volume register +// Code by Scan of Desire (Richard-William Loerakker) +// Sample from ART OF NOISE: MOMENTS IN LOVE + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label BORDERCOL = $d020 + // CIA #2 Timer A Value (16-bit) + .label CIA2_TIMER_A = $dd04 + // CIA #2 Interrupt Status & Control Register + .label CIA2_INTERRUPT = $dd0d + // CIA #2 Timer A Control Register + .label CIA2_TIMER_A_CONTROL = $dd0e + // Value that disables all CIA interrupts when stored to the CIA Interrupt registers + .const CIA_INTERRUPT_CLEAR = $7f + // The vector used when the KERNAL serves NMI interrupts + .label KERNEL_NMI = $318 + // The SID volume + .label SID_VOLUME = $d418 + .const SAMPLE_SIZE = $6100 + .label sample = 2 + // @begin +bbegin: + jmp b1 + // @1 +b1: + // [1] (byte*) sample#0 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 -- pbuz1=pbuc1 + lda #SAMPLE + sta.z sample+1 + // [2] phi from @1 to @2 [phi:@1->@2] +b2_from_b1: + jmp b2 + // @2 +b2: + // [3] call main + jsr main + // [4] phi from @2 to @end [phi:@2->@end] +bend_from_b2: + jmp bend + // @end +bend: + // main +main: { + // asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } + // Boosting 8580 Digis + // See https://gist.github.com/munshkr/30f35e39905e63876ff7 (line 909) + lda #$ff + sta $d406 + sta $d40d + sta $d414 + lda #$49 + sta $d404 + sta $d40b + sta $d412 + // asm { sei } + sei + // [7] *((const byte*) CIA2_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 -- _deref_pbuc1=vbuc2 + lda #CIA_INTERRUPT_CLEAR + sta CIA2_INTERRUPT + // [8] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() -- _deref_pptc1=pprc2 + lda #nmi + sta KERNEL_NMI+1 + // [9] *((const word*) CIA2_TIMER_A#0) ← (byte) $88 -- _deref_pwuc1=vbuc2 + lda #0 + sta CIA2_TIMER_A+1 + lda #<$88 + sta CIA2_TIMER_A + // [10] *((const byte*) CIA2_INTERRUPT#0) ← (byte) $81 -- _deref_pbuc1=vbuc2 + // speed + lda #$81 + sta CIA2_INTERRUPT + // [11] *((const byte*) CIA2_TIMER_A_CONTROL#0) ← (byte) 1 -- _deref_pbuc1=vbuc2 + lda #1 + sta CIA2_TIMER_A_CONTROL + // asm { cli } + cli + jmp breturn + // main::@return + breturn: + // [13] return + rts +} + // nmi2 +nmi2: { + // entry interrupt(HARDWARE_ALL) + sta rega+1 + stx regx+1 + sty regy+1 + // [14] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + // asm { ldaCIA2_INTERRUPT } + lda CIA2_INTERRUPT + // [16] (byte~) nmi2::$0 ← *((byte*) sample#0) >> (byte) 4 -- vbuaa=_deref_pbuz1_ror_4 + ldy #0 + lda (sample),y + lsr + lsr + lsr + lsr + // [17] *((const byte*) SID_VOLUME#0) ← (byte~) nmi2::$0 -- _deref_pbuc1=vbuaa + sta SID_VOLUME + // [18] (byte*) sample#1 ← ++ (byte*) sample#0 -- pbuz1=_inc_pbuz1 + inc.z sample + bne !+ + inc.z sample+1 + !: + // [19] (byte~) nmi2::$1 ← > (byte*) sample#1 -- vbuaa=_hi_pbuz1 + lda.z sample+1 + // [20] if((byte~) nmi2::$1!=>(const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100) goto nmi2::@1 -- vbuaa_neq_vbuc1_then_la1 + cmp #>SAMPLE+$6100 + bne b1_from_nmi2 + jmp b2 + // nmi2::@2 + b2: + // [21] (byte*) sample#2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 -- pbuz1=pbuc1 + lda #SAMPLE + sta.z sample+1 + // [22] phi from nmi2 nmi2::@2 to nmi2::@1 [phi:nmi2/nmi2::@2->nmi2::@1] + b1_from_nmi2: + b1_from_b2: + // [22] phi (byte*) sample#3 = (byte*) sample#1 [phi:nmi2/nmi2::@2->nmi2::@1#0] -- register_copy + jmp b1 + // nmi2::@1 + b1: + // [23] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() -- _deref_pptc1=pprc2 + lda #nmi + sta KERNEL_NMI+1 + // [24] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BORDERCOL + jmp breturn + // nmi2::@return + breturn: + // [25] return - exit interrupt(HARDWARE_ALL) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + // nmi +nmi: { + // entry interrupt(HARDWARE_ALL) + sta rega+1 + stx regx+1 + sty regy+1 + // [26] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + // asm { ldaCIA2_INTERRUPT } + lda CIA2_INTERRUPT + // [28] (byte~) nmi::$0 ← *((byte*) sample#0) & (byte) $f -- vbuaa=_deref_pbuz1_band_vbuc1 + lda #$f + ldy #0 + and (sample),y + // [29] *((const byte*) SID_VOLUME#0) ← (byte~) nmi::$0 -- _deref_pbuc1=vbuaa + sta SID_VOLUME + // [30] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi2() -- _deref_pptc1=pprc2 + lda #nmi2 + sta KERNEL_NMI+1 + // [31] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BORDERCOL + jmp breturn + // nmi::@return + breturn: + // [32] return - exit interrupt(HARDWARE_ALL) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + // File Data +SAMPLE: +.import binary "moments_sample.bin" + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp bend +Removing instruction jmp breturn +Removing instruction jmp b2 +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_nmi2 with b1 +Removing instruction b1: +Removing instruction b2_from_b1: +Removing instruction bend_from_b2: +Removing instruction b1_from_nmi2: +Removing instruction b1_from_b2: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction b2: +Removing instruction bend: +Removing instruction breturn: +Removing instruction b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Adding RTS to root block +Succesful ASM optimization Pass5AddMainRts + +FINAL SYMBOL TABLE +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte*) BORDERCOL +(const byte*) BORDERCOL#0 BORDERCOL = (byte*) 53280 +(byte*) CIA2_INTERRUPT +(const byte*) CIA2_INTERRUPT#0 CIA2_INTERRUPT = (byte*) 56589 +(word*) CIA2_TIMER_A +(const word*) CIA2_TIMER_A#0 CIA2_TIMER_A = (word*) 56580 +(byte*) CIA2_TIMER_A_CONTROL +(const byte*) CIA2_TIMER_A_CONTROL#0 CIA2_TIMER_A_CONTROL = (byte*) 56590 +(byte) CIA_INTERRUPT_CLEAR +(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte) $7f +(void()**) KERNEL_NMI +(const void()**) KERNEL_NMI#0 KERNEL_NMI = (void()**) 792 +(byte[SAMPLE_SIZE#0]) SAMPLE +(const byte[SAMPLE_SIZE#0]) SAMPLE#0 SAMPLE = kickasm {{ .import binary "moments_sample.bin" }} +(word) SAMPLE_SIZE +(const word) SAMPLE_SIZE#0 SAMPLE_SIZE = (word) $6100 +(byte*) SID_VOLUME +(const byte*) SID_VOLUME#0 SID_VOLUME = (byte*) 54296 +(void()) main() +(label) main::@return +interrupt(HARDWARE_ALL)(void()) nmi() +(byte~) nmi::$0 reg byte a 4.0 +(label) nmi::@return +interrupt(HARDWARE_ALL)(void()) nmi2() +(byte~) nmi2::$0 reg byte a 4.0 +(byte~) nmi2::$1 reg byte a 4.0 +(label) nmi2::@1 +(label) nmi2::@2 +(label) nmi2::@return +(byte*) sample +(byte*) sample#0 sample zp ZP_WORD:2 1.3333333333333333 +(byte*) sample#1 sample zp ZP_WORD:2 2.0 +(byte*) sample#2 sample zp ZP_WORD:2 4.0 +(byte*) sample#3 sample zp ZP_WORD:2 40.0 + +zp ZP_WORD:2 [ sample#0 sample#3 sample#1 sample#2 ] +reg byte a [ nmi2::$0 ] +reg byte a [ nmi2::$1 ] +reg byte a [ nmi::$0 ] + + +FINAL ASSEMBLER +Score: 483 + + // File Comments +// NMI Sample Player using the SID volume register +// Code by Scan of Desire (Richard-William Loerakker) +// Sample from ART OF NOISE: MOMENTS IN LOVE + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label BORDERCOL = $d020 + // CIA #2 Timer A Value (16-bit) + .label CIA2_TIMER_A = $dd04 + // CIA #2 Interrupt Status & Control Register + .label CIA2_INTERRUPT = $dd0d + // CIA #2 Timer A Control Register + .label CIA2_TIMER_A_CONTROL = $dd0e + // Value that disables all CIA interrupts when stored to the CIA Interrupt registers + .const CIA_INTERRUPT_CLEAR = $7f + // The vector used when the KERNAL serves NMI interrupts + .label KERNEL_NMI = $318 + // The SID volume + .label SID_VOLUME = $d418 + .const SAMPLE_SIZE = $6100 + .label sample = 2 + // @begin +bbegin: + // @1 + // sample = SAMPLE + // [1] (byte*) sample#0 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 -- pbuz1=pbuc1 + lda #SAMPLE + sta.z sample+1 + // [2] phi from @1 to @2 [phi:@1->@2] + // @2 + // [3] call main + jsr main + rts + // [4] phi from @2 to @end [phi:@2->@end] + // @end + // main +main: { + // asm + // asm { lda#$ff sta$d406 sta$d40d sta$d414 lda#$49 sta$d404 sta$d40b sta$d412 } + // Boosting 8580 Digis + // See https://gist.github.com/munshkr/30f35e39905e63876ff7 (line 909) + lda #$ff + sta $d406 + sta $d40d + sta $d414 + lda #$49 + sta $d404 + sta $d40b + sta $d412 + // asm { sei } + sei + // *CIA2_INTERRUPT = CIA_INTERRUPT_CLEAR + // [7] *((const byte*) CIA2_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0 -- _deref_pbuc1=vbuc2 + lda #CIA_INTERRUPT_CLEAR + sta CIA2_INTERRUPT + // *KERNEL_NMI = &nmi + // [8] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() -- _deref_pptc1=pprc2 + lda #nmi + sta KERNEL_NMI+1 + // *CIA2_TIMER_A = 0x88 + // [9] *((const word*) CIA2_TIMER_A#0) ← (byte) $88 -- _deref_pwuc1=vbuc2 + lda #0 + sta CIA2_TIMER_A+1 + lda #<$88 + sta CIA2_TIMER_A + // *CIA2_INTERRUPT = 0x81 + // [10] *((const byte*) CIA2_INTERRUPT#0) ← (byte) $81 -- _deref_pbuc1=vbuc2 + // speed + lda #$81 + sta CIA2_INTERRUPT + // *CIA2_TIMER_A_CONTROL = 0x01 + // [11] *((const byte*) CIA2_TIMER_A_CONTROL#0) ← (byte) 1 -- _deref_pbuc1=vbuc2 + lda #1 + sta CIA2_TIMER_A_CONTROL + // asm + // asm { cli } + cli + // main::@return + // } + // [13] return + rts +} + // nmi2 +nmi2: { + // entry interrupt(HARDWARE_ALL) + sta rega+1 + stx regx+1 + sty regy+1 + // (*BORDERCOL)++; + // [14] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + // asm + // asm { ldaCIA2_INTERRUPT } + lda CIA2_INTERRUPT + // *sample >> 4 + // [16] (byte~) nmi2::$0 ← *((byte*) sample#0) >> (byte) 4 -- vbuaa=_deref_pbuz1_ror_4 + ldy #0 + lda (sample),y + lsr + lsr + lsr + lsr + // *SID_VOLUME = *sample >> 4 + // [17] *((const byte*) SID_VOLUME#0) ← (byte~) nmi2::$0 -- _deref_pbuc1=vbuaa + sta SID_VOLUME + // sample++; + // [18] (byte*) sample#1 ← ++ (byte*) sample#0 -- pbuz1=_inc_pbuz1 + inc.z sample + bne !+ + inc.z sample+1 + !: + // >sample + // [19] (byte~) nmi2::$1 ← > (byte*) sample#1 -- vbuaa=_hi_pbuz1 + lda.z sample+1 + // if (>sample == >(SAMPLE+$6100)) + // [20] if((byte~) nmi2::$1!=>(const byte[SAMPLE_SIZE#0]) SAMPLE#0+(word) $6100) goto nmi2::@1 -- vbuaa_neq_vbuc1_then_la1 + cmp #>SAMPLE+$6100 + bne b1 + // nmi2::@2 + // sample = SAMPLE + // [21] (byte*) sample#2 ← (const byte[SAMPLE_SIZE#0]) SAMPLE#0 -- pbuz1=pbuc1 + lda #SAMPLE + sta.z sample+1 + // [22] phi from nmi2 nmi2::@2 to nmi2::@1 [phi:nmi2/nmi2::@2->nmi2::@1] + // [22] phi (byte*) sample#3 = (byte*) sample#1 [phi:nmi2/nmi2::@2->nmi2::@1#0] -- register_copy + // nmi2::@1 + b1: + // *KERNEL_NMI = &nmi + // [23] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi() -- _deref_pptc1=pprc2 + lda #nmi + sta KERNEL_NMI+1 + // (*BORDERCOL)--; + // [24] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BORDERCOL + // nmi2::@return + // } + // [25] return - exit interrupt(HARDWARE_ALL) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + // nmi +nmi: { + // entry interrupt(HARDWARE_ALL) + sta rega+1 + stx regx+1 + sty regy+1 + // (*BORDERCOL)++; + // [26] *((const byte*) BORDERCOL#0) ← ++ *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + // asm + // asm { ldaCIA2_INTERRUPT } + lda CIA2_INTERRUPT + // *sample & $0f + // [28] (byte~) nmi::$0 ← *((byte*) sample#0) & (byte) $f -- vbuaa=_deref_pbuz1_band_vbuc1 + lda #$f + ldy #0 + and (sample),y + // *SID_VOLUME = *sample & $0f + // [29] *((const byte*) SID_VOLUME#0) ← (byte~) nmi::$0 -- _deref_pbuc1=vbuaa + sta SID_VOLUME + // *KERNEL_NMI = &nmi2 + // [30] *((const void()**) KERNEL_NMI#0) ← &interrupt(HARDWARE_ALL)(void()) nmi2() -- _deref_pptc1=pprc2 + lda #nmi2 + sta KERNEL_NMI+1 + // (*BORDERCOL)--; + // [31] *((const byte*) BORDERCOL#0) ← -- *((const byte*) BORDERCOL#0) -- _deref_pbuc1=_dec__deref_pbuc1 + dec BORDERCOL + // nmi::@return + // } + // [32] return - exit interrupt(HARDWARE_ALL) + rega: + lda #00 + regx: + ldx #00 + regy: + ldy #00 + rti +} + // File Data +SAMPLE: +.import binary "moments_sample.bin" + diff --git a/src/test/ref/examples/nmisamples/nmisamples.sym b/src/test/ref/examples/nmisamples/nmisamples.sym new file mode 100644 index 000000000..a44d4a775 --- /dev/null +++ b/src/test/ref/examples/nmisamples/nmisamples.sym @@ -0,0 +1,43 @@ +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte*) BORDERCOL +(const byte*) BORDERCOL#0 BORDERCOL = (byte*) 53280 +(byte*) CIA2_INTERRUPT +(const byte*) CIA2_INTERRUPT#0 CIA2_INTERRUPT = (byte*) 56589 +(word*) CIA2_TIMER_A +(const word*) CIA2_TIMER_A#0 CIA2_TIMER_A = (word*) 56580 +(byte*) CIA2_TIMER_A_CONTROL +(const byte*) CIA2_TIMER_A_CONTROL#0 CIA2_TIMER_A_CONTROL = (byte*) 56590 +(byte) CIA_INTERRUPT_CLEAR +(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte) $7f +(void()**) KERNEL_NMI +(const void()**) KERNEL_NMI#0 KERNEL_NMI = (void()**) 792 +(byte[SAMPLE_SIZE#0]) SAMPLE +(const byte[SAMPLE_SIZE#0]) SAMPLE#0 SAMPLE = kickasm {{ .import binary "moments_sample.bin" }} +(word) SAMPLE_SIZE +(const word) SAMPLE_SIZE#0 SAMPLE_SIZE = (word) $6100 +(byte*) SID_VOLUME +(const byte*) SID_VOLUME#0 SID_VOLUME = (byte*) 54296 +(void()) main() +(label) main::@return +interrupt(HARDWARE_ALL)(void()) nmi() +(byte~) nmi::$0 reg byte a 4.0 +(label) nmi::@return +interrupt(HARDWARE_ALL)(void()) nmi2() +(byte~) nmi2::$0 reg byte a 4.0 +(byte~) nmi2::$1 reg byte a 4.0 +(label) nmi2::@1 +(label) nmi2::@2 +(label) nmi2::@return +(byte*) sample +(byte*) sample#0 sample zp ZP_WORD:2 1.3333333333333333 +(byte*) sample#1 sample zp ZP_WORD:2 2.0 +(byte*) sample#2 sample zp ZP_WORD:2 4.0 +(byte*) sample#3 sample zp ZP_WORD:2 40.0 + +zp ZP_WORD:2 [ sample#0 sample#3 sample#1 sample#2 ] +reg byte a [ nmi2::$0 ] +reg byte a [ nmi2::$1 ] +reg byte a [ nmi::$0 ]