1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00
kickc/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java
Flight_Control 00df07b7bf - Fixed test cases. Full retest of all test cases.
- Treat global variables of libraries, as part of the .asm library .namespace.
- Fix bugs.
- Assign meaningful struct names to .asm internal variables and labels. (Remove the $x notation).
2024-04-20 07:03:31 +03:00

463 lines
13 KiB
Java

package dk.camelot64.kickc.asm;
import dk.camelot64.cpufamily6502.CpuAddressingMode;
import dk.camelot64.cpufamily6502.CpuClobber;
import dk.camelot64.cpufamily6502.CpuOpcode;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.TargetCpu;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.StringEncoding;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A 6502 assembler program
*/
public class AsmProgram {
/**
* The target CPU
*/
private TargetCpu targetCpu;
/**
* The chunks of the program. The chunks hold the ASM lines.
*/
private List<AsmChunk> chunks;
/**Stashed lines to be inserted outside the flow. */
private List<AsmLine> stash;
/**
* The index of the next chunk.
*/
private int nextChunkIndex;
/**
* The index of the next line.
*/
private int nextLineIndex;
/** The current encoding used for printing strings. */
private StringEncoding currentEncoding = StringEncoding.SCREENCODE_MIXED;
public AsmProgram(TargetCpu targetCpu) {
this.targetCpu = targetCpu;
this.chunks = new ArrayList<>();
this.nextLineIndex = 0;
this.nextChunkIndex = 0;
this.stash = new ArrayList<>();
}
public TargetCpu getTargetCpu() {
return targetCpu;
}
public Collection<AsmChunk> getChunks() {
return chunks;
}
public AsmChunk startChunk(ScopeRef scopeRef, Integer statementIndex, String source) {
AsmChunk chunk = new AsmChunk(nextChunkIndex++, scopeRef, statementIndex, source);
chunks.add(chunk);
return chunk;
}
public AsmChunk getCurrentChunk() {
return chunks.get(chunks.size() - 1);
}
public void addLine(AsmLine line) {
line.setIndex(nextLineIndex++);
getCurrentChunk().addLine(line);
}
/**
* Put an ASM-line into the stash for being added to the program later.
* @param line The line
*/
public void stashLine(AsmLine line) {
stash.add(line);
}
/**
* Are there any lines in the stash
* @return true if there are
*/
public boolean hasStash() {
return stash.size()>0;
}
/**
* Add the stash lines to the program - and clear the stash
*/
public void addStash() {
for(AsmLine asmLine : stash) {
addLine(asmLine);
}
stash = new ArrayList<>();
}
/**
* Get the current encoding used for strings/chars
* @return The encoding
*/
public StringEncoding getCurrentEncoding() {
return currentEncoding;
}
public void ensureEncoding(Collection<StringEncoding> encodings) {
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!
StringEncoding encoding = encodings.iterator().next();
if(!getCurrentEncoding().equals(encoding)) {
if(encoding.asmEncoding != null) {
addLine(new AsmSetEncoding(encoding));
} else {
addLine(new AsmSetEncoding(StringEncoding.ASCII));
}
currentEncoding = encoding;
}
}
public void addComment(String comment, boolean isBlock) {
addLine(new AsmComment(comment, isBlock));
}
public AsmLabel addLabel(String label) {
AsmLabel asmLabel = new AsmLabel(label);
addLine(asmLabel);
return asmLabel;
}
/** #820/1 - Handle begin namespace for the .asm library.
*
* @param program
*/
public void addNamespaceBegin(Program program) {
program.setAsmLibraryNamespace(true);
addLine(new AsmNamespaceBegin(program.getAsmLibraryLabel()));
}
/** #820/1 - Handle end namespace for the .asm library.
*
* @param program
*/
public void addNamespaceEnd(Program program) {
program.setAsmLibraryNamespace(false);
addLine(new AsmNamespaceEnd());
}
public void addScopeBegin(String label) {
addLine(new AsmScopeBegin(label));
}
public void addScopeEnd() {
addLine(new AsmScopeEnd());
}
public void addImportOnce() { addLine( new AsmImportOnce());}
public void addAsmImportLibrary(AsmImportLibrary asmImportLibrary) { addLine(asmImportLibrary); }
public AsmInstruction addInstruction(String mnemonic, CpuAddressingMode addressingMode, String operand1, boolean isOperandZp) {
CpuOpcode cpuOpcode = targetCpu.getCpu65xx().getOpcode(mnemonic, addressingMode, isOperandZp);
AsmInstruction asmInstruction = new AsmInstruction(cpuOpcode, operand1, null, null);
addLine(asmInstruction);
return asmInstruction;
}
public void addLabelDecl(String name, String value) {
addLine(new AsmLabelDecl(name, value));
}
/**
* Add a constant declaration to the ASM
*
* @param name The name of the constant
* @param value The value of the constant
*/
public void addConstant(String name, String value) {
addLine(new AsmConstant(name, value));
}
/**
* Add a BYTE/WORD/DWORD data declaration to the ASM
*
* @param label The label of the data
* @param type The type of the data
* @param asmElements The value of the elements
*/
public void addDataNumeric(String label, AsmDataNumeric.Type type, List<String> asmElements) {
addLine(new AsmDataNumeric(label, type, asmElements));
}
/**
* Add a FILL data declaration to the ASM
*
* @param label The label of the data
* @param type The type of the data
* @param numElements The size of data to fill
*/
public void addDataZeroFilled(String label, AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements) {
addLine(new AsmDataZeroFill(label, type, totalSizeBytesAsm, numElements));
}
/**
* Add a string data declaration to the ASM
*
* @param label The label of the data
* @param value The value of the string
*/
public void addDataString(String label, String value) {
addLine(new AsmDataString(label, value));
}
/**
* Add data alignment to the ASM
*
* @param alignment The number to align the address of the next data to
*/
public void addDataAlignment(String alignment) {
addLine(new AsmDataAlignment(alignment));
}
/**
* Add inlines kick assembler code
*
* @param kickAsmCode The kickassembler code
*/
public void addInlinedKickAsm(String kickAsmCode, Long bytes, Long cycles, CpuClobber cpuClobber) {
addLine(new AsmInlineKickAsm(kickAsmCode, bytes, cycles, cpuClobber));
}
/**
* Add ignored kick assembler code when imported (but not when exported) an .asm library.
*
*/
public void addIgnoredImportInlinedKickAsm(String identifier, String kickAsmCode, Long bytes, Long cycles, CpuClobber cpuClobber) {
addLine(new AsmIgnoredImportInlinedKickAsm(identifier, new AsmInlineKickAsm(kickAsmCode, bytes, cycles, cpuClobber)));
}
/**
* Add data array initialized with inline kick assembler code
*
* @param label Name of the data
* @param bytes The number of bytes (from array definition)
* @param kickAsmCode Kick Assembler code to initialize the data
*/
public void addDataKickAsm(String label, int bytes, String kickAsmCode) {
addLine(new AsmDataKickAsm(label, bytes, kickAsmCode));
}
/**
* Get the number of bytes the program occupies in memory.
* Calculated by adding up the bytes of each ASM chunk in the program.
*
* @return The number of bytes
*/
public int getBytes() {
int bytes = 0;
for(AsmChunk chunk : chunks) {
bytes += chunk.getBytes();
}
return bytes;
}
/**
* Get the number of cycles it takes to execute the program
* Calculated by adding up the cycles of each ASM chunks in the program.
*
* @return The number of cycles
*/
public double getCycles() {
double cycles = 0.0;
for(AsmChunk chunk : chunks) {
cycles += chunk.getCycles();
}
return cycles;
}
/**
* Get the CPU registers clobbered by the instructions of the program
*
* @return The clobbered registers
*/
public CpuClobber getClobber() {
CpuClobber programClobber = CpuClobber.CLOBBER_NONE;
for(AsmChunk chunk : chunks) {
programClobber = new CpuClobber(programClobber, chunk.getClobber());
}
return programClobber;
}
public String toString(AsmPrintState printState, Program program) {
StringBuilder out = new StringBuilder();
for(AsmChunk chunk : chunks) {
out.append(chunk.toString(printState, program));
}
return out.toString();
}
@Override
public String toString() {
return toString(new AsmPrintState(false), null);
}
/**
* Set the index of the next line
*
* @param nextIndex The index of the next line
*/
public void setNextLineIndex(int nextIndex) {
this.nextLineIndex = nextIndex;
}
/**
* Get ASM chunk by line index
*
* @param idx The index of the line to get the chunk for
* @return The chunk with the line that has the passed index. Null if not found
*/
public AsmChunk getAsmChunk(int idx) {
for(AsmChunk chunk : chunks) {
for(AsmLine asmLine : chunk.getLines()) {
if(asmLine.getIndex() == idx) {
return chunk;
}
}
}
return null;
}
/**
* Get information about the size of the program
* @return Size information
*/
public String getSizeInfo() {
StringBuilder sizeInfo = new StringBuilder();
sizeInfo.append("SIZE ASM chunks "+ getChunks().size()).append("\n");
AtomicInteger numLines = new AtomicInteger();
getChunks().stream().forEach(asmChunk -> numLines.addAndGet(asmChunk.getLines().size()));
sizeInfo.append("SIZE ASM lines "+ numLines).append("\n");
return sizeInfo.toString();
}
public static class AsmPrintState {
// Output comments with information about the file/line number in the source file
private boolean sourceFileInfo;
// Output comments with C-source from the source file
private boolean sourceCodeInfo;
// Output comments with ICL-code and the ASM fragment name
private boolean sourceIclInfo;
// Output chunk ID in the ICL-comment
private boolean sourceChunkIdInfo;
// Output ASM line numbers
private boolean asmLineNumber;
// Current indent - used during printing
private String indent;
// The current value of code info. Set/get during printing to avoid duplicate source code info comment lines.
private String currentCodeInfo;
// The current value of file info. Set/get during printing to avoid duplicate source code info comment lines.
private String currentFileInfo;
public AsmPrintState(boolean sourceFileInfo, boolean sourceCodeInfo, boolean sourceIclInfo, boolean asmLineNumber) {
this.sourceFileInfo = sourceFileInfo;
this.sourceCodeInfo = sourceCodeInfo;
this.sourceIclInfo = sourceIclInfo;
this.asmLineNumber = asmLineNumber;
this.indent = "";
}
public AsmPrintState(boolean sourceIclInfo) {
this(false, false, sourceIclInfo, false);
}
public boolean isSourceCodeInfo() {
return sourceCodeInfo;
}
public void setSourceCodeInfo(boolean sourceCodeInfo) {
this.sourceCodeInfo = sourceCodeInfo;
}
public boolean isSourceFileInfo() {
return sourceFileInfo;
}
public void setSourceFileInfo(boolean sourceFileInfo) {
this.sourceFileInfo = sourceFileInfo;
}
public boolean isSourceIclInfo() {
return sourceIclInfo;
}
public boolean isSourceChunkIdInfo() {
return sourceChunkIdInfo;
}
public void setSourceChunkIdInfo(boolean sourceChunkIdInfo) {
this.sourceChunkIdInfo = sourceChunkIdInfo;
}
public void setSourceIclInfo(boolean sourceIclInfo) {
this.sourceIclInfo = sourceIclInfo;
}
public boolean isAsmLineNumber() {
return asmLineNumber;
}
public void setAsmLineNumber(boolean asmLineNumber) {
this.asmLineNumber = asmLineNumber;
}
public void incIndent() {
this.indent = this.indent + " ";
}
public void decIndent() {
if(this.indent.length() >= 2) {
this.indent = this.indent.substring(0, this.indent.length() - 2);
}
}
public String getIndent() {
return indent;
}
public boolean getAsmLineNumber() {
return asmLineNumber;
}
public String getCurrentCodeInfo() {
return currentCodeInfo;
}
public void setCurrentCodeInfo(String currentCodeInfo) {
this.currentCodeInfo = currentCodeInfo;
}
public String getCurrentFileInfo() {
return currentFileInfo;
}
public void setCurrentFileInfo(String currentFileInfo) {
this.currentFileInfo = currentFileInfo;
}
}
}