1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-30 09:57:11 +00:00

Added support for indirect calls to advanced expressions through a new fragment type. Closes #708

This commit is contained in:
jespergravgaard 2021-08-08 17:45:56 +02:00
parent 77db0e8701
commit 66a1222fdb
23 changed files with 1460 additions and 2415 deletions

View File

@ -1,4 +1,4 @@
//KICKC FRAGMENT CACHE 8d6655dbd 8d6657e44
//KICKC FRAGMENT CACHE 84ae04234 84ae062c1
//FRAGMENT vbuzz=vbuc1
ldz #{c1}
//FRAGMENT vbuzz_lt_vbuc1_then_la1

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
//KICKC FRAGMENT CACHE 8d6655dbd 8d6657e44
//KICKC FRAGMENT CACHE 84ae04234 84ae062c1
//FRAGMENT vbuz1=vbuc1
lda #{c1}
sta {z1}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
//KICKC FRAGMENT CACHE 8d6655dbd 8d6657e44
//KICKC FRAGMENT CACHE 84ae04234 84ae062c1
//FRAGMENT _deref_pbuc1=_inc__deref_pbuc1
inc {c1}
//FRAGMENT isr_hardware_all_entry

View File

@ -1,4 +1,4 @@
//KICKC FRAGMENT CACHE 8d6655dbd 8d6657e44
//KICKC FRAGMENT CACHE 84ae04234 84ae062c1
//FRAGMENT vbuz1=_deref_pbuc1
lda {c1}
sta {z1}
@ -244,81 +244,3 @@ inc
//FRAGMENT vbuyy_neq_vbuc1_then_la1
cpy #{c1}
bne {la1}
//FRAGMENT 0_neq_pbuc1_derefidx_vbuz1_then_la1
ldy {z1}
lda {c1},y
cmp #0
bne {la1}
//FRAGMENT pbuz1=pbuz2
lda {z2}
sta {z1}
lda {z2}+1
sta {z1}+1
//FRAGMENT vbuz1=pbuc1_derefidx_vbuz2
ldy {z2}
lda {c1},y
sta {z1}
//FRAGMENT pbuz1=_inc_pbuz2
clc
lda {z2}
adc #1
sta {z1}
lda {z2}+1
adc #0
sta {z1}+1
//FRAGMENT _deref_pbuc1=_deref_pbuz1
ldy #0
lda ({z1}),y
sta {c1}
//FRAGMENT vbuz1=_byte0_pbuz2
lda {z2}
sta {z1}
//FRAGMENT vbuz1=_byte1_pbuz2
lda {z2}+1
sta {z1}
//FRAGMENT 0_neq_pbuc1_derefidx_vbuaa_then_la1
tay
lda {c1},y
cmp #0
bne {la1}
//FRAGMENT 0_neq_pbuc1_derefidx_vbuxx_then_la1
lda {c1},x
cmp #0
bne {la1}
//FRAGMENT 0_neq_pbuc1_derefidx_vbuyy_then_la1
lda {c1},y
cmp #0
bne {la1}
//FRAGMENT vbuz1=pbuc1_derefidx_vbuxx
lda {c1},x
sta {z1}
//FRAGMENT vbuz1=pbuc1_derefidx_vbuyy
lda {c1},y
sta {z1}
//FRAGMENT vbuaa=pbuc1_derefidx_vbuz1
ldy {z1}
lda {c1},y
//FRAGMENT vbuaa=pbuc1_derefidx_vbuxx
lda {c1},x
//FRAGMENT vbuaa=pbuc1_derefidx_vbuyy
lda {c1},y
//FRAGMENT vbuxx=pbuc1_derefidx_vbuz1
ldy {z1}
ldx {c1},y
//FRAGMENT vbuaa=_byte0_pbuz1
lda {z1}
//FRAGMENT vbuxx=_byte0_pbuz1
ldx {z1}
//FRAGMENT vbuaa=_byte1_pbuz1
lda {z1}+1
//FRAGMENT vbuxx=_byte1_pbuz1
ldx {z1}+1
//FRAGMENT vbuyy=_byte0_pbuz1
ldy {z1}
//FRAGMENT vbuyy=_byte1_pbuz1
ldy {z1}+1
//FRAGMENT vbuyy=pbuc1_derefidx_vbuz1
ldx {z1}
ldy {c1},x
//FRAGMENT vbuxx=pbuc1_derefidx_vbuyy
ldx {c1},y

View File

@ -0,0 +1 @@
jsr {c1}

View File

@ -0,0 +1,7 @@
lda ({z1}),y
sta !+ +1
iny
lda ({z1}),y
sta !+ +2
!:
jsr $0000

View File

@ -58,6 +58,24 @@ public class AsmFragmentInstanceSpecBuilder {
return new AsmFragmentInstanceSpecBuilder(program, bindings, fragmentInstanceSpec);
}
/**
* Create a fragment instance spec factory for an indirect call
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpecBuilder call(StatementCallExecute call, Program program) {
return new AsmFragmentInstanceSpecBuilder(call, program);
}
private AsmFragmentInstanceSpecBuilder(StatementCallExecute call, Program program) {
this.program = program;
this.bindings = new LinkedHashMap<>();
ScopeRef codeScope = program.getStatementInfos().getBlock(call).getScope();
StringBuilder signature = new StringBuilder();
signature.append("call_");
signature.append(bind(call.getProcedureRVal()));
this.asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, signature.toString(), bindings, codeScope);
}
/**
* Create a fragment instance spec factory for an interrupt routine entry
*

View File

@ -17,7 +17,6 @@ import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos;
import dk.camelot64.kickc.passes.utils.SizeOfConstants;
import java.io.File;
import java.util.*;
/**
@ -662,13 +661,13 @@ public class Pass4CodeGeneration {
}
// Add padding if this is a union and the first member does not use all bytes
final int declaredSize = structValue.getStructType().getSizeBytes();
if(size<declaredSize) {
if(size < declaredSize) {
long paddingSize = declaredSize - size;
// TODO: Use SIZEOF constant
ConstantValue paddingSizeVal = new ConstantInteger(paddingSize);
String paddingBytesAsm = AsmFormat.getAsmConstant(program, paddingSizeVal, 99, scopeRef);
ConstantValue zeroValue = new ConstantInteger(0l, SymbolType.BYTE);
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, paddingBytesAsm, (int)paddingSize, getEncoding(zeroValue));
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, paddingBytesAsm, (int) paddingSize, getEncoding(zeroValue));
}
} else if(value instanceof StructZero) {
final SymbolTypeStruct typeStruct = ((StructZero) value).getTypeStruct();
@ -814,7 +813,7 @@ public class Pass4CodeGeneration {
StatementSource statementSource = statement.getSource();
if(warnFragmentMissing) {
String stmtFormat = "";
if(statementSource!=null)
if(statementSource != null)
stmtFormat = statementSource.format();
program.getLog().append("Warning! Unknown fragment for statement " + statement.toString(program, false) + "\nMissing ASM fragment " + e.getFragmentSignature() + "\n" + stmtFormat);
asm.addLine(new AsmInlineKickAsm(".assert \"Missing ASM fragment " + e.getFragmentSignature() + "\", 0, 1", 0L, 0L));
@ -893,7 +892,7 @@ public class Pass4CodeGeneration {
ensureEncoding(asm, asmFragmentInstanceSpecBuilder);
generateAsm(asm, asmFragmentInstanceSpecBuilder.getAsmFragmentInstanceSpec());
} else {
throw new CompileError("Intrinsic procedure not supported "+procedure.toString(program));
throw new CompileError("Intrinsic procedure not supported " + procedure.toString(program));
}
} else if(Procedure.CallingConvention.PHI_CALL.equals(procedure.getCallingConvention())) {
// Generate PHI transition
@ -921,19 +920,18 @@ public class Pass4CodeGeneration {
supported = true;
} else if(procedureRVal instanceof PointerDereferenceSimple) {
RValue pointer = ((PointerDereferenceSimple) procedureRVal).getPointer();
if(pointer instanceof ConstantValue) {
ensureEncoding(asm, pointer);
asm.addInstruction("jsr", CpuAddressingMode.ABS, AsmFormat.getAsmConstant(program, (ConstantValue) pointer, 99, block.getScope()), false);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
supported = true;
} else if(pointer instanceof VariableRef) {
while(pointer instanceof CastValue)
pointer = ((CastValue) pointer).getValue();
if(pointer instanceof VariableRef) {
Variable variable = getScope().getVariable((VariableRef) pointer);
generateIndirectCall(asm, variable, block.getScope());
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
supported = true;
} else if(pointer instanceof CastValue && ((CastValue) pointer).getValue() instanceof VariableRef) {
Variable variable = getScope().getVariable((VariableRef) ((CastValue) pointer).getValue());
generateIndirectCall(asm, variable, block.getScope());
} else {
// Generate ASM for an indirect call
AsmFragmentInstanceSpecBuilder asmFragmentInstanceSpecBuilder = AsmFragmentInstanceSpecBuilder.call(call, program);
ensureEncoding(asm, asmFragmentInstanceSpecBuilder);
generateAsm(asm, asmFragmentInstanceSpecBuilder.getAsmFragmentInstanceSpec());
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
supported = true;
}
@ -1059,7 +1057,7 @@ public class Pass4CodeGeneration {
entryName = entryFragment.getAsmFragmentInstanceSpec().getSignature();
}
try {
asm.startChunk(procedure.getRef(), null, "interrupt(" + entryName+ ")");
asm.startChunk(procedure.getRef(), null, "interrupt(" + entryName + ")");
generateAsm(asm, entryFragment.getAsmFragmentInstanceSpec());
} catch(AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
throw new CompileError("Interrupt type not supported " + procedure.getInterruptType() + " int " + procedure.toString() + "\n" + e.getMessage());

View File

@ -3393,6 +3393,11 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("function-pointer-noarg-call.c");
}
@Test
public void testFunctionPointerReturn3() throws IOException {
compileAndCompare("function-pointer-return-3.c");
}
@Test
public void testFunctionPointerReturn2() throws IOException {
compileAndCompare("function-pointer-return-2.c");

View File

@ -1,5 +1,5 @@
// Calling a function pointer with parameters
// Reference the function without &
// Calling a function pointer with return value
// Reference the function without &, Call it without *
char * const RASTER = (char*)0xd012;
char * const BORDER = (char*)0xd020;

View File

@ -0,0 +1,40 @@
// Calling a function pointer with return value
// Calling a function pointer inside a struct without *
struct Task {
char param;
void (*handler)(char);
};
char * const RASTER = (char*)0xd012;
char * const BORDER = (char*)0xd020;
char * const BACKGROUND = (char*)0xd021;
void set_border(char col) {
*BORDER = col;
}
void set_bg(char col) {
*BACKGROUND = col;
}
void run(struct Task* task) {
task->handler(task->param);
}
struct Task tasks[] = {
{ 0, &set_border },
{ 0, &set_bg },
{ 1, &set_border },
{ 2, &set_bg }
};
void main() {
for(;;) {
for(char i=0; i < sizeof(tasks)/sizeof(struct Task); i++) {
run(tasks+i);
}
}
}

View File

@ -158,7 +158,7 @@ ASSEMBLER BEFORE OPTIMIZATION
// main
// Play the music
main: {
// [1] callexecute *musicInit
// [1] callexecute *musicInit -- call__deref_pprc1
// Initialize the music
jsr musicInit
jmp __b1
@ -174,7 +174,7 @@ main: {
__b2:
// [3] *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) = ++ *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) -- _deref_pbuc1=_inc__deref_pbuc1
inc VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
// [4] callexecute *musicPlay
// [4] callexecute *musicPlay -- call__deref_pprc1
// Play the music
jsr musicPlay
jmp __b3
@ -249,7 +249,7 @@ Score: 1066
// Play the music
main: {
// (*musicInit)()
// [1] callexecute *musicInit
// [1] callexecute *musicInit -- call__deref_pprc1
// Initialize the music
jsr musicInit
// Wait for the RASTER
@ -265,7 +265,7 @@ main: {
// [3] *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) = ++ *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) -- _deref_pbuc1=_inc__deref_pbuc1
inc VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
// (*musicPlay)()
// [4] callexecute *musicPlay
// [4] callexecute *musicPlay -- call__deref_pprc1
// Play the music
jsr musicPlay
// main::@3

View File

@ -220,7 +220,7 @@ irq_play: {
// interrupt(isr_rom_sys_c64_entry) -- isr_rom_sys_c64_entry
// [0] *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) = ++ *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) -- _deref_pbuc1=_inc__deref_pbuc1
inc VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
// [1] callexecute *musicPlay
// [1] callexecute *musicPlay -- call__deref_pprc1
// Play SID
jsr musicPlay
jmp __b1
@ -244,7 +244,7 @@ irq_play: {
main: {
// asm { sei }
sei
// [6] callexecute *musicInit
// [6] callexecute *musicInit -- call__deref_pprc1
jsr musicInit
jmp __b1
// main::@1
@ -378,7 +378,7 @@ irq_play: {
// [0] *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) = ++ *((byte*)VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR) -- _deref_pbuc1=_inc__deref_pbuc1
inc VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
// (*musicPlay)()
// [1] callexecute *musicPlay
// [1] callexecute *musicPlay -- call__deref_pprc1
// Play SID
jsr musicPlay
// irq_play::@1
@ -403,7 +403,7 @@ main: {
// asm { sei }
sei
// (*musicInit)()
// [6] callexecute *musicInit
// [6] callexecute *musicInit -- call__deref_pprc1
jsr musicInit
// main::@1
// CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR

View File

@ -1147,7 +1147,7 @@ irq: {
jmp __b4
// irq::@4
__b4:
// [4] callexecute *musicPlay
// [4] callexecute *musicPlay -- call__deref_pprc1
// Play remapped SID
jsr musicPlay
// [5] phi from irq::@4 to irq::@1 [phi:irq::@4->irq::@1]
@ -1271,7 +1271,7 @@ main: {
// asm { lda#0 }
// Initialize SID
lda #0
// [24] callexecute *musicInit
// [24] callexecute *musicInit -- call__deref_pprc1
jsr musicInit
// [25] phi from main::@7 to main::@1 [phi:main::@7->main::@1]
__b1_from___b7:
@ -1846,7 +1846,7 @@ irq: {
// [3] phi from irq to irq::@4 [phi:irq->irq::@4]
// irq::@4
// (*musicPlay)()
// [4] callexecute *musicPlay
// [4] callexecute *musicPlay -- call__deref_pprc1
// Play remapped SID
jsr musicPlay
// [5] phi from irq::@4 to irq::@1 [phi:irq::@4->irq::@1]
@ -1962,7 +1962,7 @@ main: {
// Initialize SID
lda #0
// (*musicInit)()
// [24] callexecute *musicInit
// [24] callexecute *musicInit -- call__deref_pprc1
jsr musicInit
// [25] phi from main::@7 to main::@1 [phi:main::@7->main::@1]
// main::@1

View File

@ -2255,7 +2255,7 @@ irq: {
jmp __b4
// irq::@4
__b4:
// [17] callexecute *songPlay
// [17] callexecute *songPlay -- call__deref_pprc1
// play music
jsr songPlay
jmp __b1
@ -2734,7 +2734,7 @@ main: {
// asm { lda#0 }
// Initialize music
lda #0
// [116] callexecute *songInit
// [116] callexecute *songInit -- call__deref_pprc1
jsr songInit
// [117] phi from main to main::@1 [phi:main->main::@1]
__b1_from_main:
@ -3449,7 +3449,7 @@ irq: {
// [16] phi from irq::@2 to irq::@4 [phi:irq::@2->irq::@4]
// irq::@4
// (*songPlay)()
// [17] callexecute *songPlay
// [17] callexecute *songPlay -- call__deref_pprc1
// play music
jsr songPlay
// irq::@1
@ -3946,7 +3946,7 @@ main: {
// Initialize music
lda #0
// (*songInit)()
// [116] callexecute *songInit
// [116] callexecute *songInit -- call__deref_pprc1
jsr songInit
// [117] phi from main to main::@1 [phi:main->main::@1]
// main::@1

View File

@ -1,5 +1,5 @@
// Calling a function pointer with parameters
// Reference the function without &
// Calling a function pointer with return value
// Reference the function without &, Call it without *
// Commodore 64 PRG executable file
.file [name="function-pointer-return-2.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]

View File

@ -238,8 +238,8 @@ Uplifting [] best 194 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Calling a function pointer with parameters
// Reference the function without &
// Calling a function pointer with return value
// Reference the function without &, Call it without *
// Upstart
// Commodore 64 PRG executable file
.file [name="function-pointer-return-2.prg", type="prg", segments="Program"]
@ -391,8 +391,8 @@ FINAL ASSEMBLER
Score: 95
// File Comments
// Calling a function pointer with parameters
// Reference the function without &
// Calling a function pointer with return value
// Reference the function without &, Call it without *
// Upstart
// Commodore 64 PRG executable file
.file [name="function-pointer-return-2.prg", type="prg", segments="Program"]

View File

@ -0,0 +1,94 @@
// Calling a function pointer with return value
// Calling a function pointer inside a struct without *
// Commodore 64 PRG executable file
.file [name="function-pointer-return-3.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
.const SIZEOF_STRUCT_TASK = 3
.const OFFSET_STRUCT_TASK_HANDLER = 1
.const STACK_BASE = $103
.label BORDER = $d020
.label BACKGROUND = $d021
.segment Code
main: {
.label i = 2
__b3:
lda #0
sta.z i
__b1:
// for(char i=0; i < sizeof(tasks)/sizeof(struct Task); i++)
lda.z i
cmp #4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK
bcc __b2
jmp __b3
__b2:
// tasks+i
lda.z i
asl
clc
adc.z i
tax
// run(tasks+i)
txa
clc
adc #<tasks
sta.z run.task
lda #>tasks
adc #0
sta.z run.task+1
jsr run
// for(char i=0; i < sizeof(tasks)/sizeof(struct Task); i++)
inc.z i
jmp __b1
}
// set_bg(byte register(A) col)
set_bg: {
.const OFFSET_STACK_COL = 0
tsx
lda STACK_BASE+OFFSET_STACK_COL,x
// *BACKGROUND = col
sta BACKGROUND
// }
rts
}
// set_border(byte register(A) col)
set_border: {
.const OFFSET_STACK_COL = 0
tsx
lda STACK_BASE+OFFSET_STACK_COL,x
// *BORDER = col
sta BORDER
// }
rts
}
// run(struct Task* zp(3) task)
run: {
.label task = 3
// task->handler(task->param)
lda tasks,x
pha
ldy #OFFSET_STRUCT_TASK_HANDLER
lda (task),y
sta !+ +1
iny
lda (task),y
sta !+ +2
!:
jsr 0
pla
// }
rts
}
.segment Data
tasks: .byte 0
.word set_border
.byte 0
.word set_bg
.byte 1
.word set_border
.byte 2
.word set_bg

View File

@ -0,0 +1,46 @@
void main()
main: scope:[main] from
[0] phi()
to:main::@1
main::@1: scope:[main] from main main::@1 main::@3
[1] main::i#2 = phi( main/0, main::@3/main::i#1, main::@1/0 )
[2] if(main::i#2<4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK) goto main::@2
to:main::@1
main::@2: scope:[main] from main::@1
[3] main::$6 = main::i#2 << 1
[4] main::$5 = main::$6 + main::i#2
[5] run::task#0 = tasks + main::$5
[6] call run
to:main::@3
main::@3: scope:[main] from main::@2
[7] main::i#1 = ++ main::i#2
to:main::@1
__stackcall void set_bg(byte set_bg::col)
set_bg: scope:[set_bg] from
[8] set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL)
[9] *BACKGROUND = set_bg::col#0
to:set_bg::@return
set_bg::@return: scope:[set_bg] from set_bg
[10] return
to:@return
__stackcall void set_border(byte set_border::col)
set_border: scope:[set_border] from
[11] set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL)
[12] *BORDER = set_border::col#0
to:set_border::@return
set_border::@return: scope:[set_border] from set_border
[13] return
to:@return
void run(struct Task* run::task)
run: scope:[run] from main::@2
[14] stackpush(byte) = ((byte*)tasks)[main::$5]
[15] callexecute *(((void(byte)**)run::task#0)[OFFSET_STRUCT_TASK_HANDLER])
sideeffect stackpullbytes(1)
to:run::@return
run::@return: scope:[run] from run
[17] return
to:@return

View File

@ -0,0 +1,646 @@
Setting inferred __stackcall on procedure affected by address-of __stackcall void set_border(byte set_border::col) caused by statement void set_border(byte set_border::col)
Setting inferred __stackcall on procedure affected by address-of __stackcall void set_bg(byte set_bg::col) caused by statement void set_bg(byte set_bg::col)
Setting inferred __stackcall on procedure affected by address-of __stackcall void set_border(byte set_border::col) caused by statement __stackcall void set_border(byte set_border::col)
Setting inferred __stackcall on procedure affected by address-of __stackcall void set_bg(byte set_bg::col) caused by statement __stackcall void set_bg(byte set_bg::col)
Adding parameter assignment in __stackcall procedure set_border::col = param(set_border::col)
Adding parameter assignment in __stackcall procedure set_bg::col = param(set_bg::col)
Calling convention STACK_CALL adding prepare/execute/finalize for call *(*run::$1) *run::$2
Calling convention STACK_CALL replacing param(set_border::col) with stackidx(byte,set_border::OFFSET_STACK_COL)
Calling convention STACK_CALL replacing param(set_bg::col) with stackidx(byte,set_bg::OFFSET_STACK_COL)
Calling convention STACK_CALL adding stack push stackpush(byte) = *run::$2
CONTROL FLOW GRAPH SSA
__stackcall void set_border(byte set_border::col)
set_border: scope:[set_border] from
set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL)
*BORDER = set_border::col#0
to:set_border::@return
set_border::@return: scope:[set_border] from set_border
return
to:@return
__stackcall void set_bg(byte set_bg::col)
set_bg: scope:[set_bg] from
set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL)
*BACKGROUND = set_bg::col#0
to:set_bg::@return
set_bg::@return: scope:[set_bg] from set_bg
return
to:@return
void run(struct Task* run::task)
run: scope:[run] from main::@3
run::task#1 = phi( main::@3/run::task#0 )
run::$3 = (void(byte)**)run::task#1
run::$1 = run::$3 + OFFSET_STRUCT_TASK_HANDLER
run::$4 = (byte*)run::task#1
run::$2 = run::$4 + OFFSET_STRUCT_TASK_PARAM
stackpush(byte) = *run::$2
callexecute *(*run::$1)
sideeffect stackpullbytes(1)
to:run::@return
run::@return: scope:[run] from run
return
to:@return
void main()
main: scope:[main] from __start
to:main::@1
main::@1: scope:[main] from main main::@2
main::i#0 = 0
to:main::@2
main::@2: scope:[main] from main::@1 main::@4
main::i#2 = phi( main::@1/main::i#0, main::@4/main::i#1 )
main::$0 = sizeof tasks
main::$1 = main::$0 / SIZEOF_STRUCT_TASK
main::$2 = main::i#2 < main::$1
if(main::$2) goto main::@3
to:main::@1
main::@3: scope:[main] from main::@2
main::i#3 = phi( main::@2/main::i#2 )
main::$5 = main::i#3 * SIZEOF_STRUCT_TASK
main::$3 = tasks + main::$5
run::task#0 = main::$3
call run
to:main::@4
main::@4: scope:[main] from main::@3
main::i#4 = phi( main::@3/main::i#3 )
main::i#1 = ++ main::i#4
to:main::@2
main::@return: scope:[main] from
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
constant byte* const BACKGROUND = (byte*)$d021
constant byte* const BORDER = (byte*)$d020
constant byte OFFSET_STRUCT_TASK_HANDLER = 1
constant byte OFFSET_STRUCT_TASK_PARAM = 0
constant byte SIZEOF_STRUCT_TASK = 3
constant word STACK_BASE = $103
void __start()
void main()
word~ main::$0
word~ main::$1
bool~ main::$2
struct Task*~ main::$3
byte~ main::$5
byte main::i
byte main::i#0
byte main::i#1
byte main::i#2
byte main::i#3
byte main::i#4
void run(struct Task* run::task)
void(byte)**~ run::$1
byte*~ run::$2
void(byte)**~ run::$3
byte*~ run::$4
struct Task* run::task
struct Task* run::task#0
struct Task* run::task#1
__stackcall void set_bg(byte set_bg::col)
constant byte set_bg::OFFSET_STACK_COL = 0
byte set_bg::col
byte set_bg::col#0
__stackcall void set_border(byte set_border::col)
constant byte set_border::OFFSET_STACK_COL = 0
byte set_border::col
byte set_border::col#0
constant struct Task* tasks[] = { { param: 0, handler: &set_border }, { param: 0, handler: &set_bg }, { param: 1, handler: &set_border }, { param: 2, handler: &set_bg } }
Simplifying constant pointer cast (byte*) 53280
Simplifying constant pointer cast (byte*) 53281
Successful SSA optimization PassNCastSimplification
Alias main::i#2 = main::i#3 main::i#4
Alias run::task#0 = main::$3
Successful SSA optimization Pass2AliasElimination
Identical Phi Values run::task#1 run::task#0
Successful SSA optimization Pass2IdenticalPhiElimination
Simple Condition main::$2 [20] if(main::i#2<main::$1) goto main::@3
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant right-side identified [17] main::$0 = sizeof tasks
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant main::i#0 = 0
Constant main::$0 = sizeof tasks
Successful SSA optimization Pass2ConstantIdentification
Converting *(pointer+n) to pointer[n] [11] stackpush(byte) = *run::$2 -- run::$4[OFFSET_STRUCT_TASK_PARAM]
Converting *(pointer+n) to pointer[n] [12] callexecute *(*run::$1) -- run::$3[OFFSET_STRUCT_TASK_HANDLER]
Successful SSA optimization Pass2InlineDerefIdx
Simplifying expression containing zero run::$4 in [10] run::$2 = run::$4 + OFFSET_STRUCT_TASK_PARAM
Simplifying expression containing zero run::$4 in [11] stackpush(byte) = run::$4[OFFSET_STRUCT_TASK_PARAM]
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused block main::@return
Successful SSA optimization Pass2EliminateUnusedBlocks
Eliminating unused variable run::$1 and assignment [7] run::$1 = run::$3 + OFFSET_STRUCT_TASK_HANDLER
Eliminating unused variable run::$2 and assignment [9] run::$2 = run::$4
Eliminating unused constant OFFSET_STRUCT_TASK_PARAM
Successful SSA optimization PassNEliminateUnusedVars
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Resolving array sizeof() sizeof tasks
Successful SSA optimization PassNSizeOfSimplification
Constant right-side identified [13] main::$1 = main::$0 / SIZEOF_STRUCT_TASK
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant main::$1 = main::$0/SIZEOF_STRUCT_TASK
Successful SSA optimization Pass2ConstantIdentification
Converting *(pointer+n) to pointer[n] [8] stackpush(byte) = *run::$4 -- ((byte*)tasks)[main::$5]
Successful SSA optimization Pass2InlineDerefIdx
Eliminating unused variable run::$4 and assignment [7] run::$4 = (byte*)run::task#0
Successful SSA optimization PassNEliminateUnusedVars
Adding number conversion cast (unumber) 4 in
Successful SSA optimization PassNAddNumberTypeConversions
Simplifying constant integer cast 4
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 4
Successful SSA optimization PassNFinalizeNumberTypeConversions
Constant value identified (byte*)tasks in [7] stackpush(byte) = ((byte*)tasks)[main::$5]
Successful SSA optimization Pass2ConstantValues
Inlining Noop Cast [6] run::$3 = (void(byte)**)run::task#0 keeping run::task#0
Successful SSA optimization Pass2NopCastInlining
Rewriting multiplication to use shift and addition[13] main::$5 = main::i#2 * SIZEOF_STRUCT_TASK
Inlining constant with var siblings main::i#0
Constant inlined main::$1 = 4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK
Constant inlined main::i#0 = 0
Constant inlined main::$0 = 4*SIZEOF_STRUCT_TASK
Successful SSA optimization Pass2ConstantInlining
Alias main::$5 = main::$7
Successful SSA optimization Pass2AliasElimination
Finalized unsigned number type (byte) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Adding NOP phi() at start of main
Adding NOP phi() at start of main::@1
CALL GRAPH
Calls in [main] to run:7
Calls in [run] to null:17
Created 1 initial phi equivalence classes
Coalesced [9] main::i#5 = main::i#1
Coalesced down to 1 phi equivalence classes
Culled Empty Block label main::@1
Renumbering block main::@2 to main::@1
Renumbering block main::@3 to main::@2
Renumbering block main::@4 to main::@3
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
to:main::@1
main::@1: scope:[main] from main main::@1 main::@3
[1] main::i#2 = phi( main/0, main::@3/main::i#1, main::@1/0 )
[2] if(main::i#2<4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK) goto main::@2
to:main::@1
main::@2: scope:[main] from main::@1
[3] main::$6 = main::i#2 << 1
[4] main::$5 = main::$6 + main::i#2
[5] run::task#0 = tasks + main::$5
[6] call run
to:main::@3
main::@3: scope:[main] from main::@2
[7] main::i#1 = ++ main::i#2
to:main::@1
__stackcall void set_bg(byte set_bg::col)
set_bg: scope:[set_bg] from
[8] set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL)
[9] *BACKGROUND = set_bg::col#0
to:set_bg::@return
set_bg::@return: scope:[set_bg] from set_bg
[10] return
to:@return
__stackcall void set_border(byte set_border::col)
set_border: scope:[set_border] from
[11] set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL)
[12] *BORDER = set_border::col#0
to:set_border::@return
set_border::@return: scope:[set_border] from set_border
[13] return
to:@return
void run(struct Task* run::task)
run: scope:[run] from main::@2
[14] stackpush(byte) = ((byte*)tasks)[main::$5]
[15] callexecute *(((void(byte)**)run::task#0)[OFFSET_STRUCT_TASK_HANDLER])
sideeffect stackpullbytes(1)
to:run::@return
run::@return: scope:[run] from run
[17] return
to:@return
VARIABLE REGISTER WEIGHTS
void main()
byte~ main::$5 61.5
byte~ main::$6 22.0
byte main::i
byte main::i#1 22.0
byte main::i#2 24.16666666666666
void run(struct Task* run::task)
struct Task* run::task
struct Task* run::task#0 110.0
__stackcall void set_bg(byte set_bg::col)
byte set_bg::col
byte set_bg::col#0 4.0
__stackcall void set_border(byte set_border::col)
byte set_border::col
byte set_border::col#0 4.0
Initial phi equivalence classes
[ main::i#2 main::i#1 ]
Added variable main::$6 to live range equivalence class [ main::$6 ]
Added variable main::$5 to live range equivalence class [ main::$5 ]
Added variable run::task#0 to live range equivalence class [ run::task#0 ]
Added variable set_bg::col#0 to live range equivalence class [ set_bg::col#0 ]
Added variable set_border::col#0 to live range equivalence class [ set_border::col#0 ]
Complete equivalence classes
[ main::i#2 main::i#1 ]
[ main::$6 ]
[ main::$5 ]
[ run::task#0 ]
[ set_bg::col#0 ]
[ set_border::col#0 ]
Allocated zp[1]:2 [ main::i#2 main::i#1 ]
Allocated zp[1]:3 [ main::$6 ]
Allocated zp[1]:4 [ main::$5 ]
Allocated zp[2]:5 [ run::task#0 ]
Allocated zp[1]:7 [ set_bg::col#0 ]
Allocated zp[1]:8 [ set_border::col#0 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [3] main::$6 = main::i#2 << 1 [ main::i#2 main::$6 ] ( [ main::i#2 main::$6 ] { } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ]
Statement [4] main::$5 = main::$6 + main::i#2 [ main::i#2 main::$5 ] ( [ main::i#2 main::$5 ] { } ) always clobbers reg byte a
Statement [5] run::task#0 = tasks + main::$5 [ main::i#2 main::$5 ] ( [ main::i#2 main::$5 ] { } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:4 [ main::$5 ]
Statement [8] set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL) [ set_bg::col#0 ] ( [ set_bg::col#0 ] { } ) always clobbers reg byte a reg byte x
Statement [11] set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL) [ set_border::col#0 ] ( [ set_border::col#0 ] { } ) always clobbers reg byte a reg byte x
Statement [14] stackpush(byte) = ((byte*)tasks)[main::$5] [ ] ( run:6 [ main::i#2 ] { } ) always clobbers reg byte a
Statement [15] callexecute *(((void(byte)**)run::task#0)[OFFSET_STRUCT_TASK_HANDLER]) [ ] ( run:6 [ main::i#2 ] { } ) always clobbers reg byte a reg byte x reg byte y
Removing always clobbered register reg byte x as potential for zp[1]:2 [ main::i#2 main::i#1 ]
Removing always clobbered register reg byte y as potential for zp[1]:2 [ main::i#2 main::i#1 ]
Statement sideeffect stackpullbytes(1) always clobbers reg byte a
Statement [2] if(main::i#2<4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK) goto main::@2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
Statement [3] main::$6 = main::i#2 << 1 [ main::i#2 main::$6 ] ( [ main::i#2 main::$6 ] { } ) always clobbers reg byte a
Statement [4] main::$5 = main::$6 + main::i#2 [ main::i#2 main::$5 ] ( [ main::i#2 main::$5 ] { } ) always clobbers reg byte a
Statement [5] run::task#0 = tasks + main::$5 [ main::i#2 main::$5 ] ( [ main::i#2 main::$5 ] { } ) always clobbers reg byte a
Statement [8] set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL) [ set_bg::col#0 ] ( [ set_bg::col#0 ] { } ) always clobbers reg byte a reg byte x
Statement [11] set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL) [ set_border::col#0 ] ( [ set_border::col#0 ] { } ) always clobbers reg byte a reg byte x
Statement [14] stackpush(byte) = ((byte*)tasks)[main::$5] [ ] ( run:6 [ main::i#2 ] { } ) always clobbers reg byte a
Statement [15] callexecute *(((void(byte)**)run::task#0)[OFFSET_STRUCT_TASK_HANDLER]) [ ] ( run:6 [ main::i#2 ] { } ) always clobbers reg byte a reg byte x reg byte y
Statement sideeffect stackpullbytes(1) always clobbers reg byte a
Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 ,
Potential registers zp[1]:3 [ main::$6 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ main::$5 ] : zp[1]:4 , reg byte x , reg byte y ,
Potential registers zp[2]:5 [ run::task#0 ] : zp[2]:5 ,
Potential registers zp[1]:7 [ set_bg::col#0 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ set_border::col#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [main] 61.5: zp[1]:4 [ main::$5 ] 46.17: zp[1]:2 [ main::i#2 main::i#1 ] 22: zp[1]:3 [ main::$6 ]
Uplift Scope [run] 110: zp[2]:5 [ run::task#0 ]
Uplift Scope [set_border] 4: zp[1]:8 [ set_border::col#0 ]
Uplift Scope [set_bg] 4: zp[1]:7 [ set_bg::col#0 ]
Uplift Scope [Task]
Uplift Scope []
Uplifting [main] best 2669 combination reg byte x [ main::$5 ] zp[1]:2 [ main::i#2 main::i#1 ] reg byte a [ main::$6 ]
Uplifting [run] best 2669 combination zp[2]:5 [ run::task#0 ]
Uplifting [set_border] best 2663 combination reg byte a [ set_border::col#0 ]
Uplifting [set_bg] best 2657 combination reg byte a [ set_bg::col#0 ]
Uplifting [Task] best 2657 combination
Uplifting [] best 2657 combination
Attempting to uplift remaining variables inzp[1]:2 [ main::i#2 main::i#1 ]
Uplifting [main] best 2657 combination zp[1]:2 [ main::i#2 main::i#1 ]
Allocated (was zp[2]:5) zp[2]:3 [ run::task#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Calling a function pointer with return value
// Calling a function pointer inside a struct without *
// Upstart
// Commodore 64 PRG executable file
.file [name="function-pointer-return-3.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.const SIZEOF_STRUCT_TASK = 3
.const OFFSET_STRUCT_TASK_HANDLER = 1
.const STACK_BASE = $103
.label BORDER = $d020
.label BACKGROUND = $d021
.segment Code
// main
main: {
.label i = 2
// [1] phi from main main::@1 to main::@1 [phi:main/main::@1->main::@1]
__b1_from_main:
__b1_from___b1:
// [1] phi main::i#2 = 0 [phi:main/main::@1->main::@1#0] -- vbuz1=vbuc1
lda #0
sta.z i
jmp __b1
// main::@1
__b1:
// [2] if(main::i#2<4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK) goto main::@2 -- vbuz1_lt_vbuc1_then_la1
lda.z i
cmp #4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK
bcc __b2
jmp __b1_from___b1
// main::@2
__b2:
// [3] main::$6 = main::i#2 << 1 -- vbuaa=vbuz1_rol_1
lda.z i
asl
// [4] main::$5 = main::$6 + main::i#2 -- vbuxx=vbuaa_plus_vbuz1
clc
adc.z i
tax
// [5] run::task#0 = tasks + main::$5 -- pssz1=pssc1_plus_vbuxx
txa
clc
adc #<tasks
sta.z run.task
lda #>tasks
adc #0
sta.z run.task+1
// [6] call run
jsr run
jmp __b3
// main::@3
__b3:
// [7] main::i#1 = ++ main::i#2 -- vbuz1=_inc_vbuz1
inc.z i
// [1] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
__b1_from___b3:
// [1] phi main::i#2 = main::i#1 [phi:main::@3->main::@1#0] -- register_copy
jmp __b1
}
// set_bg
// set_bg(byte register(A) col)
set_bg: {
.const OFFSET_STACK_COL = 0
// [8] set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL) -- vbuaa=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_COL,x
// [9] *BACKGROUND = set_bg::col#0 -- _deref_pbuc1=vbuaa
sta BACKGROUND
jmp __breturn
// set_bg::@return
__breturn:
// [10] return
rts
}
// set_border
// set_border(byte register(A) col)
set_border: {
.const OFFSET_STACK_COL = 0
// [11] set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL) -- vbuaa=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_COL,x
// [12] *BORDER = set_border::col#0 -- _deref_pbuc1=vbuaa
sta BORDER
jmp __breturn
// set_border::@return
__breturn:
// [13] return
rts
}
// run
// run(struct Task* zp(3) task)
run: {
.label task = 3
// [14] stackpush(byte) = ((byte*)tasks)[main::$5] -- _stackpushbyte_=pbuc1_derefidx_vbuxx
lda tasks,x
pha
// [15] callexecute *(((void(byte)**)run::task#0)[OFFSET_STRUCT_TASK_HANDLER]) -- call__deref_(qprz1_derefidx_vbuc1)
ldy #OFFSET_STRUCT_TASK_HANDLER
lda (task),y
sta !+ +1
iny
lda (task),y
sta !+ +2
!:
jsr 0
// sideeffect stackpullbytes(1) -- _stackpullbyte_1
pla
jmp __breturn
// run::@return
__breturn:
// [17] return
rts
}
// File Data
.segment Data
tasks: .byte 0
.word set_border
.byte 0
.word set_bg
.byte 1
.word set_border
.byte 2
.word set_bg
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b3
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __b1_from_main:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction __b3:
Removing instruction __b1_from___b3:
Removing instruction __breturn:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Relabelling long label __b1_from___b1 to __b3
Succesful ASM optimization Pass5RelabelLongLabels
FINAL SYMBOL TABLE
constant byte* const BACKGROUND = (byte*) 53281
constant byte* const BORDER = (byte*) 53280
constant byte OFFSET_STRUCT_TASK_HANDLER = 1
constant byte SIZEOF_STRUCT_TASK = 3
constant word STACK_BASE = $103
void main()
byte~ main::$5 reg byte x 61.5
byte~ main::$6 reg byte a 22.0
byte main::i
byte main::i#1 i zp[1]:2 22.0
byte main::i#2 i zp[1]:2 24.16666666666666
void run(struct Task* run::task)
struct Task* run::task
struct Task* run::task#0 task zp[2]:3 110.0
__stackcall void set_bg(byte set_bg::col)
constant byte set_bg::OFFSET_STACK_COL = 0
byte set_bg::col
byte set_bg::col#0 reg byte a 4.0
__stackcall void set_border(byte set_border::col)
constant byte set_border::OFFSET_STACK_COL = 0
byte set_border::col
byte set_border::col#0 reg byte a 4.0
constant struct Task* tasks[] = { { param: 0, handler: &set_border }, { param: 0, handler: &set_bg }, { param: 1, handler: &set_border }, { param: 2, handler: &set_bg } }
zp[1]:2 [ main::i#2 main::i#1 ]
reg byte a [ main::$6 ]
reg byte x [ main::$5 ]
zp[2]:3 [ run::task#0 ]
reg byte a [ set_bg::col#0 ]
reg byte a [ set_border::col#0 ]
FINAL ASSEMBLER
Score: 2318
// File Comments
// Calling a function pointer with return value
// Calling a function pointer inside a struct without *
// Upstart
// Commodore 64 PRG executable file
.file [name="function-pointer-return-3.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.const SIZEOF_STRUCT_TASK = 3
.const OFFSET_STRUCT_TASK_HANDLER = 1
.const STACK_BASE = $103
.label BORDER = $d020
.label BACKGROUND = $d021
.segment Code
// main
main: {
.label i = 2
// [1] phi from main main::@1 to main::@1 [phi:main/main::@1->main::@1]
__b3:
// [1] phi main::i#2 = 0 [phi:main/main::@1->main::@1#0] -- vbuz1=vbuc1
lda #0
sta.z i
// main::@1
__b1:
// for(char i=0; i < sizeof(tasks)/sizeof(struct Task); i++)
// [2] if(main::i#2<4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK) goto main::@2 -- vbuz1_lt_vbuc1_then_la1
lda.z i
cmp #4*SIZEOF_STRUCT_TASK/SIZEOF_STRUCT_TASK
bcc __b2
jmp __b3
// main::@2
__b2:
// tasks+i
// [3] main::$6 = main::i#2 << 1 -- vbuaa=vbuz1_rol_1
lda.z i
asl
// [4] main::$5 = main::$6 + main::i#2 -- vbuxx=vbuaa_plus_vbuz1
clc
adc.z i
tax
// run(tasks+i)
// [5] run::task#0 = tasks + main::$5 -- pssz1=pssc1_plus_vbuxx
txa
clc
adc #<tasks
sta.z run.task
lda #>tasks
adc #0
sta.z run.task+1
// [6] call run
jsr run
// main::@3
// for(char i=0; i < sizeof(tasks)/sizeof(struct Task); i++)
// [7] main::i#1 = ++ main::i#2 -- vbuz1=_inc_vbuz1
inc.z i
// [1] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
// [1] phi main::i#2 = main::i#1 [phi:main::@3->main::@1#0] -- register_copy
jmp __b1
}
// set_bg
// set_bg(byte register(A) col)
set_bg: {
.const OFFSET_STACK_COL = 0
// [8] set_bg::col#0 = stackidx(byte,set_bg::OFFSET_STACK_COL) -- vbuaa=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_COL,x
// *BACKGROUND = col
// [9] *BACKGROUND = set_bg::col#0 -- _deref_pbuc1=vbuaa
sta BACKGROUND
// set_bg::@return
// }
// [10] return
rts
}
// set_border
// set_border(byte register(A) col)
set_border: {
.const OFFSET_STACK_COL = 0
// [11] set_border::col#0 = stackidx(byte,set_border::OFFSET_STACK_COL) -- vbuaa=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_COL,x
// *BORDER = col
// [12] *BORDER = set_border::col#0 -- _deref_pbuc1=vbuaa
sta BORDER
// set_border::@return
// }
// [13] return
rts
}
// run
// run(struct Task* zp(3) task)
run: {
.label task = 3
// task->handler(task->param)
// [14] stackpush(byte) = ((byte*)tasks)[main::$5] -- _stackpushbyte_=pbuc1_derefidx_vbuxx
lda tasks,x
pha
// [15] callexecute *(((void(byte)**)run::task#0)[OFFSET_STRUCT_TASK_HANDLER]) -- call__deref_(qprz1_derefidx_vbuc1)
ldy #OFFSET_STRUCT_TASK_HANDLER
lda (task),y
sta !+ +1
iny
lda (task),y
sta !+ +2
!:
jsr 0
// sideeffect stackpullbytes(1) -- _stackpullbyte_1
pla
// run::@return
// }
// [17] return
rts
}
// File Data
.segment Data
tasks: .byte 0
.word set_border
.byte 0
.word set_bg
.byte 1
.word set_border
.byte 2
.word set_bg

View File

@ -0,0 +1,30 @@
constant byte* const BACKGROUND = (byte*) 53281
constant byte* const BORDER = (byte*) 53280
constant byte OFFSET_STRUCT_TASK_HANDLER = 1
constant byte SIZEOF_STRUCT_TASK = 3
constant word STACK_BASE = $103
void main()
byte~ main::$5 reg byte x 61.5
byte~ main::$6 reg byte a 22.0
byte main::i
byte main::i#1 i zp[1]:2 22.0
byte main::i#2 i zp[1]:2 24.16666666666666
void run(struct Task* run::task)
struct Task* run::task
struct Task* run::task#0 task zp[2]:3 110.0
__stackcall void set_bg(byte set_bg::col)
constant byte set_bg::OFFSET_STACK_COL = 0
byte set_bg::col
byte set_bg::col#0 reg byte a 4.0
__stackcall void set_border(byte set_border::col)
constant byte set_border::OFFSET_STACK_COL = 0
byte set_border::col
byte set_border::col#0 reg byte a 4.0
constant struct Task* tasks[] = { { param: 0, handler: &set_border }, { param: 0, handler: &set_bg }, { param: 1, handler: &set_border }, { param: 2, handler: &set_bg } }
zp[1]:2 [ main::i#2 main::i#1 ]
reg byte a [ main::$6 ]
reg byte x [ main::$5 ]
zp[2]:3 [ run::task#0 ]
reg byte a [ set_bg::col#0 ]
reg byte a [ set_border::col#0 ]