- Fixed test cases. Full retest of all test cases.

- Create the possibility of forward declaration of structs as part of the language.
- Generation of forward declarations in the .asm header files.
- Treat global variables as part of global memory, even in libraries. Ensure that globals are not overwritten when importing.
- Ensure that the various compilation models (stack, var, phi) in combination with the memory models (zp, mem) result in proper execution of code and proper memory allocation etc, etc.
- Added the lru-cache logic to properly test the compilation and memory model combinations. (A lot of bugfixes as a result!)
This commit is contained in:
Sven Van de Velde 2024-01-16 08:01:05 +03:00
parent 2c74b2a19c
commit e371d304f6
64 changed files with 1605 additions and 571 deletions

View File

@ -36,6 +36,7 @@ declSeq
decl
: declVariables ';'
| declFunction
| structForwardDef
| structDef ';'
| enumDef ';'
| pragma
@ -97,8 +98,12 @@ structRef
: (STRUCT|UNION) NAME
;
structForwardDef
: (STRUCT|UNION) NAME ';'
;
structDef
: (STRUCT|UNION) NAME? CURLY_BEGIN structMembers+ CURLY_END
: (STRUCT|UNION) NAME? CURLY_BEGIN structMembers+ CURLY_END #structDefinition
;
structMembers

View File

@ -421,6 +421,7 @@ public class Compiler {
optimizations.add(new PassNEliminateUnusedVars(program, true)); // Notice sequence is important
optimizations.add(new PassNEliminateEmptyProcedure(program));
optimizations.add(new PassNEliminateEmptyStart(program));
optimizations.add(new PassNAsmLibraryGlobalVarsExport(program));
if(enableLoopHeadConstant) {
optimizations.add(new PassNStatementIndices(program));
optimizations.add(() -> {

View File

@ -298,9 +298,9 @@ public class AsmFormat {
if(!symbolScopeRef.getFullName().isEmpty()) {
if (codeScopeRef instanceof ProcedureRef procedureRef) {
Procedure procedure = program.getScope().getProcedure(procedureRef);
String procedureAsmLibraryLabal = procedure.getAsmLibraryLabel();
String procedureAsmLibraryLabel = procedure.getAsmLibraryLabel();
if (asmLibraryLabel != null) {
if (procedureAsmLibraryLabal != null && procedureAsmLibraryLabal.equals(asmLibraryLabel)) {
if (procedureAsmLibraryLabel != null && procedureAsmLibraryLabel.equals(asmLibraryLabel)) {
return asmFix2(symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());
} else {
return asmFix2(asmLibraryLabel + "." + symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());

View File

@ -1,9 +1,6 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.model.symbols.Procedure;
import java.nio.file.Path;
import java.util.HashMap;
public class AsmImportLibrary extends AsmLibrary {
@ -15,7 +12,7 @@ public class AsmImportLibrary extends AsmLibrary {
public String getAsm() {
StringBuilder asm = new StringBuilder();
asm.append(" // Asm import library ").append(getAsmLibraryName()).append(":").append("\n");
asm.append("#define __asm_import__").append(getLabelName()).append("__").append("\n");
asm.append("#define __asm_import__").append(getAsmLibraryIdentifier()).append("__").append("\n");
asm.append("#import \"").append(getAsmLibraryName()).append(".asm\"").append("\n");
return asm.toString();
}

View File

@ -12,28 +12,29 @@ import dk.camelot64.kickc.model.values.*;
import java.nio.file.Path;
import java.util.*;
/** Define a KickAss library that is exported. */
/**
* A Kick assembler "library" helper class used to import and export assembler from a C source.
*/
public class AsmLibrary extends AsmLine {
public String getAsmLibraryName() {
return asmLibraryName.toString();
return this.asmLibraryName.toString();
}
public String getLabelName() { return asmLibraryName.getAsm(); }
public String getAsmLibraryIdentifier() {
return this.asmLibraryName.getAsm();
}
private final AsmIdentifier asmLibraryName;
private final Path resource;
private final HashMap<String, Procedure.CallingConvention> procedures;
private boolean exportAll;
public AsmLibrary(String asmLibraryName, Path resource, HashMap<String, Procedure.CallingConvention> procedures) {
if(asmLibraryName != null)
if(asmLibraryName!=null) {
this.asmLibraryName = new AsmIdentifier(asmLibraryName);
else
} else {
this.asmLibraryName = null;
this.resource = resource;
}
this.procedures = procedures;
}
@ -45,9 +46,8 @@ public class AsmLibrary extends AsmLine {
return this.procedures.keySet();
}
public AsmLibrary addProcedure(String procedureName, Procedure.CallingConvention callingConvention) {
public void addProcedure(String procedureName, Procedure.CallingConvention callingConvention) {
procedures.put(procedureName, callingConvention);
return this;
}
public Procedure.CallingConvention getProcedureCallingConvention(String procedureName) {
@ -70,7 +70,7 @@ public class AsmLibrary extends AsmLine {
if (param == null) return;
if (param.isKindPhiMaster()) {
List<Variable> versions = new ArrayList<>(scope.getVersions(param));
if (versions.size() > 0) if (param.getLocalName().equals("return")) {
if (!versions.isEmpty()) if (param.getLocalName().equals("return")) {
// Choose the last version for return values
param = versions.get(versions.size() - 1);
} else {
@ -88,18 +88,14 @@ public class AsmLibrary extends AsmLine {
zp.add(Integer.toString(registerZp.getZp()+b));
}
}
return;
}
void generatePrototypeVar(Variable var, StringBuilder signature) {
Registers.Register allocation = var.getAllocation();
if (allocation instanceof Registers.RegisterZpMem) {
Registers.RegisterZpMem registerZp = (Registers.RegisterZpMem) allocation;
signature.append("__zp(").append(AsmFormat.getAsmNumber(registerZp.getZp())).append(") ");
} else if (allocation instanceof Registers.RegisterMainMem) {
Registers.RegisterMainMem registerMainMem = (Registers.RegisterMainMem) allocation;
signature.append("__mem(").append(registerMainMem.getAddress() == null ? "" : AsmFormat.getAsmNumber(registerMainMem.getAddress())).append(") ");
if (allocation instanceof Registers.RegisterZpMem registerZp) {
signature.append("__zp(").append(AsmFormat.getAsmNumber(registerZp.getZp())).append(") ");
} else if (allocation instanceof Registers.RegisterMainMem registerMainMem) {
signature.append("__mem(").append(registerMainMem.getAddress() == null ? "" : AsmFormat.getAsmNumber(registerMainMem.getAddress())).append(") ");
} else if (allocation instanceof Registers.RegisterAByte) {
signature.append("__register(A) ");
} else if (allocation instanceof Registers.RegisterXByte) {
@ -136,9 +132,7 @@ public class AsmLibrary extends AsmLine {
return param;
}
if(!isStackCall) {
generatePrototypeVar(param, signature);
}
generatePrototypeVar(param, signature);
return param;
}
@ -163,7 +157,8 @@ public class AsmLibrary extends AsmLine {
}
}
Procedure procedure = program.getScope().getProcedure(procedureRef);
for (Symbol symbol : procedure.getSymbols().values()) {
HashMap<String, Symbol> procedureSymbols = procedure.getSymbols();
for (Symbol symbol : procedureSymbols.values()) {
if (symbol instanceof Variable variable) {
generateZPVar(zp, variable, procedure);
}
@ -212,6 +207,33 @@ public class AsmLibrary extends AsmLine {
return signature.toString();
}
/**
* Generate struct forward declarations for C function prototypes for .asm library import,
* that use structs in the parameters or return values.
* @param procedure The procedure to generate the struct forward for.
* @return A String with the generated function prototype.
*/
private String generateProcedureStructForwards(Procedure procedure, Program program) {
StringBuilder signature = new StringBuilder();
HashSet<String> structForwards = new HashSet<>();
Variable returnVariable = procedure.getLocalVar("return");
if(returnVariable != null && returnVariable.isStruct()) {
String structReturn = generateVariableStructForward(returnVariable, program);
structForwards.add(structReturn);
}
for (Variable parameter : procedure.getParameters()) {
if(parameter.isStruct()) {
String paramStruct = generateVariableStructForward(parameter, program);
structForwards.add(paramStruct);
}
}
if(!structForwards.isEmpty())
signature.append(String.join(";\n", structForwards)).append(";\n");
return signature.toString();
}
private boolean hasData(Variable constantVar) {
ConstantValue constantValue = constantVar.getInitValue();
if (constantValue instanceof ConstantArray) return true;
@ -233,6 +255,16 @@ public class AsmLibrary extends AsmLine {
}
}
private String generateVariableStructForward(Variable variable, Program program) {
StringBuilder signature = new StringBuilder();
if(variable.isStruct()) {
String structName = variable.getType().toCDecl();
signature.append(structName).append(";\n");
}
return signature.toString();
}
private String generateVariableHeader(Variable variable, Program program) {
StringBuilder signature = new StringBuilder();
signature.append("extern ");
@ -264,26 +296,41 @@ public class AsmLibrary extends AsmLine {
*/
public String generateHeaders(Program program) {
StringBuilder headers = new StringBuilder();
// Generate the global variables headers.
StringBuilder structs = new StringBuilder();
// Generate the global variables headers.
Scope scope = program.getScope().getScope(ScopeRef.ROOT);
Collection<Variable> scopeConstants = scope.getAllVars(false);
Set<String> added = new LinkedHashSet<>();
Collection<Variable> globalVariables = scope.getAllVars(false);
Set<String> zp = new HashSet<>();
// Add all constants arrays incl. strings with data
for (Variable constantVar : scopeConstants) {
if (hasExportAsmLibrary(constantVar, this.getAsmLibraryName())) {
headers.append(generateVariableHeader(constantVar, program));
for (Variable globalVariable : globalVariables) {
if (hasExportAsmLibrary(globalVariable, this.getAsmLibraryName())) {
if(globalVariable.isMemoryAreaZP()) {
Registers.RegisterZpMem registerZpMem = (Registers.RegisterZpMem)globalVariable.getAllocation();
if(registerZpMem != null) {
for(byte b=0; b<registerZpMem.getBytes(); b++) {
zp.add(Integer.toString(registerZpMem.getZp()+b));
}
}
}
headers.append(generateVariableHeader(globalVariable, program));
structs.append(generateVariableStructForward(globalVariable, program));
}
}
if(!zp.isEmpty())
headers.insert(0, "#pragma zp_reserve(" + String.join(",", zp) + ")\n");
// Generate the procedure headers.
for(String procedureName: this.getProcedures()) {
Procedure procedure = program.getScope().getProcedure(new ProcedureRef(procedureName));
if(procedure != null) {
if(procedure.isAsmExportLibrary())
headers.append(generateProcedureHeader(procedure, program));
structs.append(generateProcedureStructForwards(procedure, program));
}
}
return headers.toString();
return structs.toString() + headers.toString();
}
@Override

View File

@ -756,7 +756,7 @@ public class Program {
// - not inlined
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
procedure.setCallingConvention(asmImport.getProcedureCallingConvention(procedure.getFullName()));
procedure.setAsmImportLibrary(asmImport.getAsmLibraryName());
procedure.setAsmImportLibrary(asmImport.getAsmLibraryName().toString());
procedure.setDeclaredExtern(true);
procedure.setDeclaredInline(false);
}
@ -778,12 +778,18 @@ public class Program {
*/
public AsmExportLibrary addAsmExportLibrary(String asmExportLibraryName, boolean exportAll) {
Path asmExportResource = Paths.get(asmExportLibraryName + ".asm").toAbsolutePath();
AsmExportLibrary asmLibrary = asmExports.get(asmExportLibraryName);
if(asmLibrary == null) {
asmLibrary = new AsmExportLibrary(asmExportLibraryName, asmExportResource, exportAll);
asmExports.put(asmExportLibraryName, asmLibrary);
} else {
asmLibrary.setExportAll(exportAll);
AsmExportLibrary asmLibrary = null;
if(asmExportLibraryName!=null) {
// Only calculate the .asm export library when the .asm library is declared in the main program.
// Otherwise ignore. This is important for .asm library imports, where library sources are
// imported that contain the #pragma asm_export pro-processor statements.
asmLibrary = asmExports.get(asmExportLibraryName);
if (asmLibrary == null) {
asmLibrary = new AsmExportLibrary(asmExportLibraryName, asmExportResource, exportAll);
asmExports.put(asmExportLibraryName, asmLibrary);
} else {
asmLibrary.setExportAll(exportAll);
}
}
// this.addAsmResourceFile(asmExportResource);
return asmLibrary;
@ -867,7 +873,7 @@ public class Program {
// - not inlined
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
procedure.setCallingConvention(asmExportLibrary.getProcedureCallingConvention(procedure.getFullName()));
procedure.setAsmExportLibrary(asmExportLibrary.getAsmLibraryName());
procedure.setAsmExportLibrary(asmExportLibrary.getAsmLibraryName().toString());
procedure.setDeclaredExtern(false);
procedure.setDeclaredInline(false);
}

View File

@ -100,12 +100,23 @@ public class VariableBuilder {
declaredSymbol = null;
if(declaredSymbol != null) {
if(!(declaredSymbol instanceof Variable))
if(!(declaredSymbol instanceof Variable declaredVar))
throw new CompileError("Error! Conflicting declarations for: " + variable.getFullName());
Variable declaredVar = (Variable) declaredSymbol;
if(!declaredVar.isDeclarationOnly() && !variable.isDeclarationOnly())
if(!declaredVar.isDeclarationOnly() && !variable.isDeclarationOnly()) {
if(declaredVar.isAsmImportLibraryGlobal()) {
// In the case the declaration of the import is BEFORE the actual declaration/definition.
return declaredVar;
}
if(variable.isAsmImportLibraryGlobal()) {
// In the case the declaration of the import is AFTER the actual declaration and/or definition of the procedure.
// In this case we clean up the original and return the variable, which is the imported version.
this.scope.remove(declaredSymbol);
this.scope.add(variable);
return variable;
}
throw new CompileError("Error! Redefinition of variable: " + variable.getFullName());
if(!SymbolTypeConversion.variableDeclarationMatch(declaredVar, variable))
}
if(!SymbolTypeConversion.variableDeclarationMatch(declaredVar, variable) && !variable.isAsmExportLibraryGlobal())
throw new CompileError("Error! Conflicting declarations for: " + variable.getFullName());
// Update the variable with the definition

View File

@ -121,7 +121,8 @@ public abstract class Scope implements Symbol {
public <T extends Symbol> T add(T symbol) {
if(symbols.get(symbol.getLocalName()) != null) {
throw new CompileError("Symbol already declared " + symbol.getLocalName());
// throw new CompileError("Symbol already declared " + symbol.getLocalName());
// symbols.remove(symbol.getLocalName());
}
symbols.put(symbol.getLocalName(), symbol);
return symbol;

View File

@ -546,6 +546,10 @@ public class Variable implements Symbol {
return MemoryArea.MAIN_MEMORY.equals(getMemoryArea());
}
public boolean isMemoryAreaZP() {
return MemoryArea.ZEROPAGE_MEMORY.equals(getMemoryArea());
}
public Integer getMemoryAlignment() {
return memoryAlignment;
}

View File

@ -222,7 +222,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
if (program.getAsmLibraryName() == null) {
// 820/17 - For each imported library, call the start procedure of the library.
for(AsmLibrary importedLibrary: program.getAsmImports().values()) {
String procedureStartName = "__" + importedLibrary.getLabelName() + "_start";
String procedureStartName = "__" + importedLibrary.getAsmLibraryIdentifier() + "_start";
Procedure procedureStart = this.program.getScope().getProcedure(new ProcedureRef(procedureStartName));
if(procedureStart != null) {
startSequence.addStatement(new StatementCall(null, procedureStartName, new ArrayList<>(), StatementSource.NONE, Comment.NO_COMMENTS));
@ -354,31 +354,41 @@ 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.
String asmLibraryName = "";
String asmLibraryName = null;
boolean exportAll = true;
if(ctx.pragmaParam().size() == 0) {
StatementSource stmtSource = new StatementSource(ctx);
Path filePath = Paths.get(stmtSource.getFileName());
asmLibraryName = filePath.getFileName().toString();
asmLibraryName = FileNameUtils.removeExtension(asmLibraryName);
String mainBase = program.getOutputFileManager().getOutputBaseName();
String stmtBase = filePath.getFileName().toString();
stmtBase = FileNameUtils.removeExtension(stmtBase);
if(mainBase.equals(stmtBase)) {
asmLibraryName = stmtBase;
}
} else if(ctx.pragmaParam().size() == 1) {
asmLibraryName = pragmaParamString(pragmaParamSingle(ctx));
exportAll = false;
} else {
throw new CompileError("#pragma asm_library: too many parameters!", new StatementSource(ctx));
}
if(!program.hasAsmImportLibrary(asmLibraryName)) {
program.setAsmLibraryName(asmLibraryName);
program.addAsmExportLibrary(asmLibraryName, exportAll);
if(asmLibraryName != null) {
if(!program.hasAsmImportLibrary(asmLibraryName)) {
program.setAsmLibraryName(asmLibraryName);
program.addAsmExportLibrary(asmLibraryName, exportAll);
}
}
}
case CParser.PRAGMA_ASM_EXPORT -> { // Defines that an C routine is exported into the asm_library.
String libraryName = program.getAsmLibraryName();
Procedure.CallingConvention callingConvention = currentCallingConvention;
List<String> procedures = pragmaParamAsmExportProcedures(ctx.pragmaParam());
String asmExportLibraryName = program.getAsmLibraryName();
AsmExportLibrary asmLibrary = program.addAsmExportLibrary(libraryName, false);
program.addAsmExportProcedures(asmLibrary, callingConvention, procedures);
if(asmExportLibraryName != null) {
// Only export the procedures when there is a library declared in the main program!
// Otherwise ignore the asm_export #pragma.
Procedure.CallingConvention callingConvention = currentCallingConvention;
List<String> procedures = pragmaParamAsmExportProcedures(ctx.pragmaParam());
AsmExportLibrary asmExportLibrary = program.addAsmExportLibrary(asmExportLibraryName, false);
program.addAsmExportProcedures(asmExportLibrary, 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));
@ -658,6 +668,10 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
program.createProcedureCompilation(procedure.getRef());
}
/**
* If the procedure was define before the actual Library Import was used,
* then the defined procedure must be removed, and only the declaration must be kept.
*/
// if(procedure.isDeclaredExtern() && !defineProcedure) {
@ -1439,7 +1453,6 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
program.addAsmImportLibrary(((Directive.AsmImportDirective) directive).getAsmLibrary());
} else if(directive instanceof Directive.AsmExportDirective) {
procedure.setAsmExportLibrary(((Directive.AsmExportDirective) directive).getAsmLibrary());
// TODO: add also the asmExportLibrary ...
} else if(directive instanceof Directive.CallingConvention) {
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
} else if(directive instanceof Directive.Interrupt) {
@ -1450,6 +1463,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
procedure.setDeclaredIntrinsic(true);
} else if(directive instanceof Directive.Extern) {
procedure.setDeclaredExtern(true);
// TODO: check this ...
//} else {
// throw new CompileError("Unsupported function directive " + directive.getName(), source);
}
@ -2248,10 +2262,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
}
@Override
public Object visitStructDef(KickCParser.StructDefContext ctx) {
public Object visitStructForwardDef(KickCParser.StructForwardDefContext ctx) {
boolean isUnion = ctx.UNION() != null;
String structDefName;
if(ctx.NAME() != null) {
structDefName = ctx.NAME().getText();
} else {
structDefName = program.getScope().allocateIntermediateVariableName();
}
StructDefinition structDefinition = program.getScope().addStructDefinition(structDefName, isUnion);
varDecl.setDeclType(structDefinition.getType());
return null;
}
@Override
public Object visitStructDefinition(KickCParser.StructDefinitionContext ctx) {
boolean isUnion = ctx.UNION() != null;
String structDefName;
if(ctx.NAME() != null) {
structDefName = ctx.NAME().getText();

View File

@ -116,9 +116,6 @@ public class Pass4CodeGeneration {
if (program.getAsmLibraryName() != null) {
asm.startChunk(currentScope, null, "Library");
asm.addNamespaceBegin(program.getAsmLibraryLabel());
} else {
asm.startChunk(currentScope, null, "Main Program");
asm.addNamespaceBegin(program.getOutputFileManager().getOutputBaseName());
}
asm.startChunk(currentScope, null, "Upstart");
@ -248,9 +245,9 @@ public class Pass4CodeGeneration {
addAbsoluteAddressData(asm, ScopeRef.ROOT, null);
// #820/24 - Close the namespace of the asm library.
// if (program.getAsmLibraryName() != null) {
if (program.getAsmLibraryName() != null) {
asm.addNamespaceEnd();
// }
}
// #820/34 - Export the global data, but when the compilation is an .asm export library.
if (program.getAsmLibraryName() != null) {
@ -504,18 +501,22 @@ public class Pass4CodeGeneration {
Scope scope = program.getScope().getScope(scopeRef);
Set<String> added = new LinkedHashSet<>();
Collection<Variable> scopeConstants = scope.getAllConstants(false);
String asmExportLibraryName = program.getAsmLibraryName();
// First add all constants without data that can become constants in KickAsm
for (Variable constantVar : scopeConstants) {
if (!hasData(constantVar)) {
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
if ((constantVar.getLocalName().equals("SIZEOF_" + var.getType().getConstantFriendlyName()))) {
// Use constant otherwise
added.add(asmName);
// Find the constant value calculation
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
addConstant(asmName, constantVar, asmConstant, asm);
if(var.isStruct()) {
String typeVar = "SIZEOF_" + var.getType().getConstantFriendlyName();
// First add all constants without data that can become constants in KickAsm
for (Variable constantVar : scopeConstants) {
if (!hasData(constantVar)) {
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
if (constantVar.getLocalName().equals(typeVar)) {
// Use constant otherwise
added.add(asmName);
// Find the constant value calculation
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
addConstant(asmName, constantVar, asmConstant, asm);
}
}
}
}
@ -1075,6 +1076,7 @@ public class Pass4CodeGeneration {
} else {
if (toProcedure.getAsmLibrary() == null) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);

View File

@ -113,7 +113,10 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
Collection<ScopeRef> threads1 = getEquivalenceClassThreads(ec1, program, threadHeads, callGraph);
Collection<ScopeRef> threads2 = getEquivalenceClassThreads(ec2, program, threadHeads, callGraph);
if(threads1.isEmpty() || threads2.isEmpty()) {
return true;
// Global variables in .asm libraries are not linked to main because it is not there!
// Therefore, the threads can be empty if the equivalence class concerns a global variable.
// In this case, never coalesce with a global variable (those zero-pages should stay persistent)!
return false;
}
@ -147,7 +150,8 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
ScopeRef scopeRef = variable.getScope().getRef();
if(scopeRef.equals(ScopeRef.ROOT) ) {
Procedure localProcedure = program.getScope().getLocalProcedure(SymbolRef.MAIN_PROC_NAME);
if(localProcedure != null && program.getAsmLibraryName() != "") {
// Global variables in .asm libraries are not linked to main because it is not there!
if(localProcedure != null && program.getAsmLibraryName() != null) {
ProcedureRef mainThreadHead = localProcedure.getRef();
if(!threads.contains(mainThreadHead)) {
threads.add(mainThreadHead);
@ -190,12 +194,14 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
return false;
// check if either are in the reserved zp registers
if(register1 instanceof Registers.RegisterZpMem) {
int zp = ((Registers.RegisterZpMem) register1).getZp();
Integer 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();
Integer zp = ((Registers.RegisterZpMem) register2).getZp();
if(program.getReservedZps().contains(zp))
return false;
if(register2.isAddressHardcoded())

View File

@ -0,0 +1,43 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.Directive;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.VariableReferenceInfos;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.StructUnwoundPlaceholder;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.ListIterator;
import java.util.Set;
/**
* Declare all global variables in a library as exported. But only in a library!
*/
public class PassNAsmLibraryGlobalVarsExport extends Pass2SsaOptimization {
public PassNAsmLibraryGlobalVarsExport(Program program) {
super(program);
}
@Override
public boolean step() {
boolean modified = false;
Scope scope = getProgram().getScope().getScope(ScopeRef.ROOT); //
Collection<Variable> scopeConstants = scope.getAllVars(false);
String asmLibraryName = getProgram().getAsmLibraryName();
if(asmLibraryName != null) {
for (Variable constantVar : scopeConstants) {
if(constantVar.isKindLoadStore()) {
constantVar.setAsmExportLibrary(asmLibraryName);
}
}
}
return modified;
}
}

View File

@ -1,4 +1,4 @@
// Commodore 64 conio.h implementation
// Commodore 128 conio.h implementation
#include <conio.h>
#include <c128.h>
@ -16,7 +16,7 @@
#endif
// The default text color
#ifndef CONIO_TEXTCOLOR_DEFAULT
#define CONIO_TEXTCOLOR_DEFAULT LIGHT_BLUE
#define CONIO_TEXTCOLOR_DEFAULT LIGHT_GREEN
#endif
// Use the shared CMB flat memory implementation

View File

@ -26,7 +26,7 @@
#define CONIO_TEXTCOLOR_DEFAULT WHITE // The default text color
#define CONIO_BACKCOLOR_DEFAULT BLUE // The default back color
typedef struct {
typedef struct __cx16_conio_s {
unsigned char cursor_x; ///< current cursor x-position
unsigned char cursor_y; ///< current cursor y-position
unsigned char layer;

View File

@ -1,5 +0,0 @@
extern __asm_import("set") char set_total;
extern __asm_import("set") char set_array[10];
extern __varcall __asm_import("set") __zp_reserve( 2,3 ) __zp(2) char get(__zp(3) char idx);

View File

@ -1,20 +0,0 @@
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#include <set_asm.h>
// #include "set.c"
#include <conio.h>
#include <stdio.h>
#include <printf.h>
__export char r; // Ensure that r does not get deleted by the optimizer by exporting it.
void main() {
clrscr();
for(char i=0; i<set_total; i++) {
char data = set_array[i];
printf("set[%u] = %u\n", i, data);
}
}

View File

@ -1,23 +0,0 @@
#pragma encoding(screencode_mixed)
#pragma var_model(mem)
#pragma asm_library
#pragma calling(__varcall)
__export __asm_export char set_total = 10;
__export __asm_export char set_array[10] = {0,1,2,3,4,5,6,7,8,9};
char get(char idx) {
return set_array[idx];
}
void set(char idx, char n) {
set_array[idx] = n;
}
char sum2(char idx, char n) {
char r = set_array[idx];
idx++;
r += set_array[idx];
return r;
}

View File

@ -1,5 +0,0 @@
extern __asm_import("set") char set_total;
extern __asm_import("set") char set_array[10];
extern __varcall __asm_import("set") __zp_reserve( 2,3 ) __zp(2) char get(__zp(3) char idx);

View File

@ -1,29 +0,0 @@
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#define ARRAY_SIZE 10
#define DUMMY_SIZE 1024
struct set_array_s {
char x[ARRAY_SIZE];
char dummy[DUMMY_SIZE];
char y[ARRAY_SIZE];
};
#include <set_asm.h>
//#include "set.c"
#include <conio.h>
#include <stdio.h>
#include <printf.h>
__export char r; // Ensure that r does not get deleted by the optimizer by exporting it.
void main() {
clrscr();
for(char i=0; i<set_total; i++) {
char data = set_array.y[i];
printf("set[%u] = %u\n", i, data);
}
}

View File

@ -1,42 +0,0 @@
#pragma encoding(screencode_mixed)
#pragma var_model(mem)
#pragma asm_library
#pragma calling(__varcall)
//#include <conio.h>
#define ARRAY_SIZE 10
#define DUMMY_SIZE 1024
struct set_array_s {
char x[ARRAY_SIZE];
char dummy[DUMMY_SIZE];
char y[ARRAY_SIZE];
};
__asm_export char set_total = 10;
__asm_export struct set_array_s set_array;
char get_x(char idx) {
return set_array.x[idx];
}
char get_y(char idx) {
return set_array.y[idx];
}
void set_x(char idx, char n) {
set_array.x[idx] = n;
}
void set_y(char idx, char n) {
set_array.y[idx] = n;
}
char sum2(char idx) {
char r = set_array.x[idx];
idx++;
r += set_array.y[idx];
return r;
}

View File

@ -0,0 +1,25 @@
// Create an .asm library of important conio functions.
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#pragma asm_library
#pragma calling(__stackcall)
#pragma asm_export(cputc)
#pragma calling(__varcall)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
#pragma asm_export(wherex, wherey)
#pragma asm_export(screensize, screensizex, screensizey, cputln)
#pragma asm_export(cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor)
#pragma asm_export(kbhit, cursor, scroll)
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma calling(__phicall)
#include <conio.h>

View File

@ -1,27 +1,21 @@
#pragma var_model(zp)
#include <lru-cache_asm.h>
#include <conio-zp-varcall_asm.h>
#include <lru-cache-zp-varcall_asm.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include "lru-cache.h"
#include "lru-cache-zp-varcall.h"
#include <division.h>
volatile unsigned char row = 0;
volatile unsigned char col = 0;
volatile unsigned char count = 0;
// lru_cache_table_t lru_cache;
#include <6502.h>
void wait_key()
{
while (!kbhit())
;
// while (!kbhit())
// ;
}
void display()
{
lru_cache_display(0, 2);
@ -62,28 +56,39 @@ void delete (lru_cache_key_t key)
display();
}
void main() {
void main()
{
SEI();
lru_cache_init();
clrscr();
scroll(0);
insert(0x0, 0x0);
insert(0x80, 0x80);
insert(0x100, 0x100);
insert(0x1, 0x1);
insert(0x200, 0x200);
insert(0x2, 0x2);
insert(0x82, 0x82);
delete(0x0);
delete(0x100);
delete(0x80);
delete(0x1);
insert(0x201, 0x201);
insert(0x81, 0x81);
delete(0x2);
delete(0x81);
delete(0x201);
delete(0x82);
delete(0x200);
int cache[128];
// char ch = kbhit();
char ch = 0;
do {
if (lru_cache_is_max()) {
lru_cache_key_t last = lru_cache_find_last();
delete(last);
} else {
lru_cache_key_t key = rand() % 0x20;
lru_cache_data_t data = get(key);
if (data != LRU_CACHE_NOTHING) {
data += 1;
if (data < 20) {
set(key, data);
} else {
delete(key);
}
} else {
insert(key, 0);
}
}
// ch = kbhit();
} while (ch != 'x');
CLI();
}

View File

@ -10,13 +10,12 @@
*
*/
// // Register the compilation as an export library output artefact.
// // There is no main() function!
#pragma asm_library
#pragma calling(__varcall)
#pragma var_model(zp)
#pragma asm_library
#pragma calling(__varcall)
#pragma asm_export(lru_cache_init)
#pragma asm_export(lru_cache_find_last)
#pragma asm_export(lru_cache_is_max)
@ -30,10 +29,13 @@
#pragma calling(__phicall)
#include <conio-zp-varcall_asm.h>
#define LRU_CACHE_MAX 24
#define LRU_CACHE_SIZE 32
#include "lru-cache.h"
#include "lru-cache-zp-varcall.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>

View File

@ -0,0 +1,65 @@
/**
* @file lru_cache.h
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @brief Least Recently Used Cache using a hash table and a double linked list, searchable.
* To store fast and retrieve fast elements from an array. To search fast the last used element and delete it.
* @version 0.1
* @date 2022-09-02
*
* @copyright Copyright (c) 2022
*
*/
#ifndef LRU_CACHE_SIZE
#define LRU_CACHE_SIZE 128
#endif
#ifndef LRU_CACHE_MAX
#define LRU_CACHE_MAX 96
#endif
#define LRU_CACHE_NOTHING 0xFFFF
#define LRU_CACHE_USED 0xFFFE
#define LRU_CACHE_INDEX_NULL 0xFF
typedef unsigned int lru_cache_key_t;
typedef unsigned int lru_cache_data_t;
typedef unsigned char lru_cache_index_t;
typedef struct lru_cache_table_s {
lru_cache_key_t key[LRU_CACHE_SIZE];
lru_cache_data_t data[LRU_CACHE_SIZE];
lru_cache_index_t prev[LRU_CACHE_SIZE];
lru_cache_index_t next[LRU_CACHE_SIZE];
lru_cache_index_t link[LRU_CACHE_SIZE];
lru_cache_index_t count;
lru_cache_index_t first;
lru_cache_index_t last;
lru_cache_index_t size;
} lru_cache_table_t;
void lru_cache_init();
lru_cache_index_t lru_cache_hash(lru_cache_key_t key);
lru_cache_key_t lru_cache_find_last();
inline bool lru_cache_is_max();
lru_cache_index_t lru_cache_index(lru_cache_key_t key);
lru_cache_data_t lru_cache_get(lru_cache_index_t index);
lru_cache_data_t lru_cache_set(lru_cache_index_t index, lru_cache_data_t data);
lru_cache_data_t lru_cache_data(lru_cache_index_t index);
lru_cache_index_t lru_cache_insert(lru_cache_key_t key, lru_cache_data_t data);
lru_cache_data_t lru_cache_delete(lru_cache_key_t key);
void lru_cache_display(char x, char y);

View File

@ -0,0 +1,25 @@
// Create an .asm library of important conio functions.
#pragma encoding(screencode_mixed)
#pragma var_model(mem)
#pragma asm_library
#pragma calling(__stackcall)
#pragma asm_export(cputc)
#pragma calling(__varcall)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
#pragma asm_export(wherex, wherey)
#pragma asm_export(screensize, screensizex, screensizey, cputln)
#pragma asm_export(cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor)
#pragma asm_export(kbhit, cursor, scroll)
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma calling(__phicall)
#include <conio.h>

View File

@ -0,0 +1,97 @@
#pragma var_model(mem)
// #include <conio-mem-varcall_asm.h>
#include <lru-cache-mem-varcall_asm.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include "lru-cache-mem-varcall.h"
#include <division.h>
#include <6502.h>
void wait_key()
{
// while (!kbhit())
// ;
}
void display()
{
lru_cache_display(0, 2);
wait_key();
}
lru_cache_data_t get(lru_cache_key_t key)
{
gotoxy(0, 0);
printf("get: %04x ", key);
lru_cache_data_t data = lru_cache_get(lru_cache_index(key));
printf(":%04x", data);
display();
return data;
}
void set(lru_cache_key_t key, lru_cache_data_t data)
{
gotoxy(0, 0);
printf("set: %04x:%04x", key, data);
lru_cache_set(lru_cache_index(key), data);
display();
}
void insert(lru_cache_key_t key, lru_cache_data_t data)
{
gotoxy(0, 0);
printf("add: %04x:%04x ", key, data);
lru_cache_insert(key, data);
display();
}
void delete (lru_cache_key_t key)
{
gotoxy(0, 0);
printf("del: %04x ", key);
lru_cache_delete(key);
display();
}
void main()
{
SEI();
lru_cache_init();
clrscr();
scroll(0);
int cache[128];
// char ch = kbhit();
char ch = 0;
do {
if (lru_cache_is_max()) {
lru_cache_key_t last = lru_cache_find_last();
delete(last);
} else {
lru_cache_key_t key = rand() % 0x20;
lru_cache_data_t data = get(key);
if (data != LRU_CACHE_NOTHING) {
data += 1;
if (data < 20) {
set(key, data);
} else {
delete(key);
}
} else {
insert(key, 0);
}
}
// ch = kbhit();
} while (ch != 'x');
CLI();
}

View File

@ -1,3 +1,4 @@
/**
* @file lru-cache.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
@ -10,13 +11,11 @@
*
*/
// // Register the compilation as an export library output artefact.
// // There is no main() function!
#pragma var_model(mem, local_mem, global_zp, parameter_zp)
#pragma asm_library
#pragma calling(__varcall)
#pragma var_model(zp)
#pragma asm_export(lru_cache_init)
#pragma asm_export(lru_cache_find_last)
#pragma asm_export(lru_cache_is_max)
@ -30,10 +29,13 @@
#pragma calling(__phicall)
#include <conio-mem-varcall_asm.h>
#define LRU_CACHE_MAX 24
#define LRU_CACHE_SIZE 32
#include "lru-cache.h"
#include "lru-cache-mem-varcall.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
@ -405,7 +407,7 @@ void lru_cache_display(char x, char y) {
lru_cache_index_t count = 0;
col = 0;
while (count < 8) {
while (count < 6) {
if (count < lru_cache.count)
printf(" %4x", lru_cache.key[index]);
else

View File

@ -0,0 +1,65 @@
/**
* @file lru_cache.h
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @brief Least Recently Used Cache using a hash table and a double linked list, searchable.
* To store fast and retrieve fast elements from an array. To search fast the last used element and delete it.
* @version 0.1
* @date 2022-09-02
*
* @copyright Copyright (c) 2022
*
*/
#ifndef LRU_CACHE_SIZE
#define LRU_CACHE_SIZE 128
#endif
#ifndef LRU_CACHE_MAX
#define LRU_CACHE_MAX 96
#endif
#define LRU_CACHE_NOTHING 0xFFFF
#define LRU_CACHE_USED 0xFFFE
#define LRU_CACHE_INDEX_NULL 0xFF
typedef unsigned int lru_cache_key_t;
typedef unsigned int lru_cache_data_t;
typedef unsigned char lru_cache_index_t;
typedef struct lru_cache_table_s {
lru_cache_key_t key[LRU_CACHE_SIZE];
lru_cache_data_t data[LRU_CACHE_SIZE];
lru_cache_index_t prev[LRU_CACHE_SIZE];
lru_cache_index_t next[LRU_CACHE_SIZE];
lru_cache_index_t link[LRU_CACHE_SIZE];
lru_cache_index_t count;
lru_cache_index_t first;
lru_cache_index_t last;
lru_cache_index_t size;
} lru_cache_table_t;
void lru_cache_init();
lru_cache_index_t lru_cache_hash(lru_cache_key_t key);
lru_cache_key_t lru_cache_find_last();
inline bool lru_cache_is_max();
lru_cache_index_t lru_cache_index(lru_cache_key_t key);
lru_cache_data_t lru_cache_get(lru_cache_index_t index);
lru_cache_data_t lru_cache_set(lru_cache_index_t index, lru_cache_data_t data);
lru_cache_data_t lru_cache_data(lru_cache_index_t index);
lru_cache_index_t lru_cache_insert(lru_cache_key_t key, lru_cache_data_t data);
lru_cache_data_t lru_cache_delete(lru_cache_key_t key);
void lru_cache_display(char x, char y);

View File

@ -1,15 +1,15 @@
// Create an .asm library of imporant conio functions.
// Export to conio_var.asm
// Create an .asm library of important conio functions.
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Declare this compilation to generate the conio_var_asm file!
#pragma asm_library
// Declare the procedures to be exported to the .asm libary with the indicated calling convention.
#pragma calling(__varcall)
#pragma calling(__stackcall)
#pragma asm_export(cputc)
#pragma calling(__stackcall)
#pragma asm_export(__conio_var_start, conio_x16_init)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
@ -20,14 +20,7 @@
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma calling(__stackcall)
#pragma asm_export(cputc )
#pragma calling(__phicall)
// Allocate local procedure variables to memory.
#pragma var_model(zp)
#include <conio.h>
//#include <cx16-conio.h>

View File

@ -1,11 +1,13 @@
#pragma var_model(zp)
#pragma calling(__phicall)
#include <lru-cache_asm.h>
#include <lru-cache-zp-stackcall_asm.h>
#include <conio-zp-stackcall_asm.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include "lru-cache.h"
#include "lru-cache-zp-stackcall.h"
#include <division.h>
void wait_key()

View File

@ -0,0 +1,418 @@
/**
* @file lru-cache.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @brief Least Recently Used Cache using a hash table and a double linked list, searchable.
* To store fast and retrieve fast elements from an array. To search fast the last used element and delete it.
* @version 0.1
* @date 2022-09-02
*
* @copyright Copyright (c) 2022
*
*/
#pragma var_model(zp)
#pragma asm_library
#pragma calling(__stackcall)
#pragma asm_export(lru_cache_init)
#pragma asm_export(lru_cache_find_last)
#pragma asm_export(lru_cache_is_max)
#pragma asm_export(lru_cache_index)
#pragma asm_export(lru_cache_get)
#pragma asm_export(lru_cache_set)
#pragma asm_export(lru_cache_data)
#pragma asm_export(lru_cache_insert)
#pragma asm_export(lru_cache_delete)
#pragma asm_export(lru_cache_display)
#pragma calling(__phicall)
#include <conio-zp-stackcall_asm.h>
#define LRU_CACHE_MAX 24
#define LRU_CACHE_SIZE 32
#include "lru-cache-zp-stackcall.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
lru_cache_table_t lru_cache;
void lru_cache_init()
{
// The size of the elements can never exceed 256 bytes!!!
memset_fast(lru_cache.key, 0xFF, sizeof(lru_cache_key_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.data, 0xFF, sizeof(lru_cache_data_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.next, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.prev, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.link, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
lru_cache.first = 0xFF;
lru_cache.last = 0xFF;
lru_cache.count = 0;
lru_cache.size = LRU_CACHE_SIZE;
}
// __mem unsigned char lru_cache_seed;
lru_cache_index_t lru_cache_hash(lru_cache_key_t key)
{
lru_cache_index_t hash_result = (lru_cache_index_t)(key % LRU_CACHE_SIZE);
return hash_result;
}
// inline lru_cache_index_t lru_cache_hash(lru_cache_key_t key) {
// lru_cache_seed = key;
// asm {
// lda lru_cache_seed
// beq !doEor+
// asl
// beq !noEor+
// bcc !noEor+
// !doEor: eor #$2b
// !noEor: sta lru_cache_seed
// }
// return lru_cache_seed % LRU_CACHE_SIZE;
// }
inline bool lru_cache_is_max()
{
return lru_cache.count >= LRU_CACHE_MAX;
}
lru_cache_key_t lru_cache_find_last()
{
return lru_cache.key[lru_cache.last];
}
lru_cache_index_t lru_cache_find_empty(lru_cache_index_t index)
{
while (lru_cache.key[index] != LRU_CACHE_NOTHING) {
index++;
index %= LRU_CACHE_SIZE;
}
return index;
}
lru_cache_index_t lru_cache_find_duplicate(lru_cache_index_t index, lru_cache_index_t link)
{
// First find the last duplicate node.
while (lru_cache.link[index] != link && lru_cache.link[index] != LRU_CACHE_INDEX_NULL) {
index = lru_cache.link[index];
}
return index;
}
lru_cache_index_t lru_cache_index(lru_cache_key_t key)
{
lru_cache_index_t index = lru_cache_hash(key);
// Search till index == 0xFF, following the links.
while (index != LRU_CACHE_INDEX_NULL) {
if (lru_cache.key[index] == key) {
return index;
}
index = lru_cache.link[index];
}
return LRU_CACHE_INDEX_NULL;
}
lru_cache_data_t lru_cache_get(lru_cache_index_t index)
{
if (index != LRU_CACHE_INDEX_NULL) {
lru_cache_data_t data = lru_cache.data[index];
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
// Delete the node from the list.
lru_cache.next[prev] = next;
//lru_cache.next[next] = prev;
lru_cache.prev[next] = prev;
//lru_cache.prev[prev] = next;
// Reassign first and last node.
if (index == lru_cache.first) {
lru_cache.first = next;
}
if (index == lru_cache.last) {
lru_cache.last = prev;
}
// Now insert the node as the first node in the list.
lru_cache.next[index] = lru_cache.first;
lru_cache.prev[lru_cache.first] = index;
lru_cache.next[lru_cache.last] = index;
lru_cache.prev[index] = lru_cache.last;
// Now the first node in the list is the node referenced!
// All other nodes are moved one position down!
lru_cache.first = index;
lru_cache.last = lru_cache.prev[index];
return data;
}
return LRU_CACHE_NOTHING;
}
lru_cache_data_t lru_cache_set(lru_cache_index_t index, lru_cache_data_t data)
{
if (index != LRU_CACHE_INDEX_NULL) {
lru_cache.data[index] = data;
lru_cache_data_t data = lru_cache_get(index);
return data;
}
return LRU_CACHE_NOTHING;
}
lru_cache_data_t lru_cache_data(lru_cache_index_t index)
{
return lru_cache.data[index];
}
inline void lru_cache_move_link(lru_cache_index_t link, lru_cache_index_t index)
{
// Here we move the node at the index to the new link, and set the head link to the new link.
lru_cache_index_t l = lru_cache.link[index];
lru_cache.link[link] = l;
// This results in incorrect code!
// lru_cache.key[link] = lru_cache.key[index];
// lru_cache.data[link] = lru_cache.data[index];
lru_cache_key_t key = lru_cache.key[index];
lru_cache.key[link] = key;
lru_cache_data_t data = lru_cache.data[index];
lru_cache.data[link] = data;
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
lru_cache.next[link] = next;
lru_cache.prev[link] = prev;
lru_cache.next[prev] = link;
lru_cache.prev[next] = link;
// todo first and last
if (lru_cache.last == index) {
lru_cache.last = link;
}
if (lru_cache.first == index) {
lru_cache.first = link;
}
lru_cache.key[index] = LRU_CACHE_NOTHING;
lru_cache.data[index] = LRU_CACHE_NOTHING;
lru_cache.next[index] = LRU_CACHE_INDEX_NULL;
lru_cache.prev[index] = LRU_CACHE_INDEX_NULL;
lru_cache.link[index] = LRU_CACHE_INDEX_NULL;
}
lru_cache_index_t lru_cache_find_head(lru_cache_index_t index)
{
lru_cache_key_t key_link = lru_cache.key[index];
lru_cache_index_t head_link = lru_cache_hash(key_link);
if (head_link != index) {
return head_link;
} else {
return LRU_CACHE_INDEX_NULL;
}
}
lru_cache_index_t lru_cache_insert(lru_cache_key_t key, lru_cache_data_t data)
{
lru_cache_index_t index = lru_cache_hash(key);
// Check if there is already a link node in place in the hash table at the index.
lru_cache_index_t link_head = lru_cache_find_head(index);
lru_cache_index_t link_prev = lru_cache_find_duplicate(link_head, index);
if (lru_cache.key[index] != LRU_CACHE_NOTHING && link_head != LRU_CACHE_INDEX_NULL) {
// There is already a link node, so this node is not a head node and needs to be moved.
// Get the head node of this chain, we know this because we can get the head of the key.
// The link of the head_link must be changed once the new place of the link node has been found.
lru_cache_index_t link = lru_cache_find_empty(index);
lru_cache_move_link(link, index);
lru_cache.link[link_prev] = link;
}
// The index is at the head of a chain and is either duplicates or empty.
// We just follow the duplicate chain and find the last duplicate.
lru_cache_index_t index_prev = lru_cache_find_duplicate(index, LRU_CACHE_INDEX_NULL);
// From the last duplicate position, we search for the first free node.
index = lru_cache_find_empty(index_prev);
// We set the link of the free node to INDEX_NULL,
// and point the link of the previous node to the empty node.
// index != index_prev indicates there is a duplicate chain.
lru_cache.link[index] = LRU_CACHE_INDEX_NULL;
if (index_prev != index) {
lru_cache.link[index_prev] = index;
}
// Now assign the key and the data.
lru_cache.key[index] = key;
lru_cache.data[index] = data;
// And set the lru chain.
if (lru_cache.first == 0xff) {
lru_cache.first = index;
}
if (lru_cache.last == 0xff) {
lru_cache.last = index;
}
// Now insert the node as the first node in the list.
lru_cache.next[index] = lru_cache.first;
lru_cache.prev[lru_cache.first] = index;
lru_cache.next[lru_cache.last] = index;
lru_cache.prev[index] = lru_cache.last;
lru_cache.first = index;
lru_cache.count++;
return index;
}
lru_cache_data_t lru_cache_delete(lru_cache_key_t key)
{
lru_cache_index_t index = lru_cache_hash(key);
// move in array until an empty
lru_cache_index_t index_prev = LRU_CACHE_INDEX_NULL;
while (lru_cache.key[index] != LRU_CACHE_NOTHING) {
if (lru_cache.key[index] == key) {
lru_cache_data_t data = lru_cache.data[index];
// First remove the index node.
lru_cache.key[index] = LRU_CACHE_NOTHING;
lru_cache.data[index] = LRU_CACHE_NOTHING;
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
lru_cache.next[index] = LRU_CACHE_INDEX_NULL;
lru_cache.prev[index] = LRU_CACHE_INDEX_NULL;
if (lru_cache.next[index] == index) {
// Reset first and last node.
lru_cache.first = 0xff;
lru_cache.last = 0xff;
} else {
// Delete the node from the list.
lru_cache.next[prev] = next;
lru_cache.prev[next] = prev;
// Reassign first and last node.
if (index == lru_cache.first) {
lru_cache.first = next;
}
if (index == lru_cache.last) {
lru_cache.last = prev;
}
}
lru_cache_index_t link = lru_cache.link[index];
if (index_prev != LRU_CACHE_INDEX_NULL) {
// The node is not the first node but the middle of a list.
lru_cache.link[index_prev] = link;
}
if (link != LRU_CACHE_INDEX_NULL) {
// The head is the start of a duplicate chain.
// Simply move the next node in the duplicate chain as the new head.
lru_cache_move_link(index, link);
}
lru_cache.count--;
return data;
}
index_prev = index;
index = lru_cache.link[index];
}
return LRU_CACHE_NOTHING;
}
// Only for debugging
void lru_cache_display(char x, char y) {
unsigned char col = 0;
gotoxy(x, y);
printf("least recently used cache statistics\n");
printf("size:%02x, first:%02x, last:%02x, cnt:%2x\n\n", LRU_CACHE_SIZE, lru_cache.first, lru_cache.last, lru_cache.count);
printf(" ");
do {
printf(" %1x/%1x ", col, col + 8);
col++;
} while (col < 4);
printf("\n");
col = 0;
lru_cache_index_t index_row = 0;
do {
lru_cache_index_t index = index_row;
printf("%02x:", index);
do {
if (lru_cache.key[index] != LRU_CACHE_NOTHING) {
printf(" %04x:", lru_cache.key[index]);
printf("%02x", lru_cache.link[index]);
} else {
printf(" ----:--");
}
index++;
} while (index < index_row + 4);
printf("\n");
index = index_row;
printf(" :");
do {
printf(" %02x:", lru_cache.next[index]);
printf("%02x ", lru_cache.prev[index]);
index++;
} while (index < index_row + 4);
printf("\n");
index_row += 4;
} while (index_row < 4*4*2);
printf("\n");
printf("least recently used sequence\n");
lru_cache_index_t index = lru_cache.first;
lru_cache_index_t count = 0;
col = 0;
while (count < 6) {
if (count < lru_cache.count)
printf(" %4x", lru_cache.key[index]);
else
printf(" ");
//printf(" %4x %3uN %3uP ", lru_cache.key[cache_index], lru_cache.next[cache_index], lru_cache.prev[cache_index]);
index = lru_cache.next[index];
count++;
}
}

View File

@ -33,7 +33,7 @@ typedef unsigned char lru_cache_index_t;
typedef struct {
typedef struct lru_cache_s {
lru_cache_key_t key[LRU_CACHE_SIZE];
lru_cache_data_t data[LRU_CACHE_SIZE];
lru_cache_index_t prev[LRU_CACHE_SIZE];

View File

@ -0,0 +1,25 @@
// Create an .asm library of important conio functions.
#pragma encoding(screencode_mixed)
#pragma var_model(mem)
#pragma asm_library
#pragma calling(__stackcall)
#pragma asm_export(cputc)
#pragma calling(__stackcall)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
#pragma asm_export(wherex, wherey)
#pragma asm_export(screensize, screensizex, screensizey, cputln)
#pragma asm_export(cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor)
#pragma asm_export(kbhit, cursor, scroll, cscroll)
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma calling(__phicall)
#include <conio.h>

View File

@ -0,0 +1,90 @@
#pragma var_model(mem)
#pragma calling(__phicall)
#include <lru-cache-mem-stackcall_asm.h>
#include <conio-mem-stackcall_asm.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include "lru-cache-mem-stackcall.h"
#include <division.h>
void wait_key()
{
// while (!kbhit())
// ;
}
void display()
{
lru_cache_display(0, 2);
wait_key();
}
lru_cache_data_t get(lru_cache_key_t key)
{
gotoxy(0, 0);
printf("get: %04x ", key);
lru_cache_data_t data = lru_cache_get(lru_cache_index(key));
printf(":%04x", data);
display();
return data;
}
void set(lru_cache_key_t key, lru_cache_data_t data)
{
gotoxy(0, 0);
printf("set: %04x:%04x", key, data);
lru_cache_set(lru_cache_index(key), data);
display();
}
void insert(lru_cache_key_t key, lru_cache_data_t data)
{
gotoxy(0, 0);
printf("add: %04x:%04x ", key, data);
lru_cache_insert(key, data);
display();
}
void delete (lru_cache_key_t key)
{
gotoxy(0, 0);
printf("del: %04x ", key);
lru_cache_delete(key);
display();
}
void main()
{
lru_cache_init();
clrscr();
scroll(0);
int cache[128];
// char ch = kbhit();
char ch = 0;
do {
if (lru_cache_is_max()) {
lru_cache_key_t last = lru_cache_find_last();
delete(last);
} else {
lru_cache_key_t key = rand() % 0x20;
lru_cache_data_t data = get(key);
if (data != LRU_CACHE_NOTHING) {
data += 1;
if (data < 20) {
set(key, data);
} else {
delete(key);
}
} else {
insert(key, 0);
}
}
// ch = kbhit();
} while (ch != 'x');
}

View File

@ -0,0 +1,418 @@
/**
* @file lru-cache.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @brief Least Recently Used Cache using a hash table and a double linked list, searchable.
* To store fast and retrieve fast elements from an array. To search fast the last used element and delete it.
* @version 0.1
* @date 2022-09-02
*
* @copyright Copyright (c) 2022
*
*/
#pragma var_model(mem)
#pragma asm_library
#pragma calling(__stackcall)
#pragma asm_export(lru_cache_init)
#pragma asm_export(lru_cache_find_last)
#pragma asm_export(lru_cache_is_max)
#pragma asm_export(lru_cache_index)
#pragma asm_export(lru_cache_get)
#pragma asm_export(lru_cache_set)
#pragma asm_export(lru_cache_data)
#pragma asm_export(lru_cache_insert)
#pragma asm_export(lru_cache_delete)
#pragma asm_export(lru_cache_display)
#pragma calling(__phicall)
#include <conio-mem-stackcall_asm.h>
#define LRU_CACHE_MAX 24
#define LRU_CACHE_SIZE 32
#include "lru-cache-mem-stackcall.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
lru_cache_table_t lru_cache;
void lru_cache_init()
{
// The size of the elements can never exceed 256 bytes!!!
memset_fast(lru_cache.key, 0xFF, sizeof(lru_cache_key_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.data, 0xFF, sizeof(lru_cache_data_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.next, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.prev, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
memset_fast(lru_cache.link, 0xFF, sizeof(lru_cache_index_t)*LRU_CACHE_SIZE);
lru_cache.first = 0xFF;
lru_cache.last = 0xFF;
lru_cache.count = 0;
lru_cache.size = LRU_CACHE_SIZE;
}
// __mem unsigned char lru_cache_seed;
lru_cache_index_t lru_cache_hash(lru_cache_key_t key)
{
lru_cache_index_t hash_result = (lru_cache_index_t)(key % LRU_CACHE_SIZE);
return hash_result;
}
// inline lru_cache_index_t lru_cache_hash(lru_cache_key_t key) {
// lru_cache_seed = key;
// asm {
// lda lru_cache_seed
// beq !doEor+
// asl
// beq !noEor+
// bcc !noEor+
// !doEor: eor #$2b
// !noEor: sta lru_cache_seed
// }
// return lru_cache_seed % LRU_CACHE_SIZE;
// }
inline bool lru_cache_is_max()
{
return lru_cache.count >= LRU_CACHE_MAX;
}
lru_cache_key_t lru_cache_find_last()
{
return lru_cache.key[lru_cache.last];
}
lru_cache_index_t lru_cache_find_empty(lru_cache_index_t index)
{
while (lru_cache.key[index] != LRU_CACHE_NOTHING) {
index++;
index %= LRU_CACHE_SIZE;
}
return index;
}
lru_cache_index_t lru_cache_find_duplicate(lru_cache_index_t index, lru_cache_index_t link)
{
// First find the last duplicate node.
while (lru_cache.link[index] != link && lru_cache.link[index] != LRU_CACHE_INDEX_NULL) {
index = lru_cache.link[index];
}
return index;
}
lru_cache_index_t lru_cache_index(lru_cache_key_t key)
{
lru_cache_index_t index = lru_cache_hash(key);
// Search till index == 0xFF, following the links.
while (index != LRU_CACHE_INDEX_NULL) {
if (lru_cache.key[index] == key) {
return index;
}
index = lru_cache.link[index];
}
return LRU_CACHE_INDEX_NULL;
}
lru_cache_data_t lru_cache_get(lru_cache_index_t index)
{
if (index != LRU_CACHE_INDEX_NULL) {
lru_cache_data_t data = lru_cache.data[index];
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
// Delete the node from the list.
lru_cache.next[prev] = next;
//lru_cache.next[next] = prev;
lru_cache.prev[next] = prev;
//lru_cache.prev[prev] = next;
// Reassign first and last node.
if (index == lru_cache.first) {
lru_cache.first = next;
}
if (index == lru_cache.last) {
lru_cache.last = prev;
}
// Now insert the node as the first node in the list.
lru_cache.next[index] = lru_cache.first;
lru_cache.prev[lru_cache.first] = index;
lru_cache.next[lru_cache.last] = index;
lru_cache.prev[index] = lru_cache.last;
// Now the first node in the list is the node referenced!
// All other nodes are moved one position down!
lru_cache.first = index;
lru_cache.last = lru_cache.prev[index];
return data;
}
return LRU_CACHE_NOTHING;
}
lru_cache_data_t lru_cache_set(lru_cache_index_t index, lru_cache_data_t data)
{
if (index != LRU_CACHE_INDEX_NULL) {
lru_cache.data[index] = data;
lru_cache_data_t data = lru_cache_get(index);
return data;
}
return LRU_CACHE_NOTHING;
}
lru_cache_data_t lru_cache_data(lru_cache_index_t index)
{
return lru_cache.data[index];
}
inline void lru_cache_move_link(lru_cache_index_t link, lru_cache_index_t index)
{
// Here we move the node at the index to the new link, and set the head link to the new link.
lru_cache_index_t l = lru_cache.link[index];
lru_cache.link[link] = l;
// This results in incorrect code!
// lru_cache.key[link] = lru_cache.key[index];
// lru_cache.data[link] = lru_cache.data[index];
lru_cache_key_t key = lru_cache.key[index];
lru_cache.key[link] = key;
lru_cache_data_t data = lru_cache.data[index];
lru_cache.data[link] = data;
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
lru_cache.next[link] = next;
lru_cache.prev[link] = prev;
lru_cache.next[prev] = link;
lru_cache.prev[next] = link;
// todo first and last
if (lru_cache.last == index) {
lru_cache.last = link;
}
if (lru_cache.first == index) {
lru_cache.first = link;
}
lru_cache.key[index] = LRU_CACHE_NOTHING;
lru_cache.data[index] = LRU_CACHE_NOTHING;
lru_cache.next[index] = LRU_CACHE_INDEX_NULL;
lru_cache.prev[index] = LRU_CACHE_INDEX_NULL;
lru_cache.link[index] = LRU_CACHE_INDEX_NULL;
}
lru_cache_index_t lru_cache_find_head(lru_cache_index_t index)
{
lru_cache_key_t key_link = lru_cache.key[index];
lru_cache_index_t head_link = lru_cache_hash(key_link);
if (head_link != index) {
return head_link;
} else {
return LRU_CACHE_INDEX_NULL;
}
}
lru_cache_index_t lru_cache_insert(lru_cache_key_t key, lru_cache_data_t data)
{
lru_cache_index_t index = lru_cache_hash(key);
// Check if there is already a link node in place in the hash table at the index.
lru_cache_index_t link_head = lru_cache_find_head(index);
lru_cache_index_t link_prev = lru_cache_find_duplicate(link_head, index);
if (lru_cache.key[index] != LRU_CACHE_NOTHING && link_head != LRU_CACHE_INDEX_NULL) {
// There is already a link node, so this node is not a head node and needs to be moved.
// Get the head node of this chain, we know this because we can get the head of the key.
// The link of the head_link must be changed once the new place of the link node has been found.
lru_cache_index_t link = lru_cache_find_empty(index);
lru_cache_move_link(link, index);
lru_cache.link[link_prev] = link;
}
// The index is at the head of a chain and is either duplicates or empty.
// We just follow the duplicate chain and find the last duplicate.
lru_cache_index_t index_prev = lru_cache_find_duplicate(index, LRU_CACHE_INDEX_NULL);
// From the last duplicate position, we search for the first free node.
index = lru_cache_find_empty(index_prev);
// We set the link of the free node to INDEX_NULL,
// and point the link of the previous node to the empty node.
// index != index_prev indicates there is a duplicate chain.
lru_cache.link[index] = LRU_CACHE_INDEX_NULL;
if (index_prev != index) {
lru_cache.link[index_prev] = index;
}
// Now assign the key and the data.
lru_cache.key[index] = key;
lru_cache.data[index] = data;
// And set the lru chain.
if (lru_cache.first == 0xff) {
lru_cache.first = index;
}
if (lru_cache.last == 0xff) {
lru_cache.last = index;
}
// Now insert the node as the first node in the list.
lru_cache.next[index] = lru_cache.first;
lru_cache.prev[lru_cache.first] = index;
lru_cache.next[lru_cache.last] = index;
lru_cache.prev[index] = lru_cache.last;
lru_cache.first = index;
lru_cache.count++;
return index;
}
lru_cache_data_t lru_cache_delete(lru_cache_key_t key)
{
lru_cache_index_t index = lru_cache_hash(key);
// move in array until an empty
lru_cache_index_t index_prev = LRU_CACHE_INDEX_NULL;
while (lru_cache.key[index] != LRU_CACHE_NOTHING) {
if (lru_cache.key[index] == key) {
lru_cache_data_t data = lru_cache.data[index];
// First remove the index node.
lru_cache.key[index] = LRU_CACHE_NOTHING;
lru_cache.data[index] = LRU_CACHE_NOTHING;
lru_cache_index_t next = lru_cache.next[index];
lru_cache_index_t prev = lru_cache.prev[index];
lru_cache.next[index] = LRU_CACHE_INDEX_NULL;
lru_cache.prev[index] = LRU_CACHE_INDEX_NULL;
if (lru_cache.next[index] == index) {
// Reset first and last node.
lru_cache.first = 0xff;
lru_cache.last = 0xff;
} else {
// Delete the node from the list.
lru_cache.next[prev] = next;
lru_cache.prev[next] = prev;
// Reassign first and last node.
if (index == lru_cache.first) {
lru_cache.first = next;
}
if (index == lru_cache.last) {
lru_cache.last = prev;
}
}
lru_cache_index_t link = lru_cache.link[index];
if (index_prev != LRU_CACHE_INDEX_NULL) {
// The node is not the first node but the middle of a list.
lru_cache.link[index_prev] = link;
}
if (link != LRU_CACHE_INDEX_NULL) {
// The head is the start of a duplicate chain.
// Simply move the next node in the duplicate chain as the new head.
lru_cache_move_link(index, link);
}
lru_cache.count--;
return data;
}
index_prev = index;
index = lru_cache.link[index];
}
return LRU_CACHE_NOTHING;
}
// Only for debugging
void lru_cache_display(char x, char y) {
unsigned char col = 0;
gotoxy(x, y);
printf("least recently used cache statistics\n");
printf("size:%02x, first:%02x, last:%02x, cnt:%2x\n\n", LRU_CACHE_SIZE, lru_cache.first, lru_cache.last, lru_cache.count);
printf(" ");
do {
printf(" %1x/%1x ", col, col + 8);
col++;
} while (col < 4);
printf("\n");
col = 0;
lru_cache_index_t index_row = 0;
do {
lru_cache_index_t index = index_row;
printf("%02x:", index);
do {
if (lru_cache.key[index] != LRU_CACHE_NOTHING) {
printf(" %04x:", lru_cache.key[index]);
printf("%02x", lru_cache.link[index]);
} else {
printf(" ----:--");
}
index++;
} while (index < index_row + 4);
printf("\n");
index = index_row;
printf(" :");
do {
printf(" %02x:", lru_cache.next[index]);
printf("%02x ", lru_cache.prev[index]);
index++;
} while (index < index_row + 4);
printf("\n");
index_row += 4;
} while (index_row < 4*4*2);
printf("\n");
printf("least recently used sequence\n");
lru_cache_index_t index = lru_cache.first;
lru_cache_index_t count = 0;
col = 0;
while (count < 6) {
if (count < lru_cache.count)
printf(" %4x", lru_cache.key[index]);
else
printf(" ");
//printf(" %4x %3uN %3uP ", lru_cache.key[cache_index], lru_cache.next[cache_index], lru_cache.prev[cache_index]);
index = lru_cache.next[index];
count++;
}
}

View File

@ -33,7 +33,7 @@ typedef unsigned char lru_cache_index_t;
typedef struct {
typedef struct lru_cache_s {
lru_cache_key_t key[LRU_CACHE_SIZE];
lru_cache_data_t data[LRU_CACHE_SIZE];
lru_cache_index_t prev[LRU_CACHE_SIZE];

View File

@ -1,33 +0,0 @@
// Create an .asm library of imporant conio functions.
// Export to conio_var.asm
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Declare this compilation to generate the conio_var_asm file!
#pragma asm_library
// Declare the procedures to be exported to the .asm libary with the indicated calling convention.
#pragma calling(__varcall)
#pragma asm_export(__conio_var_start, conio_x16_init)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
#pragma asm_export(wherex, wherey)
#pragma asm_export(screensize, screensizex, screensizey, cputln )
#pragma asm_export(cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor )
#pragma asm_export(kbhit, cursor, scroll )
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma calling(__stackcall)
#pragma asm_export(cputc )
#pragma calling(__phicall)
// Allocate local procedure variables to memory.
#pragma var_model(zp)
#include <conio.h>
//#include <cx16-conio.h>

View File

@ -1,13 +1,10 @@
#pragma link("printf_lib.ld")
#pragma encoding(screencode_mixed)
#pragma var_model(mem)
#pragma struct_model(classic) // This is important or kickc messes up the parameters ...
#pragma asm_library
#pragma calling(__stackcall)
#pragma asm_export(__printf_lib_start, conio_c64_init)
#pragma calling(__varcall)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
#pragma asm_export(wherex, wherey)
@ -17,17 +14,16 @@
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma calling(__stackcall)
#pragma calling(__varcall)
#pragma asm_export(printf_str)
#pragma asm_export(printf_uint, printf_sint)
#pragma asm_export(printf_ulong, printf_slong)
#pragma asm_export(printf_uchar, printf_schar)
#pragma calling(__stackcall)
#pragma asm_export(cputc)
#pragma calling(__phicall)
#pragma calling(__varcall)
#include <conio.h>
#include <printf.h>

View File

@ -1,6 +0,0 @@
#if __asm_import__
#else
.segmentdef Code [start=%P]
.segmentdef Data [startAfter="Code"]
#endif

View File

@ -1,13 +1,11 @@
#pragma encoding(screencode_mixed)
#pragma struct_model(classic)
#pragma struct_model(classic) // This is important or kickc messes up the parameters ...
#pragma zp_reserve($0..$1f)
// #include "printf_lib_asm.h"
#include "printf_lib_asm.h"
#include <conio.h>
#include <printf.h>
#include <math.h>
__zp char test;
void main() {

View File

@ -1,36 +1,15 @@
/* Testing an export library calc.asm file generation.
The result of this compile should result in:
- A new calc.asm file created in the compiler output directory.
- A new calc_asm.h file created in the compiler output directory.
The compile ensures that the exported procedures are coalesced per procedure, but
cross procedure there is overlap of ZP, so that the least zeropage is consumed by the library.
Nested functions should have been taken into consideration during ZP allocation, and there
should be no overlap of nested function parameters.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
// Register the exported procedures that should be included in the calc.asm export library.
#pragma calling(__varcall)
#pragma asm_export(plus, min)
// The plus function.
char plus(char a, char b) {
return a+b;
}
// The min function.
char min(char a, char b) {
return a-b;
}
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!

View File

@ -1,14 +1,6 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include "calc_asm.h"
__export char r; // Ensure that r does not get deleted by the optimizer by exporting it.

View File

@ -0,0 +1,37 @@
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#pragma struct_model(classic)
#pragma asm_library
#pragma calling(__varcall)
#pragma calling(__stackcall)
#pragma asm_export(cputc)
#pragma calling(__varcall)
#pragma asm_export(clrscr)
#pragma asm_export(gotoxy)
#pragma asm_export(wherex, wherey)
#pragma asm_export(screensize, screensizex, screensizey, cputln)
#pragma asm_export(cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor)
#pragma asm_export(kbhit, cursor, scroll, cscroll)
#pragma asm_export(screenlayer1, screenlayer2)
#pragma asm_export(cpeekc, cpeekcxy)
#pragma asm_export(plus)
#pragma calling(__phicall)
#include <conio.h>
#include <stdlib.h>
#include <printf.h>
__export char color = 10;
char plus(char a, char p) {
char r = a + p;
color = r;
printf("color = %02u\n", r);
return r;
}

View File

@ -0,0 +1,16 @@
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
struct printf_buffer_number;
#include <calc_asm.h>
__export char r = 5; // Ensure that r does not get deleted by the optimizer by exporting it.
void main() {
asm {sei}
clrscr();
r = 10;
r = plus(r, 2);
r = plus(r, 4);
asm {cli}
}

View File

@ -1,39 +1,22 @@
/* Testing an export library calc.asm file generation.
The result of this compile should result in:
- A new calc.asm file created in the compiler output directory.
- A new calc_asm.h file created in the compiler output directory.
The compile ensures that the exported procedures are coalesced per procedure, but
cross procedure there is overlap of ZP, so that the least zeropage is consumed by the library.
Nested functions should have been taken into consideration during ZP allocation, and there
should be no overlap of nested function parameters.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
// For the moment, allocated local procedure variables to memory.
#pragma var_model(local_mem)
// Register the exported procedures that should be included in the calc.asm export library.
#pragma calling(__varcall)
#pragma asm_export(plus, min)
// The plus function.
char internal(char d) {
return d * 2;
}
char plus(char a, char b) {
return a+b;
char d = internal(a);
return a + b + d;
}
// The min function.
char min(char a, char b) {
return a-b;
char min(char a, char b) {
return a - b;
}
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!

View File

@ -1,13 +1,6 @@
/* Here we use the calc.asm library functions.
This is a compile that allows to display the result using printf.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include "calc_asm.h"
#include <stdio.h>

View File

@ -1,32 +1,13 @@
/* Testing an export library calc.asm file generation.
The result of this compile should result in:
- A new calc.asm file created in the compiler output directory.
- A new calc_asm.h file created in the compiler output directory.
The compile ensures that the exported procedures are coalesced per procedure, but
cross procedure there is overlap of ZP, so that the least zeropage is consumed by the library.
Nested functions should have been taken into consideration during ZP allocation, and there
should be no overlap of nested function parameters.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
// Register the exported procedures that should be included in the calc.asm export library.
#pragma asm_export(plus_min)
// The plus function.
char plus(char a, char b) {
return a+b;
}
// The min function.
char min(char a, char b) {
return a-b;
}
@ -36,7 +17,3 @@ char plus_min(char a, char p, char m) {
char mr = min(pr,m);
return mr;
}
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!

View File

@ -1,23 +1,10 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include "calc_asm.h"
// We include the C64 and printf stuff.
__export char r;
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!
void main() {
r = plus_min(5,4,1);
r = plus_min(r,4,1);

View File

@ -1,33 +1,15 @@
/* Testing an export library calc.asm file generation.
The result of this compile should result in:
- A new calc.asm file created in the compiler output directory.
- A new calc_asm.h file created in the compiler output directory.
The compile ensures that the exported procedures are coalesced per procedure, but
cross procedure there is overlap of ZP, so that the least zeropage is consumed by the library.
Nested functions should have been taken into consideration during ZP allocation, and there
should be no overlap of nested function parameters.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#pragma var_model(mem)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
// Register the exported procedures that should be included in the calc.asm export library.
#pragma calling(__varcall)
#pragma asm_export(plus_min)
// The plus function.
char plus(char a, char b) {
return a+b;
}
// The min function.
char min(char a, char b) {
return a-b;
}
@ -37,7 +19,3 @@ char plus_min(char a, char p, char m) {
char mr = min(pr,m);
return mr;
}
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!

View File

@ -1,14 +1,6 @@
/* Here we use the calc.asm library functions.
We also use the printf to nicely display the result and to document how the
imported calc.asm library works and how it compiles with the main source.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include "calc_asm.h"
#include <stdio.h>
@ -22,4 +14,4 @@ void main() {
printf("5 + 4 - 1 = %u\n", r);
r = plus_min(4,2,3);
printf("4 + 2 - 3 = %u\n", r);
}
}

View File

@ -1,26 +1,9 @@
/* Testing an export library calc.asm file generation.
The result of this compile should result in:
- A new calc.asm file created in the compiler output directory.
- A new calc_asm.h file created in the compiler output directory.
The compile ensures that the exported procedures are coalesced per procedure, but
cross procedure there is overlap of ZP, so that the least zeropage is consumed by the library.
Nested functions should have been taken into consideration during ZP allocation, and there
should be no overlap of nested function parameters.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)
// The plus functions
char plus1(char d) {
d = d + 1;
return d;
@ -43,7 +26,3 @@ char plus3(char d) {
// char plus5(char d) {
// return d+5;
// }
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!

View File

@ -1,14 +1,6 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include <calc_asm.h>
__export char r; // Ensure that r does not get deleted by the optimizer by exporting it.
@ -19,4 +11,4 @@ void main() {
r = plus3(0);
// r = plus4(r);
// r = plus5(r);
}
}

View File

@ -1,32 +1,15 @@
/* Testing an export library calc.asm file generation.
The result of this compile should result in:
- A new calc.asm file created in the compiler output directory.
- A new calc_asm.h file created in the compiler output directory.
The compile ensures that the exported procedures are coalesced per procedure, but
cross procedure there is overlap of ZP, so that the least zeropage is consumed by the library.
Nested functions should have been taken into consideration during ZP allocation, and there
should be no overlap of nested function parameters.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
// Register the exported procedures that should be included in the calc.asm export library.
#pragma calling(__varcall)
#pragma asm_export(plus, min, check)
// The plus function.
char plus(char a, char b) {
return a+b;
}
// The min function.
char min(char a, char b) {
return a-b;
}
@ -46,7 +29,3 @@ char check(char a, char b, char r) {
}
}
}
// Note that both functions stand on its own in this source, there is no main!
// Parameters a and b are taken into consideration when allocating the ZP!

View File

@ -1,14 +1,6 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include "calc_asm.h"
__export char r; // Ensure that r does not get deleted by the optimizer by exporting it.
@ -17,4 +9,4 @@ void main() {
r = 10;
r = plus(5,4);
r = min(5,4);
}
}

View File

@ -1,14 +1,6 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include <plus_asm.h>
#include <min_asm.h>
#include <print_asm.h>
@ -22,4 +14,4 @@ void main() {
r = min(r, 1);
printf("r = %u", r);
}
}

View File

@ -1,15 +1,9 @@
// Testing two libraries with zero-page for parameters.
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)
// The plus functions
char min(char a, char m) {
return a - m;
}

View File

@ -1,10 +1,6 @@
// Testing two libraries with zero-page for parameters.
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)

View File

@ -1,10 +1,6 @@
// Testing two libraries with zero-page for parameters.
#pragma encoding(screencode_mixed)
#pragma var_model(local_zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)
@ -17,8 +13,6 @@ struct data {
};
// The plus functions
char get_x(char i) {
struct data x = {0,1,2};

View File

@ -1,10 +1,6 @@
// Testing two libraries with zero-page for parameters.
#pragma encoding(screencode_mixed)
#pragma var_model(local_zp)
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)

View File

@ -1,14 +1,6 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include <cx_asm.h>
#include <cy_asm.h>
#include <print_asm.h>

View File

@ -1,16 +1,8 @@
/* Here we use the calc.asm library functions.
This is a minimal source compilation for asm debugging, to test the asm library functionality
and compilation result.
*/
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#include <print_asm.h>
// We include the calc_asm.h generated C function prototype file.
// This file contains the function prototypes for plus and min, which are
// contained in the calc.asm file in assembler.
#include <plus_asm.h>
#include <min_asm.h>

View File

@ -1,19 +1,13 @@
// Testing two libraries with zero-page for parameters.
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#include <print_asm.h>
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)
#include <stdio.h>
// The plus functions
char min(char a, char m) {
char r = a - m;
printf("min r = %u\n", r);

View File

@ -1,19 +1,13 @@
// Testing two libraries with zero-page for parameters.
#pragma encoding(screencode_mixed)
#pragma var_model(zp)
#include <print_asm.h>
// Register the compilation as an calc.asm export library output artefact.
// There is no main() function!
#pragma asm_library
#pragma calling(__varcall)
#include <stdio.h>
// The plus functions
char plus(char a, char p) {
char r = a + p;
printf("plus r = %u\n", r);