1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-07 06:37:31 +00:00

Fixed relative labels in KickAss 5.9 #272

ASM  now uses literal encoded chars for better readability #245
This commit is contained in:
jespergravgaard 2019-08-18 23:18:53 +02:00
parent 21e4638857
commit 19c0637f1e
18 changed files with 913 additions and 58 deletions

View File

@ -7,9 +7,10 @@ import dk.camelot64.kickc.model.symbols.ConstantVar;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.types.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.values.*;
import kickass.nonasm.c64.CharToPetsciiConverter;
/** Formatting of numbers, constants, names and more for KickAssembler */
public class AsmFormat {
@ -32,29 +33,14 @@ public class AsmFormat {
} else if(value instanceof ConstantBool) {
return getAsmBool(((ConstantBool) value).getBool());
} else if(value instanceof ConstantChar) {
ConstantChar constantChar = (ConstantChar) value;
if(!ConstantString.Encoding.SCREENCODE_MIXED.equals(constantChar.getEncoding())) {
// Current KickAsm does not support encoded literal chars
// Manually convert literal chars in non-standard encodings
CharToPetsciiConverter.setCurrentEncoding(constantChar.getEncoding().name);
byte converted = CharToPetsciiConverter.convertOrChar(constantChar.getChar(), true);
return getAsmNumber(new Long(converted));
} else {
String escapedChar = ConstantChar.asciiToCharEscape(((ConstantChar) value).getChar());
if(escapedChar.length()>1) {
// Currently KickAss does not support escaped characters - so instead we must output the number value instead
CharToPetsciiConverter.setCurrentEncoding(constantChar.getEncoding().name);
byte converted = CharToPetsciiConverter.convertOrChar(constantChar.getChar(), true);
return getAsmNumber(new Long(converted));
} else {
return "'" + escapedChar + "'";
}
}
ConstantChar constantChar = (ConstantChar) value;
String escapedChar = ConstantChar.asciiToCharEscape(constantChar.getChar());
return "'" + escapedChar + "'";
} else if(value instanceof ConstantString) {
String stringValue = ((ConstantString) value).getValue();
String escapedString = ConstantString.asciiToStringEscape(stringValue);
boolean hasEscape = !stringValue.equals(escapedString);
return (hasEscape?"@":"")+"\"" + escapedString + "\"";
return (hasEscape ? "@" : "") + "\"" + escapedString + "\"";
} else if(value instanceof ConstantUnary) {
ConstantUnary unary = (ConstantUnary) value;
Operator operator = unary.getOperator();
@ -151,15 +137,15 @@ public class AsmFormat {
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
ConstantLiteral constantLiteral = operand.calculateLiteral(program.getScope());
if(constantLiteral instanceof ConstantInteger && Operators.CAST_WORD.equals(operator)&& SymbolType.WORD.contains(((ConstantInteger) constantLiteral).getValue())) {
if(constantLiteral instanceof ConstantInteger && Operators.CAST_WORD.equals(operator) && SymbolType.WORD.contains(((ConstantInteger) constantLiteral).getValue())) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
if(constantLiteral instanceof ConstantInteger && Operators.CAST_SWORD.equals(operator)&& SymbolType.SWORD.contains(((ConstantInteger) constantLiteral).getValue())) {
if(constantLiteral instanceof ConstantInteger && Operators.CAST_SWORD.equals(operator) && SymbolType.SWORD.contains(((ConstantInteger) constantLiteral).getValue())) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
if(constantLiteral instanceof ConstantInteger && (operator instanceof OperatorCastPtr) && SymbolType.WORD.contains(((ConstantInteger) constantLiteral).getValue())) {
if(constantLiteral instanceof ConstantInteger && (operator instanceof OperatorCastPtr) && SymbolType.WORD.contains(((ConstantInteger) constantLiteral).getValue())) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
@ -172,11 +158,11 @@ public class AsmFormat {
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
ConstantLiteral constantLiteral = operand.calculateLiteral(program.getScope());
if(constantLiteral instanceof ConstantInteger && Operators.CAST_DWORD.equals(operator)&& SymbolType.DWORD.contains(((ConstantInteger) constantLiteral).getValue())) {
if(constantLiteral instanceof ConstantInteger && Operators.CAST_DWORD.equals(operator) && SymbolType.DWORD.contains(((ConstantInteger) constantLiteral).getValue())) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
if(constantLiteral instanceof ConstantInteger && Operators.CAST_SDWORD.equals(operator)&& SymbolType.SDWORD.contains(((ConstantInteger) constantLiteral).getValue())) {
if(constantLiteral instanceof ConstantInteger && Operators.CAST_SDWORD.equals(operator) && SymbolType.SDWORD.contains(((ConstantInteger) constantLiteral).getValue())) {
// No cast needed
return getAsmConstant(program, operand, outerPrecedence, codeScope);
}
@ -251,8 +237,8 @@ public class AsmFormat {
if(number.longValue() >= 0L && number.longValue() <= 255L) {
return SHORT_ASM_NUMBERS[number.intValue()];
} else {
if(number.longValue()<0) {
return "-"+getAsmNumber(-number.longValue());
if(number.longValue() < 0) {
return "-" + getAsmNumber(-number.longValue());
} else {
return String.format("$%x", number.longValue());
}

View File

@ -273,9 +273,16 @@ public class AsmFragmentInstance {
public AsmParameter visitAsmExprBinary(KickCParser.AsmExprBinaryContext ctx) {
AsmParameter left = (AsmParameter) this.visit(ctx.asmExpr(0));
AsmParameter right = (AsmParameter) this.visit(ctx.asmExpr(1));
String param = "" + left.getParam() + ctx.getChild(1).getText() + right.getParam();
StringBuilder param = new StringBuilder();
param.append(left.getParam());
if(ctx.asmExpr(0) instanceof KickCParser.AsmExprLabelRelContext) {
// Add an extra space if we are doing a binary expression with a relative label as the left part
param.append(" ");
}
param.append(ctx.getChild(1).getText());
param.append(right.getParam());
boolean zp = left.isZp() && right.isZp();
return new AsmParameter(param, zp);
return new AsmParameter(param.toString(), zp);
}
@Override

View File

@ -242,6 +242,7 @@ public class ProgramValueIterator {
value instanceof ConstantLiteral ||
value instanceof ConstantRef ||
value instanceof StructZero ||
value instanceof Label ||
value instanceof LabelRef
) {
// No sub values

View File

@ -672,6 +672,7 @@ public class Pass4CodeGeneration {
}
StatementAssignment assignment = (StatementAssignment) statement;
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(assignment, assignmentAlu, program);
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
aluState.clear();
return;
@ -703,6 +704,7 @@ public class Pass4CodeGeneration {
}
} else if(statement instanceof StatementConditionalJump) {
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory((StatementConditionalJump) statement, block, program, getGraph());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
} else if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
@ -981,6 +983,7 @@ public class Pass4CodeGeneration {
asm.getCurrentChunk().setFragment("register_copy");
} else {
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(lValue, rValue, program, scope);
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
}
}

View File

@ -51,6 +51,11 @@ public class TestPrograms {
assertError("string-escapes-err-0", "Unfinished string escape sequence at end of string");
}
@Test
public void testStringEscapes3() throws IOException, URISyntaxException {
compileAndCompare("string-escapes-3");
}
@Test
public void testStringEscapes2() throws IOException, URISyntaxException {
compileAndCompare("string-escapes-2");

View File

@ -0,0 +1,26 @@
// Test using some simple supported string escape \n in both string and char
// Uses encoding PETSCII mixed
#pragma encoding(petscii_mixed)
char[] MESSAGE = "hello\nworld";
char* SCREEN = 0x0400;
void main() {
byte* line = 0x0400;
byte* cursor = line;
byte* msg = MESSAGE;
while(*msg) {
switch(*msg) {
case '\n':
line += 0x28;
cursor = line;
break;
default:
*cursor++ = *msg & 0x3f;
}
msg++;
}
}

View File

@ -1032,10 +1032,10 @@ processChars: {
ldx #' '
ldy #OFFSET_STRUCT_PROCESSINGSPRITE_SCREENPTR
lda (processing),y
sta !++1
sta !+ +1
iny
lda (processing),y
sta !++2
sta !+ +2
txa
!:
sta $ffff

View File

@ -7168,10 +7168,10 @@ processChars: {
ldx #' '
ldy #OFFSET_STRUCT_PROCESSINGSPRITE_SCREENPTR
lda (processing),y
sta !++1
sta !+ +1
iny
lda (processing),y
sta !++2
sta !+ +2
txa
!:
sta $ffff
@ -10002,10 +10002,10 @@ processChars: {
ldx #' '
ldy #OFFSET_STRUCT_PROCESSINGSPRITE_SCREENPTR
lda (processing),y
sta !++1
sta !+ +1
iny
lda (processing),y
sta !++2
sta !+ +2
txa
!:
sta $ffff
@ -12867,10 +12867,10 @@ processChars: {
ldx #' '
ldy #OFFSET_STRUCT_PROCESSINGSPRITE_SCREENPTR
lda (processing),y
sta !++1
sta !+ +1
iny
lda (processing),y
sta !++2
sta !+ +2
txa
!:
sta $ffff

View File

@ -3,13 +3,13 @@
:BasicUpstart(main)
.pc = $80d "Program"
.encoding "petscii_mixed"
.const cpm = -$3f
.const cpm = 'A'
.encoding "petscii_upper"
.const cpu = $41
.const cpu = 'A'
.encoding "screencode_mixed"
.const csm = 'A'
.encoding "screencode_upper"
.const csu = 1
.const csu = 'A'
.label screen = $400
main: {
lda #cpm

View File

@ -253,13 +253,13 @@ Target platform is c64basic
.pc = $80d "Program"
// Global Constants & labels
.encoding "petscii_mixed"
.const cpm = -$3f
.const cpm = 'A'
.encoding "petscii_upper"
.const cpu = $41
.const cpu = 'A'
.encoding "screencode_mixed"
.const csm = 'A'
.encoding "screencode_upper"
.const csu = 1
.const csu = 'A'
.label screen = $400
// @begin
bbegin:
@ -347,13 +347,13 @@ ASSEMBLER BEFORE OPTIMIZATION
.pc = $80d "Program"
// Global Constants & labels
.encoding "petscii_mixed"
.const cpm = -$3f
.const cpm = 'A'
.encoding "petscii_upper"
.const cpu = $41
.const cpu = 'A'
.encoding "screencode_mixed"
.const csm = 'A'
.encoding "screencode_upper"
.const csu = 1
.const csu = 'A'
.label screen = $400
// @begin
bbegin:
@ -472,13 +472,13 @@ Score: 62
.pc = $80d "Program"
// Global Constants & labels
.encoding "petscii_mixed"
.const cpm = -$3f
.const cpm = 'A'
.encoding "petscii_upper"
.const cpu = $41
.const cpu = 'A'
.encoding "screencode_mixed"
.const csm = 'A'
.encoding "screencode_upper"
.const csu = 1
.const csu = 'A'
.label screen = $400
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]

View File

@ -3,7 +3,7 @@
.pc = $80d "Program"
main: {
.encoding "petscii_mixed"
lda #$45
lda #'e'
sta strTemp+2
lda #0
sta strTemp+3

View File

@ -141,7 +141,7 @@ bend:
main: {
// [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'pm -- _deref_pbuc1=vbuc2
.encoding "petscii_mixed"
lda #$45
lda #'e'
sta strTemp+2
// [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2
lda #0
@ -202,7 +202,7 @@ bend:
main: {
// [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'pm -- _deref_pbuc1=vbuc2
.encoding "petscii_mixed"
lda #$45
lda #'e'
sta strTemp+2
// [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2
lda #0
@ -277,7 +277,7 @@ main: {
// strTemp[2] = 'e'
// [4] *((const byte[]) strTemp#0+(byte) 2) ← (byte) 'e'pm -- _deref_pbuc1=vbuc2
.encoding "petscii_mixed"
lda #$45
lda #'e'
sta strTemp+2
// strTemp[3] = 0
// [5] *((const byte[]) strTemp#0+(byte) 3) ← (byte) 0 -- _deref_pbuc1=vbuc2

View File

@ -25,7 +25,7 @@ main: {
bne b2
rts
b2:
lda #-$33
lda #'\n'
ldy #0
cmp (msg),y
beq b3

View File

@ -293,7 +293,7 @@ main: {
// main::@2
b2:
// [8] if(*((byte*) main::msg#2)==(byte) ' ') goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1
lda #-$33
lda #'\n'
ldy #0
cmp (msg),y
beq b3
@ -430,7 +430,7 @@ main: {
// main::@2
b2:
// [8] if(*((byte*) main::msg#2)==(byte) ' ') goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1
lda #-$33
lda #'\n'
ldy #0
cmp (msg),y
beq b3
@ -608,7 +608,7 @@ main: {
// cursor = line;
// break;
// [8] if(*((byte*) main::msg#2)==(byte) ' ') goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1
lda #-$33
lda #'\n'
ldy #0
cmp (msg),y
beq b3

View File

@ -0,0 +1,62 @@
// Test using some simple supported string escape \n in both string and char
// Uses encoding PETSCII mixed
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
main: {
.label cursor = 6
.label msg = 2
.label line = 4
lda #<$400
sta.z cursor
lda #>$400
sta.z cursor+1
lda #<$400
sta.z line
lda #>$400
sta.z line+1
lda #<MESSAGE
sta.z msg
lda #>MESSAGE
sta.z msg+1
b1:
ldy #0
lda (msg),y
cmp #0
bne b2
rts
b2:
.encoding "petscii_mixed"
lda #'\n'
ldy #0
cmp (msg),y
beq b3
lda #$3f
and (msg),y
sta (cursor),y
inc.z cursor
bne !+
inc.z cursor+1
!:
b5:
inc.z msg
bne !+
inc.z msg+1
!:
jmp b1
b3:
lda #$28
clc
adc.z line
sta.z cursor
lda #0
adc.z line+1
sta.z cursor+1
lda.z cursor
sta.z line
lda.z cursor+1
sta.z line+1
jmp b5
}
MESSAGE: .text @"hello\nworld"
.byte 0

View File

@ -0,0 +1,39 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] phi()
to:main::@1
main::@1: scope:[main] from main main::@5
[5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@5/(byte*) main::cursor#6 )
[5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@5/(byte*) main::line#5 )
[5] (byte*) main::msg#2 ← phi( main/(const byte[]) MESSAGE#0 main::@5/(byte*) main::msg#1 )
[6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2
to:main::@return
main::@return: scope:[main] from main::@1
[7] return
to:@return
main::@2: scope:[main] from main::@1
[8] if(*((byte*) main::msg#2)==(byte) '
'pm) goto main::@3
to:main::@4
main::@4: scope:[main] from main::@2
[9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f
[10] *((byte*) main::cursor#3) ← (byte~) main::$0
[11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3
to:main::@5
main::@5: scope:[main] from main::@3 main::@4
[12] (byte*) main::cursor#6 ← phi( main::@3/(byte*) main::cursor#1 main::@4/(byte*) main::cursor#2 )
[12] (byte*) main::line#5 ← phi( main::@3/(byte*~) main::line#8 main::@4/(byte*) main::line#2 )
[13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2
to:main::@1
main::@3: scope:[main] from main::@2
[14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28
[15] (byte*~) main::line#8 ← (byte*) main::cursor#1
to:main::@5

View File

@ -0,0 +1,695 @@
Warning! Adding boolean cast to non-boolean condition *((byte*) main::msg)
Identified constant variable (byte*) SCREEN
Culled Empty Block (label) main::@9
Culled Empty Block (label) main::@3
Culled Empty Block (label) main::@10
Culled Empty Block (label) main::@11
Culled Empty Block (label) main::@12
Culled Empty Block (label) main::@7
Culled Empty Block (label) main::@8
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte[]) MESSAGE#0 ← (const string) $0
to:@1
main: scope:[main] from @1
(byte*) main::line#0 ← ((byte*)) (number) $400
(byte*) main::cursor#0 ← (byte*) main::line#0
(byte*) main::msg#0 ← (byte[]) MESSAGE#0
to:main::@1
main::@1: scope:[main] from main main::@6
(byte*) main::cursor#5 ← phi( main/(byte*) main::cursor#0 main::@6/(byte*) main::cursor#6 )
(byte*) main::line#4 ← phi( main/(byte*) main::line#0 main::@6/(byte*) main::line#5 )
(byte*) main::msg#2 ← phi( main/(byte*) main::msg#0 main::@6/(byte*) main::msg#1 )
(bool~) main::$1 ← (number) 0 != *((byte*) main::msg#2)
if((bool~) main::$1) goto main::@2
to:main::@return
main::@2: scope:[main] from main::@1
(byte*) main::cursor#4 ← phi( main::@1/(byte*) main::cursor#5 )
(byte*) main::line#3 ← phi( main::@1/(byte*) main::line#4 )
(byte*) main::msg#3 ← phi( main::@1/(byte*) main::msg#2 )
if(*((byte*) main::msg#3)==(byte) '
'pm) goto main::@4
to:main::@5
main::@4: scope:[main] from main::@2
(byte*) main::msg#6 ← phi( main::@2/(byte*) main::msg#3 )
(byte*) main::line#2 ← phi( main::@2/(byte*) main::line#3 )
(byte*) main::line#1 ← (byte*) main::line#2 + (number) $28
(byte*) main::cursor#1 ← (byte*) main::line#1
to:main::@6
main::@5: scope:[main] from main::@2
(byte*) main::line#6 ← phi( main::@2/(byte*) main::line#3 )
(byte*) main::cursor#3 ← phi( main::@2/(byte*) main::cursor#4 )
(byte*) main::msg#4 ← phi( main::@2/(byte*) main::msg#3 )
(number~) main::$0 ← *((byte*) main::msg#4) & (number) $3f
*((byte*) main::cursor#3) ← (number~) main::$0
(byte*) main::cursor#2 ← ++ (byte*) main::cursor#3
to:main::@6
main::@6: scope:[main] from main::@4 main::@5
(byte*) main::cursor#6 ← phi( main::@4/(byte*) main::cursor#1 main::@5/(byte*) main::cursor#2 )
(byte*) main::line#5 ← phi( main::@4/(byte*) main::line#1 main::@5/(byte*) main::line#6 )
(byte*) main::msg#5 ← phi( main::@4/(byte*) main::msg#6 main::@5/(byte*) main::msg#4 )
(byte*) main::msg#1 ← ++ (byte*) main::msg#5
to:main::@1
main::@return: scope:[main] from main::@1
return
to:@return
@1: scope:[] from @begin
call main
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
SYMBOL TABLE SSA
(const string) $0 = (string) "hello
world"pm
(label) @1
(label) @2
(label) @begin
(label) @end
(byte[]) MESSAGE
(byte[]) MESSAGE#0
(void()) main()
(number~) main::$0
(bool~) main::$1
(label) main::@1
(label) main::@2
(label) main::@4
(label) main::@5
(label) main::@6
(label) main::@return
(byte*) main::cursor
(byte*) main::cursor#0
(byte*) main::cursor#1
(byte*) main::cursor#2
(byte*) main::cursor#3
(byte*) main::cursor#4
(byte*) main::cursor#5
(byte*) main::cursor#6
(byte*) main::line
(byte*) main::line#0
(byte*) main::line#1
(byte*) main::line#2
(byte*) main::line#3
(byte*) main::line#4
(byte*) main::line#5
(byte*) main::line#6
(byte*) main::msg
(byte*) main::msg#0
(byte*) main::msg#1
(byte*) main::msg#2
(byte*) main::msg#3
(byte*) main::msg#4
(byte*) main::msg#5
(byte*) main::msg#6
Adding number conversion cast (unumber) 0 in (bool~) main::$1 ← (number) 0 != *((byte*) main::msg#2)
Adding number conversion cast (unumber) $28 in (byte*) main::line#1 ← (byte*) main::line#2 + (number) $28
Adding number conversion cast (unumber) $3f in (number~) main::$0 ← *((byte*) main::msg#4) & (number) $3f
Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← *((byte*) main::msg#4) & (unumber)(number) $3f
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte*) main::line#0 ← (byte*)(number) $400
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 0
Simplifying constant integer cast $28
Simplifying constant integer cast $3f
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 0
Finalized unsigned number type (byte) $28
Finalized unsigned number type (byte) $3f
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$0 ← *((byte*) main::msg#4) & (byte) $3f
Alias (byte*) main::cursor#0 = (byte*) main::line#0
Alias (byte*) main::msg#2 = (byte*) main::msg#3 (byte*) main::msg#6 (byte*) main::msg#4
Alias (byte*) main::line#2 = (byte*) main::line#3 (byte*) main::line#4 (byte*) main::line#6
Alias (byte*) main::cursor#3 = (byte*) main::cursor#4 (byte*) main::cursor#5
Alias (byte*) main::cursor#1 = (byte*) main::line#1
Successful SSA optimization Pass2AliasElimination
Alias (byte*) main::msg#2 = (byte*) main::msg#5
Successful SSA optimization Pass2AliasElimination
Simple Condition (bool~) main::$1 [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant (const byte[]) MESSAGE#0 = $0
Constant (const byte*) main::cursor#0 = (byte*) 1024
Successful SSA optimization Pass2ConstantIdentification
Constant (const byte*) main::msg#0 = MESSAGE#0
Successful SSA optimization Pass2ConstantIdentification
Inlining constant with var siblings (const byte*) main::cursor#0
Inlining constant with var siblings (const byte*) main::msg#0
Constant inlined $0 = (const byte[]) MESSAGE#0
Constant inlined main::cursor#0 = (byte*) 1024
Constant inlined main::msg#0 = (const byte[]) MESSAGE#0
Successful SSA optimization Pass2ConstantInlining
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @2
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
CALL GRAPH
Calls in [] to main:2
Created 5 initial phi equivalence classes
Coalesced [13] main::line#9 ← main::line#2
Coalesced [14] main::cursor#9 ← main::cursor#2
Coalesced [17] main::msg#7 ← main::msg#1
Coalesced (already) [18] main::line#7 ← main::line#5
Coalesced [19] main::cursor#7 ← main::cursor#6
Not coalescing [21] main::line#8 ← main::cursor#1
Coalesced [22] main::cursor#8 ← main::cursor#1
Coalesced down to 3 phi equivalence classes
Culled Empty Block (label) @2
Renumbering block main::@4 to main::@3
Renumbering block main::@5 to main::@4
Renumbering block main::@6 to main::@5
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] phi()
to:main::@1
main::@1: scope:[main] from main main::@5
[5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@5/(byte*) main::cursor#6 )
[5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@5/(byte*) main::line#5 )
[5] (byte*) main::msg#2 ← phi( main/(const byte[]) MESSAGE#0 main::@5/(byte*) main::msg#1 )
[6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2
to:main::@return
main::@return: scope:[main] from main::@1
[7] return
to:@return
main::@2: scope:[main] from main::@1
[8] if(*((byte*) main::msg#2)==(byte) '
'pm) goto main::@3
to:main::@4
main::@4: scope:[main] from main::@2
[9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f
[10] *((byte*) main::cursor#3) ← (byte~) main::$0
[11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3
to:main::@5
main::@5: scope:[main] from main::@3 main::@4
[12] (byte*) main::cursor#6 ← phi( main::@3/(byte*) main::cursor#1 main::@4/(byte*) main::cursor#2 )
[12] (byte*) main::line#5 ← phi( main::@3/(byte*~) main::line#8 main::@4/(byte*) main::line#2 )
[13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2
to:main::@1
main::@3: scope:[main] from main::@2
[14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28
[15] (byte*~) main::line#8 ← (byte*) main::cursor#1
to:main::@5
VARIABLE REGISTER WEIGHTS
(byte[]) MESSAGE
(void()) main()
(byte~) main::$0 22.0
(byte*) main::cursor
(byte*) main::cursor#1 16.5
(byte*) main::cursor#2 22.0
(byte*) main::cursor#3 6.6000000000000005
(byte*) main::cursor#6 16.5
(byte*) main::line
(byte*) main::line#2 5.5
(byte*) main::line#5 16.5
(byte*~) main::line#8 22.0
(byte*) main::msg
(byte*) main::msg#1 22.0
(byte*) main::msg#2 6.111111111111112
Initial phi equivalence classes
[ main::msg#2 main::msg#1 ]
[ main::line#2 main::line#5 main::line#8 ]
[ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ]
Added variable main::$0 to zero page equivalence class [ main::$0 ]
Complete equivalence classes
[ main::msg#2 main::msg#1 ]
[ main::line#2 main::line#5 main::line#8 ]
[ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ]
[ main::$0 ]
Allocated zp ZP_WORD:2 [ main::msg#2 main::msg#1 ]
Allocated zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ]
Allocated zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ]
Allocated zp ZP_BYTE:8 [ main::$0 ]
INITIAL ASM
Target platform is c64basic
// File Comments
// Test using some simple supported string escape \n in both string and char
// Uses encoding PETSCII mixed
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _0 = 8
.label cursor = 6
.label msg = 2
.label line = 4
// [5] phi from main to main::@1 [phi:main->main::@1]
b1_from_main:
// [5] phi (byte*) main::cursor#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1
lda #<$400
sta.z cursor
lda #>$400
sta.z cursor+1
// [5] phi (byte*) main::line#2 = (byte*) 1024 [phi:main->main::@1#1] -- pbuz1=pbuc1
lda #<$400
sta.z line
lda #>$400
sta.z line+1
// [5] phi (byte*) main::msg#2 = (const byte[]) MESSAGE#0 [phi:main->main::@1#2] -- pbuz1=pbuc1
lda #<MESSAGE
sta.z msg
lda #>MESSAGE
sta.z msg+1
jmp b1
// main::@1
b1:
// [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 -- vbuc1_neq__deref_pbuz1_then_la1
ldy #0
lda (msg),y
cmp #0
bne b2
jmp breturn
// main::@return
breturn:
// [7] return
rts
// main::@2
b2:
// [8] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1
.encoding "petscii_mixed"
lda #'\n'
ldy #0
cmp (msg),y
beq b3
jmp b4
// main::@4
b4:
// [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuz1=_deref_pbuz2_band_vbuc1
lda #$3f
ldy #0
and (msg),y
sta.z _0
// [10] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuz2
lda.z _0
ldy #0
sta (cursor),y
// [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1
inc.z cursor
bne !+
inc.z cursor+1
!:
// [12] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5]
b5_from_b3:
b5_from_b4:
// [12] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy
// [12] phi (byte*) main::line#5 = (byte*~) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy
jmp b5
// main::@5
b5:
// [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1
inc.z msg
bne !+
inc.z msg+1
!:
// [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1]
b1_from_b5:
// [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy
// [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy
// [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy
jmp b1
// main::@3
b3:
// [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1
lda #$28
clc
adc.z line
sta.z cursor
lda #0
adc.z line+1
sta.z cursor+1
// [15] (byte*~) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2
lda.z cursor
sta.z line
lda.z cursor+1
sta.z line+1
jmp b5_from_b3
}
// File Data
MESSAGE: .text @"hello\nworld"
.byte 0
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] ) always clobbers reg byte a reg byte y
Statement [8] if(*((byte*) main::msg#2)==(byte) '
'pm) goto main::@3 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] ) always clobbers reg byte a reg byte y
Statement [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f [ main::msg#2 main::line#2 main::cursor#3 main::$0 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 main::$0 ] ) always clobbers reg byte a reg byte y
Statement [10] *((byte*) main::cursor#3) ← (byte~) main::$0 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] ) always clobbers reg byte y
Statement [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 [ main::msg#2 main::cursor#1 ] ( main:2 [ main::msg#2 main::cursor#1 ] ) always clobbers reg byte a
Statement [15] (byte*~) main::line#8 ← (byte*) main::cursor#1 [ main::msg#2 main::line#8 main::cursor#1 ] ( main:2 [ main::msg#2 main::line#8 main::cursor#1 ] ) always clobbers reg byte a
Potential registers zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] : zp ZP_WORD:2 ,
Potential registers zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] : zp ZP_WORD:4 ,
Potential registers zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] : zp ZP_WORD:6 ,
Potential registers zp ZP_BYTE:8 [ main::$0 ] : zp ZP_BYTE:8 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [main] 61.6: zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] 44: zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] 28.11: zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] 22: zp ZP_BYTE:8 [ main::$0 ]
Uplift Scope []
Uplifting [main] best 1463 combination zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] reg byte a [ main::$0 ]
Uplifting [] best 1463 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test using some simple supported string escape \n in both string and char
// Uses encoding PETSCII mixed
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label cursor = 6
.label msg = 2
.label line = 4
// [5] phi from main to main::@1 [phi:main->main::@1]
b1_from_main:
// [5] phi (byte*) main::cursor#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1
lda #<$400
sta.z cursor
lda #>$400
sta.z cursor+1
// [5] phi (byte*) main::line#2 = (byte*) 1024 [phi:main->main::@1#1] -- pbuz1=pbuc1
lda #<$400
sta.z line
lda #>$400
sta.z line+1
// [5] phi (byte*) main::msg#2 = (const byte[]) MESSAGE#0 [phi:main->main::@1#2] -- pbuz1=pbuc1
lda #<MESSAGE
sta.z msg
lda #>MESSAGE
sta.z msg+1
jmp b1
// main::@1
b1:
// [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 -- vbuc1_neq__deref_pbuz1_then_la1
ldy #0
lda (msg),y
cmp #0
bne b2
jmp breturn
// main::@return
breturn:
// [7] return
rts
// main::@2
b2:
// [8] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1
.encoding "petscii_mixed"
lda #'\n'
ldy #0
cmp (msg),y
beq b3
jmp b4
// main::@4
b4:
// [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuaa=_deref_pbuz1_band_vbuc1
lda #$3f
ldy #0
and (msg),y
// [10] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuaa
ldy #0
sta (cursor),y
// [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1
inc.z cursor
bne !+
inc.z cursor+1
!:
// [12] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5]
b5_from_b3:
b5_from_b4:
// [12] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy
// [12] phi (byte*) main::line#5 = (byte*~) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy
jmp b5
// main::@5
b5:
// [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1
inc.z msg
bne !+
inc.z msg+1
!:
// [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1]
b1_from_b5:
// [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy
// [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy
// [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy
jmp b1
// main::@3
b3:
// [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1
lda #$28
clc
adc.z line
sta.z cursor
lda #0
adc.z line+1
sta.z cursor+1
// [15] (byte*~) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2
lda.z cursor
sta.z line
lda.z cursor+1
sta.z line+1
jmp b5_from_b3
}
// File Data
MESSAGE: .text @"hello\nworld"
.byte 0
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp b1
Removing instruction jmp breturn
Removing instruction jmp b4
Removing instruction jmp b5
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction ldy #0
Succesful ASM optimization Pass5UnnecesaryLoadElimination
Replacing label b5_from_b3 with b5
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
Removing instruction bend_from_b1:
Removing instruction b5_from_b3:
Removing instruction b5_from_b4:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction b1_from_main:
Removing instruction breturn:
Removing instruction b4:
Removing instruction b1_from_b5:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction ldy #0
Succesful ASM optimization Pass5UnnecesaryLoadElimination
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(byte[]) MESSAGE
(const byte[]) MESSAGE#0 MESSAGE = (string) "hello
world"pm
(void()) main()
(byte~) main::$0 reg byte a 22.0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@4
(label) main::@5
(label) main::@return
(byte*) main::cursor
(byte*) main::cursor#1 cursor zp ZP_WORD:6 16.5
(byte*) main::cursor#2 cursor zp ZP_WORD:6 22.0
(byte*) main::cursor#3 cursor zp ZP_WORD:6 6.6000000000000005
(byte*) main::cursor#6 cursor zp ZP_WORD:6 16.5
(byte*) main::line
(byte*) main::line#2 line zp ZP_WORD:4 5.5
(byte*) main::line#5 line zp ZP_WORD:4 16.5
(byte*~) main::line#8 line zp ZP_WORD:4 22.0
(byte*) main::msg
(byte*) main::msg#1 msg zp ZP_WORD:2 22.0
(byte*) main::msg#2 msg zp ZP_WORD:2 6.111111111111112
zp ZP_WORD:2 [ main::msg#2 main::msg#1 ]
zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ]
zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ]
reg byte a [ main::$0 ]
FINAL ASSEMBLER
Score: 1291
// File Comments
// Test using some simple supported string escape \n in both string and char
// Uses encoding PETSCII mixed
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
.label cursor = 6
.label msg = 2
.label line = 4
// [5] phi from main to main::@1 [phi:main->main::@1]
// [5] phi (byte*) main::cursor#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1
lda #<$400
sta.z cursor
lda #>$400
sta.z cursor+1
// [5] phi (byte*) main::line#2 = (byte*) 1024 [phi:main->main::@1#1] -- pbuz1=pbuc1
lda #<$400
sta.z line
lda #>$400
sta.z line+1
// [5] phi (byte*) main::msg#2 = (const byte[]) MESSAGE#0 [phi:main->main::@1#2] -- pbuz1=pbuc1
lda #<MESSAGE
sta.z msg
lda #>MESSAGE
sta.z msg+1
// main::@1
b1:
// while(*msg)
// [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 -- vbuc1_neq__deref_pbuz1_then_la1
ldy #0
lda (msg),y
cmp #0
bne b2
// main::@return
// }
// [7] return
rts
// main::@2
b2:
// case '\n':
// line += 0x28;
// cursor = line;
// break;
// [8] if(*((byte*) main::msg#2)==(byte) ' 'pm) goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1
.encoding "petscii_mixed"
lda #'\n'
ldy #0
cmp (msg),y
beq b3
// main::@4
// *msg & 0x3f
// [9] (byte~) main::$0 ← *((byte*) main::msg#2) & (byte) $3f -- vbuaa=_deref_pbuz1_band_vbuc1
lda #$3f
and (msg),y
// *cursor++ = *msg & 0x3f
// [10] *((byte*) main::cursor#3) ← (byte~) main::$0 -- _deref_pbuz1=vbuaa
sta (cursor),y
// *cursor++ = *msg & 0x3f;
// [11] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1
inc.z cursor
bne !+
inc.z cursor+1
!:
// [12] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5]
// [12] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy
// [12] phi (byte*) main::line#5 = (byte*~) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy
// main::@5
b5:
// msg++;
// [13] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1
inc.z msg
bne !+
inc.z msg+1
!:
// [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1]
// [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy
// [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy
// [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy
jmp b1
// main::@3
b3:
// line += 0x28
// [14] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1
lda #$28
clc
adc.z line
sta.z cursor
lda #0
adc.z line+1
sta.z cursor+1
// [15] (byte*~) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2
lda.z cursor
sta.z line
lda.z cursor+1
sta.z line+1
jmp b5
}
// File Data
MESSAGE: .text @"hello\nworld"
.byte 0

View File

@ -0,0 +1,31 @@
(label) @1
(label) @begin
(label) @end
(byte[]) MESSAGE
(const byte[]) MESSAGE#0 MESSAGE = (string) "hello
world"pm
(void()) main()
(byte~) main::$0 reg byte a 22.0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@4
(label) main::@5
(label) main::@return
(byte*) main::cursor
(byte*) main::cursor#1 cursor zp ZP_WORD:6 16.5
(byte*) main::cursor#2 cursor zp ZP_WORD:6 22.0
(byte*) main::cursor#3 cursor zp ZP_WORD:6 6.6000000000000005
(byte*) main::cursor#6 cursor zp ZP_WORD:6 16.5
(byte*) main::line
(byte*) main::line#2 line zp ZP_WORD:4 5.5
(byte*) main::line#5 line zp ZP_WORD:4 16.5
(byte*~) main::line#8 line zp ZP_WORD:4 22.0
(byte*) main::msg
(byte*) main::msg#1 msg zp ZP_WORD:2 22.0
(byte*) main::msg#2 msg zp ZP_WORD:2 6.111111111111112
zp ZP_WORD:2 [ main::msg#2 main::msg#1 ]
zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ]
zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ]
reg byte a [ main::$0 ]