1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-09 18:29:36 +00:00

- Successful export and import of conio.

- TODO: Create header file for import.
- TODO: Resolve coalesce of exported procedure parameters.
- TODO: Remove constant optimizations for exported procedure parameters.
This commit is contained in:
Sven Van de Velde 2023-11-18 10:40:50 +01:00
parent de6dad44db
commit 1209353803
39 changed files with 2167 additions and 166 deletions

4
.gitignore vendored
View File

@ -18,4 +18,6 @@ kickc.iml
**/.target/*
*/workspace.xml
/gen/*
**/fragment/cache/*
**/fragment/cache/*
/src/test/kc/library/.target/
/src/test/kc/library/lib/.target/

View File

@ -151,7 +151,7 @@ directive
: CONST #directiveConst
| ALIGN PAR_BEGIN NUMBER PAR_END #directiveAlign
| REGISTER ( PAR_BEGIN ( NAME ) PAR_END)? #directiveRegister
| ADDRESS_ZEROPAGE #directiveMemoryAreaZp
| ADDRESS_ZEROPAGE ( PAR_BEGIN ( NUMBER ) PAR_END )? #directiveMemoryAreaZp
| ADDRESS_MAINMEM #directiveMemoryAreaMain
| ADDRESS PAR_BEGIN ( expr ) PAR_END #directiveMemoryAreaAddress
| VOLATILE #directiveVolatile

View File

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

View File

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

View File

@ -27,6 +27,12 @@ public class CompileLog {
*/
private boolean verboseAsmOptimize = false;
/**
* Should ASM output be verbose.
*/
private boolean verboseAsm = true;
/**
* Should SSA optimization be verbose.
*/
@ -273,6 +279,10 @@ public class CompileLog {
return this;
}
public boolean isVerboseAsm() { return verboseAsm; }
public void setVerboseAsm(boolean verboseAsm) { this.verboseAsm = verboseAsm; }
public boolean isVerboseAsmOptimize() {
return verboseAsmOptimize;
}

View File

@ -249,7 +249,9 @@ public class Compiler {
new Pass1AssertNoLValueIntermediate(program).execute();
new PassNAddTypeConversionAssignment(program, true).execute();
new Pass1AddressOfHandling(program).execute();
new Pass1AsmUsesHandling(program).execute();
new Pass1AssertProcedureCallParameters(program).execute();
new Pass1ModifiedVarsAnalysis(program).execute();
new Pass1CallStackVarPrepare(program).execute();
@ -747,8 +749,10 @@ public class Compiler {
private void pass5GenerateAndOptimizeAsm() {
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
if(getLog().isVerboseAsm()) {
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
}
getLog().append("ASSEMBLER OPTIMIZATIONS");
List<Pass5AsmOptimization> pass5Optimizations = new ArrayList<>();
@ -769,8 +773,10 @@ public class Compiler {
getLog().append("Succesful ASM optimization " + optimization.getClass().getSimpleName());
asmOptimized = true;
if(getLog().isVerboseAsmOptimize()) {
getLog().append("ASSEMBLER");
getLog().append(program.getAsm().toString());
if(getLog().isVerboseAsm()) {
getLog().append("ASSEMBLER");
getLog().append(program.getAsm().toString());
}
}
}
}
@ -786,9 +792,11 @@ public class Compiler {
getLog().append("\nFINAL SYMBOL TABLE");
getLog().append(program.getScope().toStringVars(program, false, false));
getLog().append("\nFINAL ASSEMBLER");
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
if(getLog().isVerboseAsm()) {
getLog().append("\nFINAL ASSEMBLER");
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
}
getLog().append("\nZERO PAGE TABLE");
getLog().append(program.getScope().toStringVars(program, true, true));

View File

@ -114,6 +114,9 @@ public class KickC implements Callable<Integer> {
@CommandLine.Option(names = {"-v"}, description = "Verbose output describing the compilation process")
private boolean verbose = false;
@CommandLine.Option(names = {"-vnoasm"}, description = "Verbosity Option. Don't show any generated assembler during compilation.")
private boolean verboseAsm = false;
@CommandLine.Option(names = {"-vasmout"}, description = "Verbosity Option. Show KickAssembler standard output during compilation.")
private boolean verboseAsmOut = false;
@ -585,6 +588,10 @@ public class KickC implements Callable<Integer> {
compiler.getLog().setVerboseAsmOptimize(true);
compiler.getLog().setSysOut(true);
}
if(verboseAsm) {
compiler.getLog().setVerboseAsm(false);
compiler.getLog().setSysOut(true);
}
if(verboseFixLongBranch) {
compiler.getLog().setVerboseFixLongBranch(true);
compiler.getLog().setSysOut(true);

View File

@ -287,13 +287,17 @@ public class AsmFormat {
public static String getAsmSymbolName(Program program, Symbol symbol, ScopeRef codeScopeRef) {
ScopeRef symbolScopeRef = symbol.getScope().getRef();
String asmName = symbol.getLocalName();
if(symbol instanceof Variable && ((Variable) symbol).getAsmName() != null) {
asmName = ((Variable) symbol).getAsmName();
String asmLibrary = "";
if(symbol instanceof Variable boundVariable) {
if (boundVariable.getAsmName() != null) {
asmName = boundVariable.getAsmName();
asmLibrary = boundVariable.getAsmLibrary();
}
}
if(!symbolScopeRef.equals(codeScopeRef)) {
if(symbolScopeRef.getFullName().length() > 0)
if(!symbolScopeRef.getFullName().isEmpty())
// Reference to symbol in another scope
return asmFix2(symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());
return asmFix2(asmLibrary + symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());
else {
// Check if the local code scope has a symbol with the same name
final Scope codeScope = program.getScope().getScope(codeScopeRef);
@ -303,7 +307,7 @@ public class AsmFormat {
else
// Implicit reference to global symbol
if (symbol instanceof Procedure) {
String namespace = ((Procedure)symbol).getLibrary();
String namespace = ((Procedure)symbol).getAsmLibrary();
if (namespace != null)
asmName = namespace + "." + asmName;
}
@ -312,7 +316,7 @@ public class AsmFormat {
} else {
// Reference to local symbol
if (symbol instanceof Procedure) {
String namespace = ((Procedure)symbol).getLibrary();
String namespace = ((Procedure)symbol).getAsmLibrary();
if (namespace != null)
asmName = namespace + "." + asmName;
}

View File

@ -1,64 +0,0 @@
package dk.camelot64.kickc.asm;
import java.nio.file.Path;
import java.util.*;
/** Define a KickAss library that is imported. */
public class AsmImport extends AsmLine {
public String getFullName() {
return name;
}
private final String name;
public Path getPath() {
return resource;
}
private final Path resource;
private final Set<String> procedures;
public AsmImport(String name, Path resource, Set<String> procedures) {
this.name = name;
this.resource = resource;
this.procedures = procedures;
}
public AsmImport(String name, Path resource) {
this(name, resource, new HashSet<>());
}
public AsmImport addProcedure(String procedureName) {
procedures.add(procedureName);
return this;
}
public boolean hasProcedure(String procedureName) {
return procedures.contains(procedureName);
}
@Override
public int getLineBytes() {
return 0;
}
@Override
public double getLineCycles() {
return 0;
}
@Override
public String getAsm() {
StringBuilder asm = new StringBuilder();
asm.append(".library ").append(" [ ");
asm.append("name=\"").append(name).append("\"");
for(String procedureName : procedures) {
asm.append(",");
asm.append(procedureName);
}
asm.append(" ]");
return asm.toString();
}
}

View File

@ -0,0 +1,73 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.model.symbols.Procedure;
import java.nio.file.Path;
import java.util.*;
/** Define a KickAss library that is exported. */
public class AsmLibrary extends AsmLine {
public String getFullName() {
return name;
}
private final String name;
public Path getPath() {
return resource;
}
private final Path resource;
private final HashMap<String, Procedure.CallingConvention> procedures;
public AsmLibrary(String name, Path resource, HashMap<String, Procedure.CallingConvention> procedures) {
this.name = name;
this.resource = resource;
this.procedures = procedures;
}
public AsmLibrary(String name, Path resource) {
this(name, resource, new LinkedHashMap<>());
}
public Set<String> getProcedures() {
return this.procedures.keySet();
}
public AsmLibrary addProcedure(String procedureName, Procedure.CallingConvention callingConvention) {
procedures.put(procedureName, callingConvention);
return this;
}
public Procedure.CallingConvention getProcedureCallingConvention(String procedureName) {
return procedures.get(procedureName);
}
public boolean hasProcedure(String procedureName) {
return procedures.containsKey(procedureName);
}
@Override
public int getLineBytes() {
return 0;
}
@Override
public double getLineCycles() {
return 0;
}
@Override
public String getAsm() {
StringBuilder asm = new StringBuilder();
asm.append(".export ").append(" [ ");
asm.append("name=\"").append(name).append("\"");
for(String procedureName : procedures.keySet()) {
asm.append(",");
asm.append(procedureName);
}
asm.append(" ]");
return asm.toString();
}
}

View File

@ -57,7 +57,7 @@ final public class AsmFragmentInstanceSpecBuilder {
(callingDistance.equals(Bank.CallingDistance.NEAR)?null:toBank.bankArea()));
ScopeRef codeScope = program.getScope().getRef();
bindings.bind("c1", new ConstantInteger(toBank.bankNumber()));
bindings.bind("la1", new LabelRef(((toProcedure.getLibrary() != null)?toProcedure.getLibrary().toLowerCase() + ".":"") + toProcedure.getFullName()));
bindings.bind("la1", new LabelRef(((toProcedure.getAsmLibrary() != null)?toProcedure.getAsmLibrary().toLowerCase() + ".":"") + toProcedure.getFullName()));
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}

View File

@ -94,7 +94,13 @@ public class Directive {
/** Variable __zp */
public static class MemZp extends Directive {
public MemZp() { super("__zp"); }
public Integer zp;
public MemZp(Integer zp) {
super("__zp");
this.zp = zp;
}
public MemZp() {super( "__zp");}
}
/** Variable __mem */

View File

@ -61,7 +61,7 @@ public class LiveRangeEquivalenceClassSet {
}
/**
* Consolidates two live range equivalence calsses into one.
* Consolidates two live range equivalence classes into one.
* All variables and live ranges from the other class is added to the first one - and the other one is deleted.
*
* @param equivalenceClass The first live range equivalence class.

View File

@ -2,7 +2,7 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.OutputFileManager;
import dk.camelot64.kickc.asm.AsmImport;
import dk.camelot64.kickc.asm.AsmLibrary;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
@ -110,9 +110,14 @@ public class Program {
private String asmLibrary;
/**
* Defines the asm source to be imported with extern __stackcall functions.
* Defines the asm source to be imported with the defined procedures.
*/
private Map<String, AsmImport> asmImports;
private Map<String, AsmLibrary> asmImports;
/**
* Defines the asm source to be exported with the defined procedures.
*/
private Map<String, AsmLibrary> asmExports;
/** All #pragma code segments. Collected during parsing. These are used by the bank() pragmas to validate if the code segment exists during compilation.*/
public Program() {
@ -127,6 +132,7 @@ public class Program {
this.reservedZps = new ArrayList<>();
this.procedureCompilations = new ArrayList<>();
this.asmImports = new LinkedHashMap<>();
this.asmExports = new LinkedHashMap<>();
}
/**
@ -195,7 +201,11 @@ public class Program {
public String prettyControlFlowGraph() {
StringBuilder graphPretty = new StringBuilder();
for(ProcedureCompilation procedureCompilation : getProcedureCompilations()) {
graphPretty.append(procedureCompilation.getGraph().toString(this));
if(procedureCompilation.getGraph() != null) {
graphPretty.append(procedureCompilation.getGraph().toString(this));
} else {
graphPretty.append(procedureCompilation.toString()).append(": null");
}
}
return graphPretty.toString();
}
@ -617,33 +627,34 @@ public class Program {
return entryPointBlocks;
}
public Map<String, AsmImport> getAsmImports() {
public Map<String, AsmLibrary> getAsmImports() {
return asmImports;
}
public void setAsmImports(Map<String, AsmImport> asmImports) {
public void setAsmImports(Map<String, AsmLibrary> asmImports) {
this.asmImports = asmImports;
}
public void addAsmImportProcedures(String asmImport, List<String> procedures) {
public void addAsmImportProcedures(String asmImport, Procedure.CallingConvention callingConvention, List<String> procedures) {
Path asmImportResource = Paths.get(asmImport + ".asm").toAbsolutePath();
String asmImportName = Paths.get(asmImport).getFileName().toString();
AsmImport library = this.asmImports.get(asmImportName);
AsmLibrary library = this.asmImports.get(asmImportName);
if(library == null) {
this.asmImports.put(asmImportName, new AsmImport(asmImportName, asmImportResource));
this.asmImports.put(asmImportName, new AsmLibrary(asmImportName, asmImportResource));
this.addAsmResourceFile(asmImportResource);
}
for(String procedureName : procedures) {
this.asmImports.get(asmImportName).addProcedure(procedureName);
this.asmImports.get(asmImportName).addProcedure(procedureName, callingConvention);
}
}
public AsmImport isProcedureInAsmImport(String procedureName) {
Map<String, AsmImport> asmImports = this.getAsmImports();
public AsmLibrary isProcedureInAsmImport(String procedureName) {
Map<String, AsmLibrary> asmImports = this.getAsmImports();
for (String asmImportKey : asmImports.keySet()) {
AsmImport library = asmImports.get(asmImportKey);
AsmLibrary library = asmImports.get(asmImportKey);
if (library.hasProcedure(procedureName)) {
return library;
}
@ -651,16 +662,70 @@ public class Program {
return null;
}
public void setProcedureAsAsmImport(Procedure procedure, AsmImport asmImport) {
public void setProcedureAsAsmImport(Procedure procedure, AsmLibrary asmImport) {
// Here we check if the procedure declaration is already included in the asm import.
// If yes, the procedure should:,
// - have a stackcall calling convention
// - be external
// - not inlined
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
procedure.setCallingConvention(Procedure.CallingConvention.STACK_CALL);
procedure.setLibrary(asmImport.getFullName());
procedure.setCallingConvention(asmImport.getProcedureCallingConvention(procedure.getFullName()));
procedure.setAsmLibrary(asmImport.getFullName());
procedure.setDeclaredExtern(true);
procedure.setDeclaredInline(false);
}
public Map<String, AsmLibrary> getAsmExports() {
return asmExports;
}
public void addAsmExportProcedures(String asmExport, Procedure.CallingConvention callingConvention, List<String> procedures) {
Path asmExportResource = Paths.get(asmExport + ".asm").toAbsolutePath();
String asmExportName = Paths.get(asmExport).getFileName().toString();
AsmLibrary library = this.asmExports.get(asmExportName);
if(library == null) {
this.asmExports.put(asmExportName, new AsmLibrary(asmExportName, asmExportResource));
// this.addAsmResourceFile(asmExportResource); // Not yet, only when designing the multiple exports from one source file.
}
for(String procedureName : procedures) {
this.asmExports.get(asmExportName).addProcedure(procedureName, callingConvention);
}
}
public boolean isProcedureAsmExport(String procedureName) {
Map<String, AsmLibrary> asmExports = this.getAsmExports();
for (String asmExportKey : asmExports.keySet()) {
AsmLibrary library = asmExports.get(asmExportKey);
if (library.hasProcedure(procedureName)) {
return true;
}
}
return false;
}
public AsmLibrary getAsmExportLibrary(Procedure procedure) {
Map<String, AsmLibrary> asmExports = this.getAsmExports();
for (String asmExportKey : asmExports.keySet()) {
AsmLibrary library = asmExports.get(asmExportKey);
if (library.hasProcedure(procedure.getFullName())) {
return library;
}
}
return null;
}
public void setProcedureAsAsmExport(Procedure procedure, AsmLibrary asmLibrary) {
// Here we check if the procedure declaration is already included in the asm export.
// If yes, the procedure should:,
// - have a stackcall or varcall calling convention
// - be external
// - not inlined
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
procedure.setCallingConvention(asmLibrary.getProcedureCallingConvention(procedure.getFullName()));
procedure.setAsmLibrary(asmLibrary.getFullName());
procedure.setDeclaredExtern(false);
procedure.setDeclaredInline(false);
}
}

View File

@ -199,7 +199,7 @@ public class Registers {
@Override
public String toString() {
return "zp[" + getBytes() + "]:" + getZp();
return "zp[" + getBytes() + "]:" + String.format("%03d", getZp()) + String.format(" (0x%02x)", getZp());
}
@Override

View File

@ -301,6 +301,15 @@ public class VariableBuilder {
return hasDirective(Directive.Export.class);
}
/**
* Declared as extern (__extern keyword)
*
* @return true if declared as __extern
*/
public boolean isExtern() {
return hasDirective(Directive.Extern.class);
}
/**
* Declared but not defined. ( "extern" keyword)
*
@ -461,6 +470,11 @@ public class VariableBuilder {
return register;
}
Directive.MemZp zpDirective = findDirective(Directive.MemZp.class, directives);
if(zpDirective != null) {
return new Registers.RegisterZpMem(zpDirective.zp, -1, true);
}
// No hard-coded register
return null;
}

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.model.symbols;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.Directive;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.types.SymbolType;
@ -46,15 +47,15 @@ public class Procedure extends Scope {
/** The source of the procedure definition. */
private StatementSource definitionSource;
/** The library where the function resides */
private String library;
/** The .asm library where the procedure resides in compiled form. */
private String asmLibrary;
public String getLibrary() {
return library;
public String getAsmLibrary() {
return asmLibrary;
}
public void setLibrary(String library) {
this.library = library;
public void setAsmLibrary(String asmLibrary) {
this.asmLibrary = asmLibrary;
}
/**
@ -133,7 +134,7 @@ public class Procedure extends Scope {
this.callingConvention = callingConvention;
this.constructorRefs = new ArrayList<>();
this.isConstructor = false;
this.library = null;
this.asmLibrary = null;
}
public StatementSource getDefinitionSource() {
@ -207,6 +208,10 @@ public class Procedure extends Scope {
this.parameterNames = new ArrayList<>();
for(Variable parameter : parameters) {
parameterNames.add(parameter.getLocalName());
if(parameter.getRegister() instanceof Directive.Register.MemZp) {
if (((Directive.MemZp) parameter.getRegister()).zp != null)
this.addReservedZp(((Directive.MemZp) parameter.getRegister()).zp);
}
}
}
@ -274,6 +279,10 @@ public class Procedure extends Scope {
this.reservedZps = reservedZps;
}
public void addReservedZp(Integer reservedZp) {
this.reservedZps.add(reservedZp);
}
/**
* Get references to all constructors needed for this procedure
*

View File

@ -104,6 +104,18 @@ public class Variable implements Symbol {
/** If the variable is a struct that is/should be unwound to variables for each member. */
private boolean structUnwind;
public void setAsmLibrary(String asmLibrary) {
this.asmLibrary = asmLibrary;
}
public String getAsmLibrary() {
return asmLibrary != null ? asmLibrary + "." : "";
}
/** The imported asm library where the variable is declared for specific cases */
private String asmLibrary;
/**
* Create a variable (or constant)
*

View File

@ -127,6 +127,7 @@ public class CParser {
public static final String PRAGMA_ASM_LIBRARY = "asm_library";
public static final String PRAGMA_ASM_IMPORT = "asm_import";
public static final String PRAGMA_ASM_EXPORT = "asm_export";
/**
* The Program.

View File

@ -3,7 +3,7 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.cpufamily6502.CpuClobber;
import dk.camelot64.kickc.NumberParser;
import dk.camelot64.kickc.SourceLoader;
import dk.camelot64.kickc.asm.AsmImport;
import dk.camelot64.kickc.asm.AsmLibrary;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.operators.*;
@ -324,14 +324,22 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return null;
}
case CParser.PRAGMA_ASM_LIBRARY -> { // Defines that the result is an asm routine or capability library instead of a program.
this.asmLibrary = pragmaParamName(pragmaParamSingle(ctx));
this.asmLibrary = pragmaParamString(pragmaParamSingle(ctx));
program.setAsmLibrary(this.asmLibrary);
}
case CParser.PRAGMA_ASM_EXPORT -> { // Defines that an C routine is exported into the asm_library.
String libraryName = pragmaParamString(pragmaParamFirst(ctx));
Procedure.CallingConvention callingConvention = pragmaParamCallingConvention(ctx.pragmaParam(1));
List<String> procedures = pragmaParamAsmExportProcedures(ctx.pragmaParam());
program.addAsmExportProcedures(libraryName, callingConvention, procedures);
}
case CParser.PRAGMA_ASM_IMPORT -> { // Defines that an asm routine or capability library is imported into the program.
String libraryName = pragmaParamString(pragmaParamFirst(ctx));
List<String> procedures = pragmaParamAsmImportProcedures(ctx.pragmaParam(), libraryName);
Procedure.CallingConvention callingConvention = pragmaParamCallingConvention(ctx.pragmaParam(1));
List<String> procedures = pragmaParamAsmImportProcedures(ctx.pragmaParam());
program.addAsmImportProcedures(libraryName, procedures);
program.addAsmImportProcedures(libraryName, callingConvention, procedures);
}
default -> program.getLog().append("Warning! Unknown #pragma " + pragmaName);
}
@ -461,9 +469,30 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
* @param libraryFunctionParams The library function params
* @return The list of parsed function parameters
*/
private List<String> pragmaParamAsmImportProcedures(List<KickCParser.PragmaParamContext> libraryParams, String libraryName) {
private List<String> pragmaParamAsmImportProcedures(List<KickCParser.PragmaParamContext> libraryParams) {
List<String> procedures = new ArrayList<>();
for(KickCParser.PragmaParamContext reserveCtx : libraryParams.subList(1, libraryParams.size())) {
for(KickCParser.PragmaParamContext reserveCtx : libraryParams.subList(2, libraryParams.size())) {
if(reserveCtx instanceof KickCParser.PragmaParamNameContext) {
final TerminalNode name = ((KickCParser.PragmaParamNameContext) reserveCtx).NAME();
// Only a single reserved address
String procedureName = name.getText();
procedures.add(procedureName);
} else {
throw new CompileError("Expected a NAME parameter. Found '" + reserveCtx.getText() + "'.", new StatementSource(reserveCtx.getParent()));
}
}
return procedures;
}
/**
* Parse all the function names from the library export function list, delimited by comma.
*
* @param libraryParams The library function params
* @return The list of parsed function parameters
*/
private List<String> pragmaParamAsmExportProcedures(List<KickCParser.PragmaParamContext> libraryParams) {
List<String> procedures = new ArrayList<>();
for(KickCParser.PragmaParamContext reserveCtx : libraryParams.subList(2, libraryParams.size())) {
if(reserveCtx instanceof KickCParser.PragmaParamNameContext) {
final TerminalNode name = ((KickCParser.PragmaParamNameContext) reserveCtx).NAME();
// Only a single reserved address
@ -503,7 +532,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
String procedureName = varDecl.getVarName();
Procedure procDeclared = (Procedure)program.getScope().getSymbol(new SymbolRef(procedureName));
AsmImport asmImport = this.program.isProcedureInAsmImport(procedureName);
AsmLibrary asmImport = this.program.isProcedureInAsmImport(procedureName);
// We skip the procedure definition if:
// - it is already defined in an asm library.
@ -580,14 +609,21 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
procedure = (Procedure) existingSymbol;
} else {
AsmImport asmImport = this.program.isProcedureInAsmImport(procedure.getFullName());
AsmLibrary asmImport = this.program.isProcedureInAsmImport(procedure.getFullName());
if(asmImport != null)
this.program.setProcedureAsAsmImport(procedure, asmImport);
program.getScope().add(procedure);
program.createProcedureCompilation(procedure.getRef());
}
if(procedure.isDeclaredExtern() && !defineProcedure) {
// TODO: remove the library dependency and rework to one routine call.
AsmLibrary asmExport = this.program.getAsmExportLibrary(procedure);
if(asmExport != null)
this.program.setProcedureAsAsmExport(procedure, asmExport);
// if(procedure.isDeclaredExtern() && !defineProcedure) {
if(procedure.isDeclaredExtern() && !defineProcedure && existingSymbol == null) {
/** This is an almost exact copy of a procedure definition.
* When a procedure is defined external, the required control blocks are to be allocated
@ -627,7 +663,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
if (parameter.name == null)
throw new CompileError("Illegal unnamed parameter.", statementSource);
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, null, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, parameter.directives, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
final Variable paramVar = varBuilder.build();
parameterList.add(paramVar);
}
@ -730,7 +766,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
public Object visitParameterDeclTypeDeclarator(KickCParser.ParameterDeclTypeDeclaratorContext ctx) {
this.visit(ctx.declType());
this.visit(ctx.declarator());
ParameterDecl paramDecl = new ParameterDecl(varDecl.getVarName(), varDecl.getEffectiveType());
ParameterDecl paramDecl = new ParameterDecl(varDecl.getVarName(), varDecl.getEffectiveType(), varDecl.getDeclDirectives());
varDecl.exitType();
return paramDecl;
}
@ -738,12 +774,12 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
@Override
public Object visitParameterDeclTypeName(KickCParser.ParameterDeclTypeNameContext ctx) {
SymbolType paramType = (SymbolType) this.visit(ctx.typeName());
return new ParameterDecl(null, paramType);
return new ParameterDecl(null, paramType, null);
}
@Override
public Object visitParameterDeclList(KickCParser.ParameterDeclListContext ctx) {
return new ParameterDecl(null, SymbolType.PARAM_LIST);
return new ParameterDecl(null, SymbolType.PARAM_LIST, null);
}
@Override
@ -976,10 +1012,12 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
static class ParameterDecl {
final public String name;
final public SymbolType type;
final public List<Directive> directives;
public ParameterDecl(String name, SymbolType type) {
public ParameterDecl(String name, SymbolType type, List<Directive> directives) {
this.name = name;
this.type = type;
this.directives = directives;
}
}
@ -1339,7 +1377,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
Bank bank = new Bank(directiveBank.getBankArea(), directiveBank.getBankNumber());
procedure.setBank(bank);
} else if(directive instanceof Directive.Library) {
procedure.setLibrary(((Directive.Library) directive).getLibrary());
procedure.setAsmLibrary(((Directive.Library) directive).getLibrary());
} else if(directive instanceof Directive.CallingConvention) {
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
} else if(directive instanceof Directive.Interrupt) {
@ -1439,7 +1477,16 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
@Override
public Directive visitDirectiveMemoryAreaZp(KickCParser.DirectiveMemoryAreaZpContext ctx) {
return new Directive.MemZp();
String zpText = ctx.NUMBER().getText();
if(zpText != null)
try {
Number zpNumber = NumberParser.parseLiteral(zpText);
return new Directive.MemZp(zpNumber.intValue());
} catch(NumberFormatException e) {
throw new CompileError(e.getMessage(), new StatementSource(ctx));
}
else
return new Directive.MemZp();
}
@Override

View File

@ -75,8 +75,7 @@ public class Pass1AddressOfHandling extends Pass2SsaOptimization {
//getLog().append("Setting inferred volatile on symbol affected by address-of: " + variable.toString() + " in " + stmtStr);
}
}
} else if(toSymbol instanceof Procedure) {
Procedure procedure = (Procedure) toSymbol;
} else if(toSymbol instanceof Procedure procedure) {
if(procedure.getParameters().size() > 0 || !SymbolType.VOID.equals(procedure.getReturnType())) {
procedure.setCallingConvention(Procedure.CallingConvention.STACK_CALL);
getLog().append("Setting inferred __stackcall on procedure affected by address-of " + toSymbol.toString(getProgram()) + " caused by statement " + stmtStr);

View File

@ -1,8 +1,6 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Graph;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.Procedure;
@ -87,6 +85,7 @@ public class Pass1CallVar extends Pass2SsaOptimization {
for(int i = 0; i < parameterDefs.size(); i++) {
final RValue parameterVal = call.getParameters().get(i);
final Variable parameterDef = parameterDefs.get(i);
parameterDef.setAsmLibrary(procedure.getAsmLibrary());
stmtIt.add(new StatementAssignment(parameterDef.getVariableRef(), parameterVal, false, source, comments));
comments = Comment.NO_COMMENTS;
}

View File

@ -2,7 +2,6 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.Graph;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.ProcedureRef;
@ -28,12 +27,14 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base {
Collection<Procedure> allProcedures = getProgram().getScope().getAllProcedures(true);
for(Procedure procedure : allProcedures)
if(!ProcedureUtils.isEntrypoint(procedure.getRef(), getProgram()))
if(!calledProcedures.contains(procedure.getRef()))
// The procedure is not used - mark for removal!
unusedProcedures.add(procedure.getRef());
if(!this.getProgram().isProcedureAsmExport(procedure.getFullName()))
if(!calledProcedures.contains(procedure.getRef()))
// The procedure is not used - mark for removal!
unusedProcedures.add(procedure.getRef());
for(ProcedureRef unusedProcedure : unusedProcedures) {
removeProcedure(getProgram(), unusedProcedure);
if(!this.getProgram().isProcedureAsmExport(unusedProcedure.getFullName()))
removeProcedure(getProgram(), unusedProcedure);
}
return unusedProcedures.size() > 0;

View File

@ -130,8 +130,16 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
// Alias assignment
VariableRef alias = (VariableRef) assignment.getrValue2();
List<VarAssignments.VarAssignment> assignments = VarAssignments.get(alias, program.getGraph(), program.getScope());
/* TODO: Sven; Here I need to build the logic to evaluate that if a var has
never been assigned, that it only accepts to continue if the procedure
is exported.
Instead of continue always, the throw exception is to be done when
the assignment.size() == 0 and the procedure is exported.
:throw new InternalError("Error! Var is never assigned! " + alias);:
*/
if(assignments.size() == 0)
throw new InternalError("Error! Var is never assigned! " + alias);
continue;
else if(assignments.size() > 1)
// Multiple assignments exist
continue;

View File

@ -9,6 +9,7 @@ import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Variable;
@ -49,6 +50,13 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
SymbolType valueType = SymbolTypeInference.inferType(getProgramScope(), constVal);
SymbolType variableType = variable.getType();
if(scope instanceof Procedure procedure) {
if (getProgram().isProcedureAsmExport(procedure.getFullName()))
if (procedure.getParameters().contains(variable))
continue;
}
if(!SymbolType.NUMBER.equals(variableType) && SymbolType.NUMBER.equals(valueType)) {
// Value is number - wait til it is cast to a proper type
constants.remove(constRef);

View File

@ -200,12 +200,26 @@ public class Pass4CodeGeneration {
asm.addNamespaceEnd();
}
// Now we import each asm library imported through the #pragma import() statement.
// In case of an asm library import, we generate the instructions to import the library.
// TODO: Solve the need for the #define __asm_import__, to solve the .segmentdef conflict.
for(String asmLibraryName : program.getAsmImports().keySet()) {
asm.addComment("Asm library " + asmLibraryName + ":", false);
asm.addLine( new AsmInlineKickAsm( "#define __asm_import__", null, null, null));
asm.addLine( new AsmInlineKickAsm("#import \"" + asmLibraryName + ".asm\"", null, null, null));
}
// In case of an asm library export, we generate the exported procedure prototypes,
// to be imported into the C program. These prototypes set the register and zeropage usages.
// TODO: I need to add an assert function to validate that each exported procedure is also declared and defined!
for(AsmLibrary asmLibrary : program.getAsmExports().values()) {
for(String procedureName : asmLibrary.getProcedures()) {
Procedure procedure = getScope().getProcedure(new ProcedureRef(procedureName));
if(procedure != null) {
// Generate parameter information
generateSignatureComments(asm, procedure);
}
}
}
program.setAsm(asm);
}
@ -269,9 +283,8 @@ public class Pass4CodeGeneration {
signature.append(param.getType().toCDecl(parameter.getLocalName()));
}
signature.append(")");
if (i > 0) {
asm.addComment(signature.toString(), false);
}
// Always add the signature comments...
asm.addComment(signature.toString(), false);
if(!procedure.getBank().isCommon()) {
asm.addComment(" " + procedure.getBank(), false);
}
@ -918,12 +931,12 @@ public class Pass4CodeGeneration {
} else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) {
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
if(toProcedure.getLibrary() == null) {
if(toProcedure.getAsmLibrary() == null) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
// Call the library routine and clobber all registers.
// Possibly an option to be given that specific routines only clobber specific registers.
asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getLibrary() + "." + call.getProcedure().getFullName(), false);
asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getAsmLibrary() + "." + call.getProcedure().getFullName(), false);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
} else {
@ -944,7 +957,7 @@ public class Pass4CodeGeneration {
// if (toProcedure.isDeclaredBanked() && fromProcedure.getBank() != toProcedure.getBank()) {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
} else {
if (toProcedure.getLibrary() == null) {
if (toProcedure.getAsmLibrary() == null) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);

View File

@ -4,6 +4,7 @@ import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCallFinalize;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.StructDefinition;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.RValue;
@ -58,6 +59,19 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base {
EquivalenceClassAdder equivalenceClassAdder = new EquivalenceClassAdder(liveRangeEquivalenceClassSet);
equivalenceClassAdder.visitGraph(getGraph());
// Add any load/store variables with an initializer - and load/store struct variables
for(Variable variable : getSymbols().getAllVariables(true)) {
if(variable.getScope() instanceof Procedure procedure) {
// check if export
if(getProgram().isProcedureAsmExport((procedure.getFullName()))) {
List<Variable> params = procedure.getParameters();
if(params.contains(variable)) {
addToEquivalenceClassSet(variable.getVariableRef(), new ArrayList<>(), liveRangeEquivalenceClassSet);
}
}
}
}
// Add any load/store variables with an initializer - and load/store struct variables
for(Variable variable : getSymbols().getAllVariables(true)) {
if(variable.getScope() instanceof StructDefinition)

View File

@ -56,6 +56,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
canCoalesceSegments(ec1, ec2, program) &&
canCoalesceVolatile(ec1, ec2, program) &&
canCoalesceThreads(ec1, ec2, threadHeads, program) &&
// canCoalesceExports(ec1, ec2, program) &&
canCoalesceClobber(ec1, ec2, unknownFragments, program);
}
@ -85,6 +86,8 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
return dataSegment1.equals(dataSegment2);
}
/**
* Determines if two live range equivalence classes can be coalesced without cross-thread clobber.
* This is possible if they are both only called from the same thread head.
@ -105,7 +108,51 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
if(threads1.isEmpty() || threads2.isEmpty()) {
return true;
}
return threads1.equals(threads2);
Procedure proc1 = program.getScope().getProcedure((ProcedureRef)threads1.toArray()[0]);
Procedure proc2 = program.getScope().getProcedure((ProcedureRef)threads2.toArray()[0]);
if(program.isProcedureAsmExport(proc1.getFullName()) || program.isProcedureAsmExport(proc2.getFullName()) ) {
return true;
} else {
return threads1.equals(threads2);
}
}
private static boolean isExportParameter(VariableRef varRef, Program program) {
Variable variable = program.getScope().getVariable(varRef);
ScopeRef scopeRef = variable.getScope().getRef();
if (scopeRef instanceof ProcedureRef) {
if (program.isProcedureAsmExport(scopeRef.getFullName())) {
Procedure procedure = program.getScope().getProcedure((ProcedureRef)scopeRef);
if(procedure.getParameters().contains(variable))
return true;
}
}
return false;
}
/**
* Determines if two live range equivalence classes can be coalesced when exported.
* This is possible if they are both exported and input parameters.
*
* @param ec1 One equivalence class
* @param ec2 Another equivalence class
* @param threadHeads The heads (in the call graph) from each thread in the program
* @param program The program
* @return True if the two equivalence classes can be coalesced into one without problems.
*/
private static boolean canCoalesceExports(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Program program) {
for(VariableRef varRef : ec1.getVariables()) {
if(isExportParameter(varRef, program)) {
for(VariableRef varRef2 : ec2.getVariables()) {
if(isExportParameter(varRef2, program))
return false;
}
}
}
return true;
}
/**
@ -170,11 +217,15 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
int zp = ((Registers.RegisterZpMem) register1).getZp();
if(program.getReservedZps().contains(zp))
return false;
if(register1.isAddressHardcoded())
return false;
}
if(register2 instanceof Registers.RegisterZpMem) {
int zp = ((Registers.RegisterZpMem) register2).getZp();
if(program.getReservedZps().contains(zp))
return false;
if(register2.isAddressHardcoded())
return false;
}
return true;
}

View File

@ -62,6 +62,7 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
// Copy Resource Files
for(Path asmResourceFile : getProgram().getAsmResourceFiles()) {
File binFile = getTmpFile(tmpDir, asmResourceFile.getFileName().toString());
getLog().append("Preparing to copy " + asmResourceFile.toString() + " to " + binFile.toPath());
Files.copy(asmResourceFile, binFile.toPath());
}
} catch(IOException e) {

View File

@ -1,7 +1,6 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Graph;
import dk.camelot64.kickc.asm.AsmLibrary;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.VariableReferenceInfos;
import dk.camelot64.kickc.model.statements.*;
@ -36,6 +35,8 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos();
boolean modified = false;
for(var block : getGraph().getAllBlocks()) {
Procedure procedure = getProgram().getProcedure(block);
AsmLibrary asmLibrary = getProgram().getAsmExportLibrary(procedure);
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
while(stmtIt.hasNext()) {
Statement statement = stmtIt.next();
@ -102,15 +103,15 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
} else if(statement instanceof StatementPhiBlock) {
StatementPhiBlock statementPhi = (StatementPhiBlock) statement;
ListIterator<StatementPhiBlock.PhiVariable> phiVarIt = statementPhi.getPhiVariables().listIterator();
while(phiVarIt.hasNext()) {
while (phiVarIt.hasNext()) {
StatementPhiBlock.PhiVariable phiVariable = phiVarIt.next();
VariableRef variableRef = phiVariable.getVariable();
Variable variable = getProgramScope().getVariable(variableRef);
if(eliminate(variable, referenceInfos, statement)) {
if(pass2 || getLog().isVerbosePass1CreateSsa()) {
if (eliminate(variable, referenceInfos, statement)) {
if (pass2 || getLog().isVerbosePass1CreateSsa()) {
getLog().append("Eliminating unused variable - keeping the phi block " + variableRef.toString(getProgram()));
}
if(variable != null) {
if (variable != null) {
variable.getScope().remove(variable);
}
phiVarIt.remove();
@ -154,20 +155,25 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
* @return true if the variable & statement should be eliminated
*/
private boolean eliminate(Variable variable, VariableReferenceInfos referenceInfos, Statement statement) {
if(variable == null)
if (variable == null)
// Eliminate if already deleted
return true;
if(variable.getScope() instanceof EnumDefinition || variable.getScope() instanceof StructDefinition)
if (variable.getScope() instanceof EnumDefinition || variable.getScope() instanceof StructDefinition)
// Do not eliminate inside enums or structs
return false;
if(!pass2 && isReturnValue(variable)) {
// Do not eliminate return variables in pass 1
return false;
}
if(variable.getScope() instanceof Procedure && ((Procedure) variable.getScope()).getParameters().contains(variable)) {
// Do not eliminate parameters
if (!pass2 && isReturnValue(variable))
return false;
if (variable.getScope() instanceof Procedure procedure) {
if (((Procedure) variable.getScope()).getParameters().contains(variable))
// Do not eliminate parameters
return false;
if (isReturnValue(variable) && getProgram().isProcedureAsmExport(procedure.getFullName()))
// Do not eliminate unused return value is the procedure is exported.
return false;
}
final boolean unused = referenceInfos.isUnused(variable.getRef());
if(!unused)
// Do not eliminate is not unused

View File

@ -30,6 +30,8 @@ public class ProcedureUtils {
return true;
if(Pass2ConstantIdentification.isAddressOfUsed(procedureRef, program))
return true;
if(program.isProcedureAsmExport(procedureRef.getFullName()))
return true;
return false;
}

View File

@ -171,7 +171,7 @@ public class TestProgramsThorough extends TestPrograms {
@Test
public void testNesConio() throws IOException {
compileAndCompare("examples/nes/nes-conio.c");
compileAndCompare("examples/nes/nes-conio-stack.c");
}
@Test
@ -241,7 +241,7 @@ public class TestProgramsThorough extends TestPrograms {
@Test
public void testAtariXlConioTest() throws IOException {
compileAndCompare("examples/atarixl/atarixl-conio.c");
compileAndCompare("examples/atarixl/atarixl-conio-stack.c");
}
@Test

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
#pragma link("conio.ld")
#pragma encoding(petscii_mixed)
#pragma var_model(zp)
#pragma asm_library("conio")
#pragma zp_reserve(0x10..0xFF)
#pragma code_seg(CodeConIO)
#pragma data_seg(DataConIO)
#include <conio.h>
//#include <cx16-conio.h>
__export volatile void* funcs[] = {
&clrscr,
&gotoxy,
// &wherex,
// &wherey,
// &screensize,
// &screensizex,
// &screensizey,
// &cputc,
// &cputln,
// &cputcxy,
// &cputs,
// &cputsxy,
// &textcolor,
// &bgcolor,
// &bordercolor,
// &kbhit,
// &cursor,
// &scroll,
// &screenlayer0,
// &screenlayer1,
// &cpeekc,
// &cpeekcxy,
};

View File

@ -0,0 +1,24 @@
typedef struct {
unsigned char cursor_x; ///< current cursor x-position
unsigned char cursor_y; ///< current cursor y-position
} cx16_conio_t;
cx16_conio_t __conio;
void gotoxy(unsigned char x, unsigned char y)
{
__conio.cursor_x = (x>=1)?1:x;
__conio.cursor_y = (y>=2)?2:y;
// __conio.offset = __conio.offsets[y] + __conio.cursor_x << 1;
}
void cputs(const char* s) {
__conio.cursor_x = (s==0)?1:2;
}
void cputsxy(unsigned char x, unsigned char y, const char* s) {
// gotoxy(x, y);
cputs(s);
}

View File

@ -0,0 +1,5 @@
void gotoxy(unsigned char x, unsigned char y);
void cputsxy(unsigned char x, unsigned char y, const char* s);
void cputs(const char* s);

View File

@ -0,0 +1,27 @@
#pragma link("conio.ld")
#pragma encoding(petscii_mixed)
#pragma var_model(zp)
#pragma asm_library("conio_var")
#pragma asm_export("conio_var", __varcall, __start, conio_x16_init)
#pragma asm_export("conio_var", __varcall, clrscr)
#pragma asm_export("conio_var", __varcall, gotoxy)
#pragma asm_export("conio_var", __varcall, wherex, wherey)
#pragma asm_export("conio_var", __varcall, screensize, screensizex, screensizey, cputln )
#pragma asm_export("conio_var", __stackcall, cputc )
#pragma asm_export("conio_var", __varcall, cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor )
#pragma asm_export("conio_var", __varcall, kbhit, cursor, scroll )
#pragma asm_export("conio_var", __varcall, screenlayer1, screenlayer2)
#pragma asm_export("conio_var", __varcall, cpeekc, cpeekcxy)
#pragma zp_reserve(0x80..0xFF)
#pragma code_seg(CodeConIO)
#pragma data_seg(DataConIO)
//#include "conio-test.h"
#include <conio.h>
#include <cx16-conio.h>

View File

@ -4,21 +4,44 @@
// Instead of using conio.c and conio.h, this code imports a
// conio (.asm) library, already pre-compiled.
// The functions, defined using the #pragma directive,
// are declared as external __stackcall by the compiler.
// are declared as external __varcall by the compiler.
// So any function in your code using the conio functions will follow
// stack calling convention.
// In the ideal world, these functions should not be provided but
// linked by a "linker".
#pragma asm_import("lib/conio", clrscr, gotoxy, wherex, wherey)
#pragma asm_import("lib/conio", screensize, screensizex, screensizey)
#pragma asm_import("lib/conio", cputc, cputln, cputcxy, cputs, cputsxy)
#pragma asm_import("lib/conio", textcolor, bgcolor, bordercolor, kbhit)
#pragma asm_import("lib/conio", cursor, scroll)
#pragma asm_import("lib/conio", screenlayer0, screenlayer1)
#pragma asm_import("lib/conio", cpeekc, cpeekcxy)
#pragma asm_import("conio_var", __varcall, conio_x16_init, clrscr, gotoxy)
#pragma asm_import("conio_var", __varcall, wherex, wherey)
#pragma asm_import("conio_var", __varcall, screensize, screensizex, screensizey)
#pragma asm_import("conio_var", __stackcall, cputc )
#pragma asm_import("conio_var", __varcall, cputln, cputcxy, cputs, cputsxy)
#pragma asm_import("conio_var", __varcall, textcolor, bgcolor, bordercolor, kbhit)
#pragma asm_import("conio_var", __varcall, cursor, scroll)
#pragma asm_import("conio_var", __varcall, screenlayer0, screenlayer1)
//#pragma asm_import("conio_var", __varcall, cpeekc, cpeekcxy)
// void __start()
extern void conio_x16_init();
extern void clrscr();
extern void gotoxy(__zp($d) char x, __zp(2) char y);
extern __zp(4) char wherex();
extern __zp(3) char wherey();
extern void screensize(__zp(8) char *x, __zp($10) char *y);
extern __zp($d) char screensizex();
extern __zp(2) char screensizey();
extern void cputln();
extern void cputc(__zp($a) char c);
extern void cputcxy(__zp($d) char x, __zp(2) char y, __zp(7) char c);
extern void cputs(__zp($e) const char *s);
extern void cputsxy(__zp($d) char x, __zp(2) char y, __zp($10) const char *s);
extern __zp(2) char textcolor(__zp(6) char color);
extern __zp(2) char bgcolor(__zp(3) char color);
extern __zp(4) char bordercolor(__zp(3) char color);
extern char kbhit();
extern __zp(4) char cursor(__zp(5) char onoff);
extern __zp($d) char scroll(__zp(5) char onoff);
extern void screenlayer1();
// Linkage
@ -29,8 +52,14 @@
#include <printf.h>
void main() {
// clrscr();
char c = 'x';
asm { jsr conio_var.__start }
textcolor(WHITE);
bgcolor(GREEN);
clrscr();
char c = 'e';
signed char sc = -12;
unsigned char uc = 34;
signed int si = -1234;
@ -38,22 +67,39 @@ void main() {
signed long sl = -123456;
unsigned long ul = 567890;
gotoxy(10,2);
// char
printf("A char: %c\n", c);
gotoxy(10,4);
// pointer
printf("A pointer: %p\n", &c);
gotoxy(10,6);
// percent sign
printf("A percent: %%\n");
gotoxy(10,8);
// signed char
printf("A signed char: %hhd\n", sc);
gotoxy(10,10);
// unsigned char
printf("An unsigned char: %hhu\n", uc);
gotoxy(50,2);
// signed int
printf("A signed int: %d\n", si);
gotoxy(50,4);
// unsigned int
printf("An unsigned int: %u\n", ui);
gotoxy(50,6);
// signed long
printf("A signed long: %ld\n", sl);
gotoxy(50,8);
// unsigned long
printf("An unsigned long: %lu\n", ul);