mirror of https://gitlab.com/camelot/kickc.git
- 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:
parent
2c74b2a19c
commit
e371d304f6
|
@ -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
|
||||
|
|
|
@ -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(() -> {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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];
|
|
@ -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>
|
||||
|
|
@ -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');
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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];
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
#if __asm_import__
|
||||
#else
|
||||
.segmentdef Code [start=%P]
|
||||
.segmentdef Data [startAfter="Code"]
|
||||
#endif
|
|
@ -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() {
|
||||
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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}
|
||||
}
|
|
@ -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!
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue