1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00

- First well improved version for exporting and importing .asm library functions.

- Full documentation in the new manual directory containing the kickc.md markup manual file.
- library folder containing a concrete example.
This commit is contained in:
Sven Van de Velde 2023-11-24 15:46:48 +01:00
parent 818da3f995
commit b794791cc6
18 changed files with 316 additions and 120 deletions

View File

@ -1183,24 +1183,126 @@ If proc1, proc2, or proc3 are never used, then the init function is not called,
## 2.11 Libraries
KickC has now a new experimental facility that allows to **define** and **import** pre-compiled functions in the form **.asm libraries**. The compiler will arrange the functions contained within the .asm library callable using the stack calling convention from your KickC source C code. (At a later stage, it is planned to also implement zero-page calling convention, and even register calling convention where applicable, improving the calling convention used.)
KickC has now a new facility that allows to **export** and **import** pre-compiled functions in the form **kick assembler libraries**.
Such pre-compiled libraries can greatly speed up your development process when designing larger projects.
However, as the .asm libraries are already pre-compiled, important limitations and design decisions are taken, which you need to be aware of:
1. **Less optimized assembler**: The .asm libraries code cannot be optimized by KickC (through SSA optimization). All the functions defined in assembler are imported "as-is", and require as a result a very generic code implementation by KickC. On the positive side, such libraries are then also usable in multiple programs, and can be important to speed up your compilations!
2. **Functions are called using the Stack**: Each function in the .asm library (for the moment) follow the stack calling convention. As a result, ensure that the depth of your stack and the depth of the call stack is limited to the stack size of your target CPU! Bare in mind that functions with a large amount of parameters are all passed through the stack!
3. **Limit Functional Dependencies**: Ensure that the functions, that are defined in the .asm library, are as much as possible "isolated" and "self contained". Building libraries with lots of dependencies result in a lot of code overhead and possibly even duplicate code!
4. **Zero Page Allocation**: If your .asm library contains the usage of complex data structures and/or pointers, there is a high likelyhood that your library will have to consume **zero page** within the .asm library. Because these zero page allocations are "statically" defined as part of the assembler generated code in the .asm library, it is important that your KickC C program reserves the needed zero page used by the .asm library when importing!
5. **No Zero Page Coalesce**: Additionally, any zero page usage in your .asm library will take dedicated space in your KickC program, and zero page coalesce cannot not take into account any of the .asm library reserved zero pages!
6. **Independent .asm library creation**: Your .asm libraries must be created and compiled using special "stub" C-code. This stub C code will import and declare all the functions that are to be generated into the .asm library. See below for an example.
Libraries prevent you to compile every time your large and growing program.
The idea of libraries is that you first define your `<library>.asm` files, which are meant to contain small and independent part of pre-compiled logic. Those small libraries can then be used to speed up compilation time for your main program, and libraries can be re-used accross different main programs.
However, using libraries have specific consequences, like code size, segmentation, optimization, and more ..., which is explained further in this section.
In summary, the compiler handles libraries as follows, depending on whether you import or export:
- **export**: Library functions are exported during assembler code generation to indicated `<library>.asm` files, which are saved at the output directory location. You will indicate the C functions that need to be exported, using a special `#pragma asm_export` keyword, or by using the function directive keyword `__asm_export` at the function declaration.
- **import**: Library functions are imported during assembler code generation of your main program, importing the indicated `<library>.asm` files, which are read from the source directory location. You must indicate which C functions are defined within libraries, using a special `#pragma asm_import` keyword, or by using the function directive keyword `__asm_import` at the function declaration.
### 2.11.1 Consequences and dependencies using libraries
However, as libraries are already pre-compiled, important limitations and design decisions are to be taken. Library functions are callable using a pre-defined interface mechanism, outlining the sequence of calling parameters and return value, implemented through a KICKC function calling convention.
As a consequence such pre-compiled interface, there are the following attention points, of which you need to be aware of:
1. **"Less" optimized assembler**: Your resulting assembler using pre-compiled code in libraries will be less optimized by KickC (through SSA optimization), compared to compiling all your source in one compilation run. KickC cannot optimize library functions accross the interface boundaries, as the pre-compiled assembler has the interface of such functions already defined in assembler level. The resulting assembler will be more generic, and as a consequence, less-optimized. However, the assembler code that is generated "within" the library, will be fully optimized, as long as library function interface boundaries are not crossed.
2. **Zero page allocation**: There is a high likelyhood that your library will have to consume 6502 **zero page** registers within the .asm library. Because these zero page allocations are pre-compiled or statically defined, it is important that your C program indicates the zero page usage by the library functions when importing! You need to ensure that the zero page used by the library functions are not conflicting with the zero page usage of your main program. The compiler however, will indicate if such is the case and will refuse further compilation if there is any unresolvable conflict. As a best practice, ensure that libraries use zero page in the lower regions of the 6502 processor, and that your functions are "atomic".
3. **Zero page coalesce**: Related to point 2, any zero page usage in your .asm library will take dedicated space in your KickC program, and zero page coalesce cannot not take into account any of the .asm library reserved zero pages!
4. **Limit functional dependencies**: Ensure that the functions being **exported** in a library are as much as possible **atomic** and **self contained**. Exporting library functions with lots of dependencies result in code overhead and possibly even duplicate code! And this is the main difference compared to normal C style `.o` (object) files linkage. Your .asm libraries **contains all the code** required to run the functionality of the library functions and is **self-contained**. So ensure that when exporting functions into .asm libraries, that no extra or unexpected code or logic is exported, which can be a result of function calling dependencies. Carefully ensure that your library functions are atomic or self-contained. It is good practice to inspect the generated assembler in your .asm libraries when creating libraries, so verify if there aren't any other embedded and unwanted functions within your .asm library. But be aware that in some cases such cannot be avoided.
### 2.11.2 Export C functions to .asm libraries
When creating your first exported `<library>.asm` file, is is best to define a small separate .c program, that contains the functions to be exported in the .asm library, and use the KickC export facilities to export your functions. Let us explore the compiler facilities for exporting libraries, followed by an example.
There are two different but complementary facilities to export C functions within your C program, the first using a `#pragma` directive and the other using a `function()` directive.
- **`#pragma asm_export( "<library>", <__calling_convention>, <foo>, <foo>, ...)`**
The `#pragma asm_export` indicates in your C program which functions are to be exported and by which calling convention:
- `"<library>"` : A string containing the `<library>` name to be exported. The library name is **excluding** the .asm extension!
- `<__calling_convention>` : Indicating which KickC function calling mechanism or convention is applied to export the function.
- `<foo>` - The function name(s) to be exported, comma separated.
For example:
`#pragma asm_export("flight_lib", __stackcall, flight_init)`
`#pragma asm_export("flight_lib", __varcall, flight_route, flight_progress)`
Creates after compilation a `flight_engine.asm` file, containing 3 exported functions `flight_init`, `flight_route` and `flight_progress`. `flight_init` is exported using the _6502 stack_ calling convention while `flight_route` and `flight_distance` are exported using the zeropage calling convention.
- **`<type> __asm_export("<library>") <__calling_convention> foo()`**
A function declaration can use the `__asm_export("<library>")` directive to indicate that the function is exported to the `<library>`, accompanied with the normal other function directives:
- `<type>` - Each function `foo()` must have a type declaration, following the normal syntax.
- `<__calling_convention>` - Optionally, the `foo()` can be declared using one of the KickC supported calling conventions directives.
- Each `foo()` must be specifically declared with an `__asm_export("<library>")` directive to have it exported.
The difference between the two function export facilities is that using the `#pragma __asm_export` preprocessor directive allows to also export functions that are already defined within the standard KickC C libraries, which are included in your program using the `#include` preprocessor command. For example, it is possible to export *conio* C library functions to an .asm library using the #pragma technique:
```
#pragma asm_export("conio_lib", __stackcall, conio_init, cputs, cputsxy)
#include <conio.h>
```
When exporting C functions that are of your own creation, for example, for C functions `flight_init`, `flight_route` and `flight_progress`, it is recommended to export those using compiler directives:
```
void __asm_export("flight_lib") __stackcall flight_init(char flightno);
void __asm_export("flight_lib") __varcall flight_route(char flightno, int direction);
void __asm_export("flight_lib") __varcall flight_direction(char flightno, signed char distance);
```
As a result, a new `flight_lib.asm` file will be created in your output directory.
> Additionally, a new `flight_lib.h` file will also be created, and is to be used when importing the `flight_lib.asm` library into your main program. The header file contains the exact function prototype declarations for each function within the `flight_lib.asm` library, namely also the exact zero page and/or register for each parameter defining the interface for each library function.
### 2.11.3 Import .asm libraries for usage in your C programs.
Once you have your .asm library files, like `flight_lib.asm` (see above), you are ready to import those libraries and use them in your main C programs.
The KickC compiler only provides `function()` directive language facilities to import C functions.
This is to enforce the programmer to declare imported .asm functions with its parameter register usage prototypes, which can be through main memory locations, through zeropage registers or through cpu registers.
- **`extern <type> __asm_import("<library>") <__calling_convention> foo( <type> __zp(number) <parameter>, ...)`**
A function declaration can use the `__asm_import("<library>")` directive to indicate that the function is imported from the `<library>`, accompanied with the normal other function directives:
- `extern` - Each imported function must be declared as external.
- `<type>` - Each function `foo()` must have a type declaration, following the normal syntax.
- `<__calling_convention>` - Optionally, the `foo()` can be declared using one of the KickC supported calling conventions directives.
- Each `foo()` must be specifically declared with an `__asm_import("<library>")` directive to indicate it is imported
parameters:
- Each `<parameter>` and its containing `<type>` must be declared with the specific registers, which can be zeropage register or cpu register based.
- The `__zp(#nr)` directive for each parameter specifies to the KickC compiler which zeropage registers are being used for the .asm library function interface.
- The `__register([A,X,Y])` directive specifies for each parameter which CPU register is used to interface with the .asm library function. The KickC compiler will take these register allocations into account when interfacing with these external .asm library functions from your main C program!
Let us illustrate with an example for our flight_lib.asm library.
As we already know, a `flight_lib.h` file was created by the compiler. It is required that this `flight_lib.h` file gets imported in your main C program, as this header file contains the external library function declarations and register usage.
```
// This header file was generated by KickC and contains for each library function:
// - The library import definition
// - Function calling convention used
// - Function parameter memory location, zeropage register or cpu register allocation.
#include "flight_lib.h"
int main() {
....
flight_init(1);
flight_route(1, 90);
flight_progress(1, 2);
...
}
```
In practice using libraries is fairly easy, it just requires you to practice a bit with it, and gain experience. A best practice is to start small, and grow slowly the complexity as you gain experience and deeper understanding of how .asm libraries work.
The test folder in the KickC source code contains a library directory with a concrete example exporting and importing a conio .asm library file.
## 2.12 Comparison with Standard C99

View File

@ -80,7 +80,8 @@ FORM_SSA: '__ssa' ;
FORM_MA: '__ma' ;
INTRINSIC: '__intrinsic' ;
CALLINGCONVENTION: '__stackcall' | '__phicall' | '__varcall' | '__intrinsiccall';
LIBRARY: '__library';
ASM_IMPORT: '__asm_import';
ASM_EXPORT: '__asm_export';
IF: 'if' ;
ELSE: 'else' ;
WHILE: 'while' ;

View File

@ -166,7 +166,8 @@ directive
| INTERRUPT ( PAR_BEGIN NAME PAR_END )? #directiveInterrupt
| LOCAL_RESERVE PAR_BEGIN pragmaParam ( COMMA pragmaParam )* PAR_END #directiveReserveZp
| CALLINGCONVENTION #directiveCallingConvention
| LIBRARY PAR_BEGIN pragmaParam PAR_END #directiveLibrary
| ASM_IMPORT PAR_BEGIN pragmaParam PAR_END #directiveAsmImport
| ASM_EXPORT PAR_BEGIN pragmaParam PAR_END #directiveAsmExport
;
stmtSeq

View File

@ -1,5 +1,6 @@
package dk.camelot64.kickc;
import dk.camelot64.kickc.asm.AsmLibrary;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateCache;
import dk.camelot64.kickc.fragment.AsmFragmentTemplateUsages;
@ -360,7 +361,7 @@ public class KickC implements Callable<Integer> {
}
if(calling != null) {
Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvension(calling);
Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvention(calling);
if(callingConvention == null) {
System.err.println("Unknown calling convention " + calling);
StringBuffer supported = new StringBuffer();
@ -418,6 +419,22 @@ public class KickC implements Callable<Integer> {
asmWriter.close();
asmOutputStream.close();
// In case of asm library exports, we generate the exported procedure prototypes,
// to be imported into the C program. These prototypes set the register and zeropage usages.
// TODO: I need to add an assert function to validate that each exported procedure is also declared and defined!
for(AsmLibrary asmLibrary : program.getAsmExports().values()) {
String headers = asmLibrary.generateHeaders(program);
Path asmExportHeaderPath = program.getOutputFileManager().getAsmExportHeaderFile();
System.out.println("Writing asm export header file " + asmExportHeaderPath);
FileOutputStream asmExportOutputStream = new FileOutputStream(asmExportHeaderPath.toFile());
OutputStreamWriter asmExportWriter = new OutputStreamWriter(asmExportOutputStream);
asmExportWriter.write(headers);
asmExportWriter.close();
asmExportOutputStream.close();
}
// Save the fragment synthesizer cache files
program.getAsmFragmentMasterSynthesizer().finalize(compiler.getLog());

View File

@ -108,6 +108,10 @@ public class OutputFileManager {
return getOutputFile(getOutputDirectory(), getAsmExtension());
}
public Path getAsmExportHeaderFile() {
return getOutputFile(getOutputDirectory(), "h");
}
/**
* Get the output file name with a specific extension
*

View File

@ -1,6 +1,11 @@
package dk.camelot64.kickc.asm;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.ProcedureRef;
import java.nio.file.Path;
import java.util.*;
@ -47,6 +52,79 @@ public class AsmLibrary extends AsmLine {
return procedures.containsKey(procedureName);
}
/**
* Generate part of a comment that describes a returnvalue/parameter
*
* @param param The variable to describe
* @param scope The scope (procedure)
* @param signature The signature to append to
* @return The version of the variable chosen
*/
Variable generateSignatureVar(Variable param, Scope scope, StringBuilder signature) {
if (param == null) return param;
if (param.isKindPhiMaster()) {
List<Variable> versions = new ArrayList<>(scope.getVersions(param));
if (versions.size() > 0) if (param.getLocalName().equals("return")) {
// Choose the last version for return values
param = versions.get(versions.size() - 1);
} else {
// Choose the first version for parameters
param = versions.get(0);
}
else
// Parameter optimized away to a constant or unused
return param;
}
Registers.Register allocation = param.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(") ");
} else if (allocation instanceof Registers.RegisterAByte) {
signature.append("__register(A) ");
} else if (allocation instanceof Registers.RegisterXByte) {
signature.append("__register(X) ");
} else if (allocation instanceof Registers.RegisterYByte) {
signature.append("__register(Y) ");
} else if (allocation instanceof Registers.RegisterZByte) {
signature.append("__register(Z) ");
}
return param;
}
private String generateHeader(Procedure procedure) {
StringBuilder signature = new StringBuilder();
signature.append("extern ");
signature.append(procedure.getCallingConvention().getName()).append(" ");
signature.append("__asm_import(").append(this.getFullName()).append(") ");
generateSignatureVar(procedure.getLocalVar("return"), procedure, signature);
signature.append(procedure.getReturnType().toCDecl());
signature.append(" ").append(procedure.getLocalName()).append("(");
int i = 0;
for (Variable parameter : procedure.getParameters()) {
if (i++ > 0) signature.append(", ");
Variable param = generateSignatureVar(parameter, procedure, signature);
signature.append(param.getType().toCDecl(parameter.getLocalName()));
}
signature.append(");");
signature.append("\n");
// Always add the signature comments...
return signature.toString();
}
public String generateHeaders(Program program) {
StringBuilder headers = new StringBuilder();
for(String procedureName: this.getProcedures()) {
Procedure procedure = program.getScope().getProcedure(new ProcedureRef(procedureName));
if(procedure != null)
headers.append(generateHeader(procedure));
}
return headers.toString();
}
@Override
public int getLineBytes() {
return 0;

View File

@ -182,23 +182,37 @@ public class Directive {
}
}
/** Indicates the location is in a library. */
public static class Library extends Directive {
public String getLibrary() {
return library;
/** Indicates the location is in an asm library. */
public static class AsmLibraryDirective extends Directive {
public String getAsmLibrary() {
return asmLibrary;
}
public void setLibrary(String library) {
this.library = library;
public void setAsmLibrary(String asmLibrary) {
this.asmLibrary = asmLibrary;
}
private String library;
private String asmLibrary;
public Library(String library) {
super("__library");
this.library = library;
public AsmLibraryDirective(String name) {
super(name);
}
}
/** Indicates the item is imported from an asm library during compilation. */
public static class AsmImportDirective extends AsmLibraryDirective {
public AsmImportDirective(String asmImportLibrary) {
super("__asm_import");
this.setAsmLibrary(asmImportLibrary);
}
}
/** Indicates the item is exported to an asm library during compilation. */
public static class AsmExportDirective extends AsmLibraryDirective {
public AsmExportDirective(String asmExportLibrary) {
super("__asm_export");
this.setAsmLibrary(asmExportLibrary);
}
}
}

View File

@ -2,6 +2,7 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.OutputFileManager;
import dk.camelot64.kickc.asm.AsmFormat;
import dk.camelot64.kickc.asm.AsmLibrary;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer;
@ -358,6 +359,7 @@ public class Program {
this.asm = asm;
}
/**
* Get the call-graph for the program. Calculates the call-graph if it has not already been calculated.
*
@ -639,19 +641,17 @@ public class Program {
this.asmImports = asmImports;
}
public AsmLibrary addAsmImportLibrary(String asmImportLibraryName) {
Path asmImportResource = Paths.get(asmImportLibraryName + ".asm").toAbsolutePath();
AsmLibrary asmLibrary = this.asmImports.putIfAbsent(asmImportLibraryName, new AsmLibrary(asmImportLibraryName, asmImportResource));
// this.addAsmResourceFile(asmImportResource);
return asmLibrary;
}
public void addAsmImportProcedures(String asmImport, Procedure.CallingConvention callingConvention, List<String> procedures) {
Path asmImportResource = Paths.get(asmImport + ".asm").toAbsolutePath();
String asmImportName = Paths.get(asmImport).getFileName().toString();
public void addAsmImportProcedures(AsmLibrary asmImportLibrary, Procedure.CallingConvention callingConvention, List<String> procedures) {
AsmLibrary library = this.asmImports.get(asmImportName);
if(library == null) {
this.asmImports.put(asmImportName, new AsmLibrary(asmImportName, asmImportResource));
this.addAsmResourceFile(asmImportResource);
}
for(String procedureName : procedures) {
this.asmImports.get(asmImportName).addProcedure(procedureName, callingConvention);
asmImportLibrary.addProcedure(procedureName, callingConvention);
}
}
@ -674,7 +674,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.setAsmLibrary(asmImport.getFullName());
procedure.setAsmImportLibrary(asmImport.getFullName());
procedure.setDeclaredExtern(true);
procedure.setDeclaredInline(false);
}
@ -727,10 +727,9 @@ public class Program {
// - not inlined
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
procedure.setCallingConvention(asmLibrary.getProcedureCallingConvention(procedure.getFullName()));
procedure.setAsmLibrary(asmLibrary.getFullName());
procedure.setAsmExportLibrary(asmLibrary.getFullName());
procedure.setDeclaredExtern(false);
procedure.setDeclaredInline(false);
procedure.setAsmExported(true);
}
}

View File

@ -49,23 +49,25 @@ public class Procedure extends Scope {
/** The .asm library where the procedure resides in compiled form. */
private String asmLibrary;
private boolean asmImportLibrary;
private boolean asmExportLibrary;
public String getAsmLibrary() {
return asmLibrary;
}
public void setAsmLibrary(String asmLibrary) {
public void setAsmImportLibrary(String asmLibrary) {
this.asmLibrary = asmLibrary;
this.asmImportLibrary = true;
}
private boolean asmExported;
public boolean isAsmExported() {
return asmExported;
public void setAsmExportLibrary(String asmLibrary) {
this.asmLibrary = asmLibrary;
this.asmExportLibrary = true;
}
public void setAsmExported(boolean asmExported) {
this.asmExported = asmExported;
public boolean isAsmLibrary() {
return this.asmLibrary != null;
}
@ -120,7 +122,7 @@ public class Procedure extends Scope {
}
/** Get a calling convention by name. */
public static CallingConvention getCallingConvension(String name) {
public static CallingConvention getCallingConvention(String name) {
for(CallingConvention value : CallingConvention.values()) {
if(value.getName().equalsIgnoreCase(name)) {
return value;

View File

@ -298,10 +298,10 @@ public class Variable implements Symbol {
return Kind.LOAD_STORE.equals(getKind());
}
public boolean isAsmExportParameter() {
public boolean isAsmLibraryParameter() {
Procedure procedure = this.getContainingProcedure();
if(procedure != null)
return procedure.isAsmExported() && procedure.getParameters().contains(this);
return procedure.isAsmLibrary() && procedure.getParameters().contains(this);
else
return false;
}

View File

@ -348,7 +348,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
Procedure.CallingConvention callingConvention = pragmaParamCallingConvention(ctx.pragmaParam(1));
List<String> procedures = pragmaParamAsmImportProcedures(ctx.pragmaParam());
program.addAsmImportProcedures(libraryName, callingConvention, procedures);
AsmLibrary asmLibrary = program.addAsmImportLibrary(libraryName);
program.addAsmImportProcedures(asmLibrary, callingConvention, procedures);
}
default -> program.getLog().append("Warning! Unknown #pragma " + pragmaName);
}
@ -406,7 +407,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
if(!(paramCtx instanceof KickCParser.PragmaParamCallingConventionContext))
throw new CompileError("Expected a CALLINGCONVENTION parameter. Found '" + paramCtx.getText() + "'.", new StatementSource(paramCtx.getParent()));
final String callingConventionName = ((KickCParser.PragmaParamCallingConventionContext) paramCtx).CALLINGCONVENTION().getText();
final Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvension(callingConventionName);
final Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvention(callingConventionName);
if(callingConvention == null)
throw new CompileError("Expected a CALLINGCONVENTION parameter. Found '" + paramCtx.getText() + "'.", new StatementSource(paramCtx.getParent()));
return callingConvention;
@ -1385,8 +1386,12 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
} else if(directive instanceof Directive.Bank directiveBank) {
Bank bank = new Bank(directiveBank.getBankArea(), directiveBank.getBankNumber());
procedure.setBank(bank);
} else if(directive instanceof Directive.Library) {
procedure.setAsmLibrary(((Directive.Library) directive).getLibrary());
} else if(directive instanceof Directive.AsmImportDirective) {
procedure.setAsmImportLibrary(((Directive.AsmImportDirective) directive).getAsmLibrary());
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) {
@ -1455,14 +1460,20 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return new Directive.Interrupt(interruptType);
}
public Object visitDirectiveLibrary(KickCParser.DirectiveLibraryContext ctx) {
String library = ctx.pragmaParam().getText().toLowerCase(Locale.ENGLISH);
return new Directive.Library(library);
public Object visitDirectiveAsmImport(KickCParser.DirectiveAsmImportContext ctx) {
String asmImport = ctx.pragmaParam().getText().toLowerCase(Locale.ENGLISH);
this.program.addAsmImportLibrary(asmImport);
return new Directive.AsmImportDirective(asmImport);
}
public Object visitDirectiveAsmExport(KickCParser.DirectiveAsmExportContext ctx) {
String asmExport = ctx.pragmaParam().getText().toLowerCase(Locale.ENGLISH);
return new Directive.AsmExportDirective(asmExport);
}
@Override
public Directive visitDirectiveCallingConvention(KickCParser.DirectiveCallingConventionContext ctx) {
Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvension(ctx.getText());
Procedure.CallingConvention callingConvention = Procedure.CallingConvention.getCallingConvention(ctx.getText());
return new Directive.CallingConvention(callingConvention);
}

View File

@ -144,7 +144,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
LValue lValue = assignment.getlValue();
if(lValue instanceof VariableRef varRef) {
Variable var = getProgramScope().getVariable(varRef);
if(var.isVolatile() || var.isKindLoadStore() || var.isAsmExportParameter())
if(var.isVolatile() || var.isKindLoadStore() || var.isAsmLibraryParameter())
// Do not examine volatiles and non-versioned variables
continue;
if(var.getRegister() != null && var.getRegister().isMem())
@ -175,7 +175,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
// Look for constants among non-versioned variables
for(Variable variable : getProgramScope().getAllVariables(true)) {
if(variable.isVolatile() || !variable.isKindLoadStore() || variable.isAsmExportParameter()) // #828 - Added
if(variable.isVolatile() || !variable.isKindLoadStore() || variable.isAsmLibraryParameter()) // #828 - Added
// Do not examine volatiles, non-constants or versioned variables
continue;
if(variable.getRegister() != null && variable.getRegister().isMem())

View File

@ -208,18 +208,6 @@ public class Pass4CodeGeneration {
asm.addLine( new AsmInlineKickAsm("#import \"" + asmLibraryName + ".asm\"", null, null, null));
}
// In case of an asm library export, we generate the exported procedure prototypes,
// to be imported into the C program. These prototypes set the register and zeropage usages.
// TODO: I need to add an assert function to validate that each exported procedure is also declared and defined!
for(AsmLibrary asmLibrary : program.getAsmExports().values()) {
for(String procedureName : asmLibrary.getProcedures()) {
Procedure procedure = getScope().getProcedure(new ProcedureRef(procedureName));
if(procedure != null) {
// Generate parameter information
generateSignatureComments(asm, procedure);
}
}
}
program.setAsm(asm);
}

View File

@ -112,7 +112,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
Procedure proc1 = program.getScope().getProcedure((ProcedureRef)threads1.toArray()[0]);
Procedure proc2 = program.getScope().getProcedure((ProcedureRef)threads2.toArray()[0]);
if(proc1.isAsmExported() || proc2.isAsmExported()) {
if(proc1.isAsmLibrary() || proc2.isAsmLibrary()) {
return true;
} else {
return threads1.equals(threads2);
@ -121,7 +121,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
private static boolean isAsmExportParameter(VariableRef varRef, Program program) {
Variable variable = program.getScope().getVariable(varRef);
return variable.isAsmExportParameter();
return variable.isAsmLibraryParameter();
}
/**

View File

@ -36,7 +36,6 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
boolean modified = false;
for(var block : getGraph().getAllBlocks()) {
Procedure procedure = getProgram().getProcedure(block);
AsmLibrary asmLibrary = getProgram().getAsmExportLibrary(procedure);
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
while(stmtIt.hasNext()) {
Statement statement = stmtIt.next();

View File

@ -1543,24 +1543,3 @@ memcpy8_vram_vram: {
.segment DataConIO
__conio: .fill SIZEOF_STRUCT___0, 0
}
// void __conio_var_start()
// void conio_x16_init()
// void clrscr()
// void gotoxy(__zp(2) char x, __zp(5) char y)
// __zp($b) char wherex()
// __zp(8) char wherey()
// void screensize(__zp(6) char *x, __zp($e) char *y)
// __zp(5) char screensizex()
// __zp(2) char screensizey()
// void cputln()
// void cputc(__zp(8) char c)
// void cputcxy(__zp(2) char x, __zp(5) char y, __zp(3) char c)
// void cputs(__zp($c) const char *s)
// void cputsxy(__zp(2) char x, __zp(5) char y, __zp($e) const char *s)
// __zp(8) char textcolor(__zp(2) char color)
// __zp(8) char bgcolor(__zp(2) char color)
// __zp(8) char bordercolor(__zp(3) char color)
// char kbhit()
// __zp(8) char cursor(__zp(2) char onoff)
// __zp(5) char scroll(__zp(3) char onoff)
// void screenlayer1()

View File

@ -0,0 +1,21 @@
extern __varcall __asm_import(conio_var) void __conio_var_start();
extern __varcall __asm_import(conio_var) void conio_x16_init();
extern __varcall __asm_import(conio_var) void clrscr();
extern __varcall __asm_import(conio_var) void gotoxy(__zp(2) char x, __zp(5) char y);
extern __varcall __asm_import(conio_var) __zp($b) char wherex();
extern __varcall __asm_import(conio_var) __zp(8) char wherey();
extern __varcall __asm_import(conio_var) void screensize(__zp(6) char *x, __zp($e) char *y);
extern __varcall __asm_import(conio_var) __zp(5) char screensizex();
extern __varcall __asm_import(conio_var) __zp(2) char screensizey();
extern __varcall __asm_import(conio_var) void cputln();
extern __stackcall __asm_import(conio_var) void cputc(__zp(8) char c);
extern __varcall __asm_import(conio_var) void cputcxy(__zp(2) char x, __zp(5) char y, __zp(3) char c);
extern __varcall __asm_import(conio_var) void cputs(__zp($c) const char *s);
extern __varcall __asm_import(conio_var) void cputsxy(__zp(2) char x, __zp(5) char y, __zp($e) const char *s);
extern __varcall __asm_import(conio_var) __zp(8) char textcolor(__zp(2) char color);
extern __varcall __asm_import(conio_var) __zp(8) char bgcolor(__zp(2) char color);
extern __varcall __asm_import(conio_var) __zp(8) char bordercolor(__zp(3) char color);
extern __phicall __asm_import(conio_var) char kbhit();
extern __varcall __asm_import(conio_var) __zp(8) char cursor(__zp(2) char onoff);
extern __varcall __asm_import(conio_var) __zp(5) char scroll(__zp(3) char onoff);
extern __varcall __asm_import(conio_var) void screenlayer1();

View File

@ -14,37 +14,17 @@
#pragma var_model(zp)
#pragma asm_import("conio_var", __varcall, __conio_var_start, conio_x16_init, clrscr, gotoxy)
#pragma asm_import("conio_var", __varcall, wherex, wherey)
#pragma asm_import("conio_var", __varcall, screensize, screensizex, screensizey)
#pragma asm_import("conio_var", __stackcall, cputc )
#pragma asm_import("conio_var", __varcall, cputln, cputcxy, cputs, cputsxy)
#pragma asm_import("conio_var", __varcall, textcolor, bgcolor, bordercolor, kbhit)
#pragma asm_import("conio_var", __varcall, cursor, scroll)
#pragma asm_import("conio_var", __varcall, screenlayer0, screenlayer1)
//#pragma asm_import("conio_var", __varcall, __conio_var_start, conio_x16_init, clrscr, gotoxy)
//#pragma asm_import("conio_var", __varcall, wherex, wherey)
//#pragma asm_import("conio_var", __varcall, screensize, screensizex, screensizey)
//#pragma asm_import("conio_var", __stackcall, cputc )
//#pragma asm_import("conio_var", __varcall, cputln, cputcxy, cputs, cputsxy)
//#pragma asm_import("conio_var", __varcall, textcolor, bgcolor, bordercolor, kbhit)
//#pragma asm_import("conio_var", __varcall, cursor, scroll)
//#pragma asm_import("conio_var", __varcall, screenlayer0, screenlayer1)
//#pragma asm_import("conio_var", __varcall, cpeekc, cpeekcxy)
extern void __conio_var_start();
extern void conio_x16_init();
extern void clrscr();
extern void gotoxy(__zp(2) char x, __zp(5) char y);
extern __zp($b) char wherex();
extern __zp(8) char wherey();
extern void screensize(__zp(6) char *x, __zp($e) char *y);
extern __zp(5) char screensizex();
extern __zp(2) char screensizey();
extern void cputln();
extern void cputc(__zp(8) char c);
extern void cputcxy(__zp(2) char x, __zp(5) char y, __zp(3) char c);
extern void cputs(__zp($c) const char *s);
extern void cputsxy(__zp(2) char x, __zp(5) char y, __zp($e) const char *s);
extern __zp(8) char textcolor(__zp(2) char color);
extern __zp(8) char bgcolor(__zp(2) char color);
extern __zp(8) char bordercolor(__zp(3) char color);
extern char kbhit();
extern __zp(8) char cursor(__zp(2) char onoff);
extern __zp(5) char scroll(__zp(3) char onoff);
extern void screenlayer1();
#include "conio_var.h"
// Linkage
#pragma link("printf-library.ld")