1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-23 23:32:55 +00:00

Implemented initial KickAsm segment support. #113

This commit is contained in:
jespergravgaard 2019-08-09 11:31:08 +02:00
parent 9a54c0f814
commit 8f0b9c886f
13 changed files with 283 additions and 26 deletions

View File

@ -0,0 +1,60 @@
package dk.camelot64.kickc.asm;
import java.util.LinkedHashMap;
import java.util.Map;
/** Define a KickAss output file */
public class AsmFile implements AsmLine {
private final String name;
private final Map<String,String> parameters;
private int index;
public AsmFile(String name, Map<String, String> parameters) {
this.name = name;
this.parameters = parameters;
}
public AsmFile(String name) {
this(name, new LinkedHashMap<>());
}
public AsmFile param(String paramName, String paramValue) {
parameters.put(paramName, paramValue);
return this;
}
@Override
public int getLineBytes() {
return 0;
}
@Override
public double getLineCycles() {
return 0;
}
@Override
public String getAsm() {
StringBuffer asm = new StringBuffer();
asm.append(".file ").append(" [ ");
asm.append("name=\"").append(name).append("\"");
for(String paramName : parameters.keySet()) {
asm.append(",");
String paramValue = parameters.get(paramName);
asm.append(paramName).append("=").append(paramValue);
}
asm.append(" ]");
return asm.toString();
}
@Override
public int getIndex() {
return index;
}
@Override
public void setIndex(int index) {
this.index = index;
}
}

View File

@ -0,0 +1,64 @@
package dk.camelot64.kickc.asm;
import java.util.LinkedHashMap;
import java.util.Map;
/** Selects a KickAss segment (defined using AsmSegmentDef)*/
public class AsmSegment implements AsmLine {
private final String name;
private final Map<String,String> parameters;
private int index;
public AsmSegment(String name, Map<String, String> parameters) {
this.name = name;
this.parameters = parameters;
}
public AsmSegment(String name) {
this(name, new LinkedHashMap<>());
}
public AsmSegment param(String paramName, String paramValue) {
parameters.put(paramName, paramValue);
return this;
}
@Override
public int getLineBytes() {
return 0;
}
@Override
public double getLineCycles() {
return 0;
}
@Override
public String getAsm() {
StringBuffer asm = new StringBuffer();
asm.append(".segment ").append(name);
if(parameters!=null && parameters.size()>0) {
asm.append(" [ ");
boolean first = true;
for(String paramName : parameters.keySet()) {
if(!first) asm.append(",");
first = false;
String paramValue = parameters.get(paramName);
asm.append(paramName).append("=").append(paramValue);
}
asm.append(" ]");
}
return asm.toString();
}
@Override
public int getIndex() {
return index;
}
@Override
public void setIndex(int index) {
this.index = index;
}
}

View File

@ -0,0 +1,61 @@
package dk.camelot64.kickc.asm;
import java.util.LinkedHashMap;
import java.util.Map;
/** Define a KickAss segment */
public class AsmSegmentDef implements AsmLine {
private final String name;
private final Map<String,String> parameters;
private int index;
public AsmSegmentDef(String name, Map<String, String> parameters) {
this.name = name;
this.parameters = parameters;
}
public AsmSegmentDef(String name) {
this(name, new LinkedHashMap<>());
}
public AsmSegmentDef param(String paramName, String paramValue) {
parameters.put(paramName, paramValue);
return this;
}
@Override
public int getLineBytes() {
return 0;
}
@Override
public double getLineCycles() {
return 0;
}
@Override
public String getAsm() {
StringBuffer asm = new StringBuffer();
asm.append(".segmentdef ").append(name).append(" [ ");
boolean first = true;
for(String paramName : parameters.keySet()) {
if(!first) asm.append(",");
first = false;
String paramValue = parameters.get(paramName);
asm.append(paramName).append("=").append(paramValue);
}
asm.append(" ]");
return asm.toString();
}
@Override
public int getIndex() {
return index;
}
@Override
public void setIndex(int index) {
this.index = index;
}
}

View File

@ -1,7 +1,5 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.fragment.AsmFormat;
/** Set the program counter */
public class AsmSetPc implements AsmLine {

View File

@ -6,8 +6,12 @@ package dk.camelot64.kickc.model;
public enum TargetPlatform {
/** Commodore 64 with BASIC upstart SYS-command. */
C64BASIC("c64basic"),
/** Commodore 64 with BASIC upstart SYS-command - using Code and Data segments. */
C64BASIC_SEGMENTS("c64basic_segments"),
/** 6502 assembler (with no upstart code.)*/
ASM6502("asm6502");
ASM6502("asm6502"),
/** 6502 assembler (with no upstart code.)*/
ASM6502_SEGMENTS("asm6502_segments");
/** The default target platform. */
public static final TargetPlatform DEFAULT = C64BASIC;

View File

@ -84,16 +84,41 @@ public class Pass4CodeGeneration {
if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) {
programPc = 0x080d;
} else {
programPc = 0x1000;
programPc = 0x2000;
}
}
asm.startChunk(currentScope, null, "Upstart");
if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) {
if(TargetPlatform.C64BASIC_SEGMENTS.equals(program.getTargetPlatform())) {
useSegments = true;
currentCodeSegmentName = "Code";
currentDataSegmentName = "Data";
asm.addLine(new AsmFile(program.getFileName() + ".prg").param("type", "\"prg\"").param("segments", "\"Program\""));
asm.addLine(new AsmSegmentDef("Program").param("segments", "\"Basic,Code,Data\""));
asm.addLine(new AsmSegmentDef("Basic").param("start", "$0801"));
asm.addLine(new AsmSegmentDef("Code").param("start", AsmFormat.getAsmNumber(programPc)));
asm.addLine(new AsmSegmentDef("Data").param("startAfter", "\"Code\""));
asm.addLine(new AsmSegment("Basic"));
asm.addLine(new AsmBasicUpstart("bbegin"));
setCurrentSegment(currentCodeSegmentName, asm);
} else if(TargetPlatform.ASM6502_SEGMENTS.equals(program.getTargetPlatform())) {
useSegments = true;
currentCodeSegmentName = "Code";
currentDataSegmentName = "Data";
asm.addLine(new AsmFile(program.getFileName() + ".prg").param("type", "\"prg\"").param("segments", "\"Program\""));
asm.addLine(new AsmSegmentDef("Program").param("segments", "\"Code,Data\""));
asm.addLine(new AsmSegmentDef("Code").param("start", AsmFormat.getAsmNumber(programPc)));
asm.addLine(new AsmSegmentDef("Data").param("startAfter", "\"Code\""));
setCurrentSegment(currentCodeSegmentName, asm);
} else if(TargetPlatform.ASM6502.equals(program.getTargetPlatform())) {
useSegments = false;
asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc)));
} else if(TargetPlatform.C64BASIC.equals(program.getTargetPlatform())) {
useSegments = false;
asm.addLine(new AsmSetPc("Basic", AsmFormat.getAsmNumber(0x0801)));
asm.addLine(new AsmBasicUpstart("bbegin"));
}
asm.addLine(new AsmSetPc("Program", AsmFormat.getAsmNumber(programPc)));
}
// Generate global ZP labels
asm.startChunk(currentScope, null, "Global Constants & labels");
@ -104,6 +129,7 @@ public class Pass4CodeGeneration {
// The current block is in a different scope. End the old scope.
generateScopeEnding(asm, currentScope);
currentScope = block.getScope();
setCurrentSegment(currentCodeSegmentName, asm);
asm.startChunk(currentScope, null, block.getLabel().getFullName());
// Add any procedure comments
if(block.isProcedureEntry(program)) {
@ -120,7 +146,6 @@ public class Pass4CodeGeneration {
}
generateComments(asm, block.getComments());
// Generate entry points (if needed)
genBlockEntryPoints(asm, block);
@ -160,6 +185,9 @@ public class Pass4CodeGeneration {
currentScope = ScopeRef.ROOT;
asm.startChunk(currentScope, null, "File Data");
if(hasData(currentScope)) {
setCurrentSegment(currentDataSegmentName, asm);
}
addData(asm, ScopeRef.ROOT);
// Add all absolutely placed inline KickAsm
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
@ -181,6 +209,28 @@ public class Pass4CodeGeneration {
program.setAsm(asm);
}
// Should the generated program use segments?
boolean useSegments = false;
// Name of the current data segment
private String currentCodeSegmentName = "Code";
// Name of the current code segment
private String currentDataSegmentName = "Data";
// Name of the current active segment
private String currentSegmentName = "";
/**
* Set the current ASM segment - if needed
*
* @param segmentName The segment name we want
* @param asm The ASM program (where a .segment line is added if needed)
*/
private void setCurrentSegment(String segmentName, AsmProgram asm) {
if(useSegments && !currentSegmentName.equals(segmentName)) {
asm.addLine(new AsmSegment(segmentName));
currentSegmentName = segmentName;
}
}
/**
* ASM names of variables being used for indirect calls in the current scope (procedure).
* These will all be added as indirect JMP's at the end of the procedure scope.
@ -201,6 +251,9 @@ public class Pass4CodeGeneration {
asm.addInstruction("jmp", AsmAddressingMode.IND, indirectCallAsmName, false);
}
indirectCallAsmNames = new ArrayList<>();
if(hasData(currentScope)) {
setCurrentSegment(currentDataSegmentName, asm);
}
addData(asm, currentScope);
asm.addScopeEnd();
}
@ -391,6 +444,23 @@ public class Pass4CodeGeneration {
return useLabel;
}
/**
* Examine whether there are any data directives to be added
*
* @param scopeRef The scope
*/
private boolean hasData(ScopeRef scopeRef) {
Scope scope = program.getScope().getScope(scopeRef);
Collection<ConstantVar> scopeConstants = scope.getAllConstants(false);
for(ConstantVar constantVar : scopeConstants) {
if(hasData(constantVar)) {
return true;
}
}
return false;
}
/**
* Add data directives for constants declarations
*
@ -952,8 +1022,8 @@ public class Pass4CodeGeneration {
* @param encodings The encodings to ensure
*/
private void ensureEncoding(AsmProgram asm, Collection<ConstantString.Encoding> encodings) {
if(encodings == null || encodings.size()==0) return;
if(encodings.size()>1) {
if(encodings == null || encodings.size() == 0) return;
if(encodings.size() > 1) {
throw new CompileError("Different character encodings in one ASM statement not supported!");
}
// Size is 1 - grab it!
@ -1002,7 +1072,6 @@ public class Pass4CodeGeneration {
}
/**
* Get phi transitions for a specific to-block.
*

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.asm.*;
import dk.camelot64.kickc.fragment.AsmFormat;
import dk.camelot64.kickc.model.Program;
import java.util.LinkedHashMap;
@ -34,7 +35,7 @@ public class Pass5DoubleJumpElimination extends Pass5AsmOptimization {
currentLabel = ((AsmLabel) line).getLabel();
} else if(line instanceof AsmComment || line instanceof AsmConstant || line instanceof AsmLabelDecl) {
// ignore
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmDataKickAsm) {
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmDataKickAsm|| line instanceof AsmSegmentDef|| line instanceof AsmSegment|| line instanceof AsmFile) {
currentLabel = null;
} else if(line instanceof AsmInstruction) {
if(currentLabel != null) {

View File

@ -1,4 +1,4 @@
byte[15] fibs = $1100;
byte[15] fibs;
void main() {
fibs[0] = 0;

View File

@ -1,7 +1,6 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label fibs = $1100
main: {
lda #0
sta fibs
@ -18,3 +17,4 @@ main: {
bcc b1
rts
}
fibs: .fill $f, 0

View File

@ -2,7 +2,7 @@ Culled Empty Block (label) main::@2
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte[$f]) fibs#0 ← ((byte[$f])) (number) $1100
(byte[$f]) fibs#0 ← { fill( $f, 0) }
to:@1
main: scope:[main] from @1
*((byte[$f]) fibs#0 + (number) 0) ← (number) 0
@ -59,12 +59,10 @@ Adding number conversion cast (unumber) 1 in (number~) main::$1 ← (byte) main:
Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (byte) main::i#2 + (unumber)(number) 1
Adding number conversion cast (unumber) $f in (bool~) main::$3 ← (byte) main::i#1 < (number) $f
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte[$f]) fibs#0 ← (byte[$f])(number) $1100
Inlining cast *((byte[$f]) fibs#0 + (unumber)(number) 0) ← (unumber)(number) 0
Inlining cast *((byte[$f]) fibs#0 + (unumber)(number) 1) ← (unumber)(number) 1
Inlining cast (byte) main::i#0 ← (unumber)(number) 0
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 4352
Simplifying constant integer cast 0
Simplifying constant integer cast 0
Simplifying constant integer cast 1
@ -87,7 +85,9 @@ Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::i#2 + (byt
Inferred type updated to byte in (unumber~) main::$1 ← (byte) main::i#2 + (byte) 1
Simple Condition (bool~) main::$3 [11] if((byte) main::i#1<(byte) $f) goto main::@1
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant (const byte[$f]) fibs#0 = (byte*) 4352
Constant right-side identified [0] (byte[$f]) fibs#0 ← { fill( $f, 0) }
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const byte[$f]) fibs#0 = { fill( $f, 0) }
Constant (const byte) main::i#0 = 0
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero fibs#0 in [1] *((const byte[$f]) fibs#0 + (byte) 0) ← (byte) 0
@ -169,7 +169,6 @@ Target platform is c64basic
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label fibs = $1100
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
@ -229,6 +228,7 @@ main: {
rts
}
// File Data
fibs: .fill $f, 0
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] *((const byte[$f]) fibs#0) ← (byte) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a
@ -255,7 +255,6 @@ ASSEMBLER BEFORE OPTIMIZATION
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label fibs = $1100
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
@ -307,6 +306,7 @@ main: {
rts
}
// File Data
fibs: .fill $f, 0
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
@ -337,7 +337,7 @@ FINAL SYMBOL TABLE
(label) @begin
(label) @end
(byte[$f]) fibs
(const byte[$f]) fibs#0 fibs = (byte*) 4352
(const byte[$f]) fibs#0 fibs = { fill( $f, 0) }
(void()) main()
(byte~) main::$2 reg byte a 22.0
(label) main::@1
@ -359,7 +359,6 @@ Score: 263
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.label fibs = $1100
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
@ -403,4 +402,5 @@ main: {
rts
}
// File Data
fibs: .fill $f, 0

View File

@ -2,7 +2,7 @@
(label) @begin
(label) @end
(byte[$f]) fibs
(const byte[$f]) fibs#0 fibs = (byte*) 4352
(const byte[$f]) fibs#0 fibs = { fill( $f, 0) }
(void()) main()
(byte~) main::$2 reg byte a 22.0
(label) main::@1

View File

@ -1,5 +1,5 @@
// Tests the target platform ASM6502
.pc = $1000 "Program"
.pc = $2000 "Program"
main: {
ldx #0
b2:

View File

@ -143,7 +143,7 @@ Target platform is asm6502
// File Comments
// Tests the target platform ASM6502
// Upstart
.pc = $1000 "Program"
.pc = $2000 "Program"
// Global Constants & labels
// @begin
bbegin:
@ -212,7 +212,7 @@ ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Tests the target platform ASM6502
// Upstart
.pc = $1000 "Program"
.pc = $2000 "Program"
// Global Constants & labels
// @begin
bbegin:
@ -311,7 +311,7 @@ Score: 161
// File Comments
// Tests the target platform ASM6502
// Upstart
.pc = $1000 "Program"
.pc = $2000 "Program"
// Global Constants & labels
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]