mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-04-08 14:37:40 +00:00
Implemented support for __address() on arrays. Removed support for pc parameter on inline kickasm. Closes #480. Closes #479
This commit is contained in:
parent
804d39cf0a
commit
cb1b9bece1
@ -71,23 +71,23 @@ public class VariableBuilder {
|
||||
}
|
||||
variable.setMemoryArea(this.getMemoryArea());
|
||||
variable.setMemoryAlignment(this.getAlignment());
|
||||
variable.setMemoryAddress(this.getAddress());
|
||||
variable.setDeclarationOnly(this.isDeclarationOnly());
|
||||
|
||||
|
||||
// Check if the symbol has already been declared
|
||||
Symbol declaredSymbol = scope.getLocalSymbol(varName);
|
||||
if(declaredSymbol!=null && !declaredSymbol.getFullName().equals(variable.getFullName()))
|
||||
if(declaredSymbol != null && !declaredSymbol.getFullName().equals(variable.getFullName()))
|
||||
// We found another symbol!
|
||||
declaredSymbol = null;
|
||||
|
||||
if(declaredSymbol!=null) {
|
||||
if(declaredSymbol != null) {
|
||||
if(!(declaredSymbol instanceof Variable))
|
||||
throw new CompileError("Error! Conflicting declarations for: "+variable.getFullName());
|
||||
throw new CompileError("Error! Conflicting declarations for: " + variable.getFullName());
|
||||
Variable declaredVar = (Variable) declaredSymbol;
|
||||
if(!declaredVar.isDeclarationOnly() && !variable.isDeclarationOnly())
|
||||
throw new CompileError("Error! Redefinition of variable: "+variable.getFullName());
|
||||
throw new CompileError("Error! Redefinition of variable: " + variable.getFullName());
|
||||
if(!SymbolTypeConversion.variableDeclarationMatch(declaredVar, variable))
|
||||
throw new CompileError("Error! Conflicting declarations for: "+variable.getFullName());
|
||||
throw new CompileError("Error! Conflicting declarations for: " + variable.getFullName());
|
||||
|
||||
// Update the variable with the definition
|
||||
if(!variable.isDeclarationOnly()) {
|
||||
@ -249,6 +249,7 @@ public class VariableBuilder {
|
||||
|
||||
/**
|
||||
* Declared but not defined. ( "extern" keyword)
|
||||
*
|
||||
* @return true if the variable is declared but not defined.
|
||||
*/
|
||||
public boolean isDeclarationOnly() {
|
||||
@ -282,7 +283,7 @@ public class VariableBuilder {
|
||||
VariableBuilderConfig.Scope scope = VariableBuilderConfig.getScope(isScopeGlobal(), isScopeLocal(), isScopeParameter(), isScopeMember());
|
||||
VariableBuilderConfig.Type type = VariableBuilderConfig.getType(isTypeInteger(), isArray(), isTypePointer(), isTypeStruct());
|
||||
VariableBuilderConfig.Setting setting = config.getSetting(scope, type);
|
||||
if(setting!=null && VariableBuilderConfig.Optimization.MA.equals(setting.optimization))
|
||||
if(setting != null && VariableBuilderConfig.Optimization.MA.equals(setting.optimization))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
@ -328,7 +329,7 @@ public class VariableBuilder {
|
||||
VariableBuilderConfig.Scope scope = VariableBuilderConfig.getScope(isScopeGlobal(), isScopeLocal(), isScopeParameter(), isScopeMember());
|
||||
VariableBuilderConfig.Type type = VariableBuilderConfig.getType(isTypeInteger(), isArray(), isTypePointer(), isTypeStruct());
|
||||
VariableBuilderConfig.Setting setting = config.getSetting(scope, type);
|
||||
if(setting!=null && VariableBuilderConfig.MemoryArea.MEM.equals(setting.memoryArea))
|
||||
if(setting != null && VariableBuilderConfig.MemoryArea.MEM.equals(setting.memoryArea))
|
||||
return Variable.MemoryArea.MAIN_MEMORY;
|
||||
else
|
||||
return Variable.MemoryArea.ZEROPAGE_MEMORY;
|
||||
@ -353,12 +354,32 @@ public class VariableBuilder {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any memory-address of the variables data
|
||||
*
|
||||
* @return The memory alignment
|
||||
*/
|
||||
public Long getAddress() {
|
||||
Directive.Address addressDirective = findDirective(Directive.Address.class, directives);
|
||||
if(addressDirective != null) {
|
||||
if(isArray()) {
|
||||
return addressDirective.address;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any hard-coded register allocation.
|
||||
*
|
||||
* @return Hard-coded register allocation. Null if not hard-coded.
|
||||
*/
|
||||
public Registers.Register getRegister() {
|
||||
|
||||
if(isArray())
|
||||
// Arrays are not put into registers
|
||||
return null;
|
||||
|
||||
Directive.Address addressDirective = findDirective(Directive.Address.class, directives);
|
||||
if(addressDirective != null) {
|
||||
Variable.MemoryArea memoryArea = (addressDirective.address < 0x100) ? Variable.MemoryArea.ZEROPAGE_MEMORY : Variable.MemoryArea.MAIN_MEMORY;
|
||||
@ -394,6 +415,7 @@ public class VariableBuilder {
|
||||
private <DirectiveClass extends Directive> boolean hasDirective(Class<DirectiveClass> directiveClass) {
|
||||
return findDirective(directiveClass, directives) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines whether a specific directive is present in the source
|
||||
*
|
||||
|
@ -148,11 +148,11 @@ public class ProgramValueIterator {
|
||||
if(location != null) {
|
||||
execute(new ProgramValue.ProgramValueKickAsmLocation(statementKickAsm), handler, statement, statementsIt, block);
|
||||
}
|
||||
RValue bytes = statementKickAsm.getLocation();
|
||||
RValue bytes = statementKickAsm.getBytes();
|
||||
if(bytes != null) {
|
||||
execute(new ProgramValue.ProgramValueKickAsmBytes(statementKickAsm), handler, statement, statementsIt, block);
|
||||
}
|
||||
RValue cycles = statementKickAsm.getLocation();
|
||||
RValue cycles = statementKickAsm.getCycles();
|
||||
if(cycles != null) {
|
||||
execute(new ProgramValue.ProgramValueKickAsmCycles(statementKickAsm), handler, statement, statementsIt, block);
|
||||
}
|
||||
|
@ -94,6 +94,9 @@ public class Variable implements Symbol {
|
||||
/** Specifies that the variable must be aligned in memory. Only allowed for arrays & strings. [Only Variables in memory and arrays] */
|
||||
private Integer memoryAlignment;
|
||||
|
||||
/** Specifies that the variable must be placed at an absolute address in memory. Only allowed for arrays & strings. [Only Variables in memory and arrays] */
|
||||
private Long memoryAddress;
|
||||
|
||||
/** The data segment to put the variable into (if it is allocated in memory). [Only variables stored in memory and arrays] */
|
||||
private String dataSegment;
|
||||
|
||||
@ -193,6 +196,7 @@ public class Variable implements Symbol {
|
||||
throw new InternalError("Cannot version non-PHI variable " + phiMaster.toString());
|
||||
Variable version = new Variable(phiMaster.getName() + "#" + versionNum, Kind.PHI_VERSION, phiMaster.getType(), phiMaster.getScope(), phiMaster.getMemoryArea(), phiMaster.getDataSegment(), phiMaster.getArraySpec(), null);
|
||||
version.setMemoryAlignment(phiMaster.getMemoryAlignment());
|
||||
version.setMemoryAddress(phiMaster.getMemoryAddress());
|
||||
version.setOptimize(phiMaster.isOptimize());
|
||||
version.setNoModify(phiMaster.isNoModify());
|
||||
version.setRegister(phiMaster.getRegister());
|
||||
@ -239,6 +243,7 @@ public class Variable implements Symbol {
|
||||
public static Variable createConstant(Variable variable, ConstantValue constantValue) {
|
||||
Variable constVar = new Variable(variable.getName(), Kind.CONSTANT, variable.getType(), variable.getScope(), MemoryArea.MAIN_MEMORY, variable.getDataSegment(), variable.getArraySpec(), constantValue);
|
||||
constVar.setMemoryAlignment(variable.getMemoryAlignment());
|
||||
constVar.setMemoryAddress(variable.getMemoryAddress());
|
||||
constVar.setOptimize(variable.isOptimize());
|
||||
constVar.setNoModify(variable.isNoModify());
|
||||
constVar.setRegister(variable.getRegister());
|
||||
@ -261,6 +266,7 @@ public class Variable implements Symbol {
|
||||
public static Variable createCopy(String name, Scope scope, Variable original) {
|
||||
Variable copy = new Variable(name, original.getKind(), original.getType(), scope, original.getMemoryArea(), original.getDataSegment(), original.getArraySpec(), original.getInitValue());
|
||||
copy.setMemoryAlignment(original.getMemoryAlignment());
|
||||
copy.setMemoryAddress(original.getMemoryAddress());
|
||||
copy.setOptimize(original.isOptimize());
|
||||
copy.setNoModify(original.isNoModify());
|
||||
copy.setPermanent(original.isPermanent());
|
||||
@ -576,6 +582,14 @@ public class Variable implements Symbol {
|
||||
this.memoryAlignment = memoryAlignment;
|
||||
}
|
||||
|
||||
public Long getMemoryAddress() {
|
||||
return memoryAddress;
|
||||
}
|
||||
|
||||
public void setMemoryAddress(Long memoryAddress) {
|
||||
this.memoryAddress = memoryAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the variable a struct that should be unwound to member variables
|
||||
*
|
||||
|
@ -453,6 +453,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
||||
for(AsmDirective asmDirective : asmDirectives) {
|
||||
if(asmDirective instanceof AsmDirectiveLocation) {
|
||||
statementKickAsm.setLocation(((AsmDirectiveLocation) asmDirective).getAddress());
|
||||
throw new RuntimeException("KickAsm pc directive no longer supported!");
|
||||
} else if(asmDirective instanceof AsmDirectiveBytes) {
|
||||
statementKickAsm.setBytes(((AsmDirectiveBytes) asmDirective).getBytes());
|
||||
} else if(asmDirective instanceof AsmDirectiveCycles) {
|
||||
|
@ -164,6 +164,7 @@ public class Pass4CodeGeneration {
|
||||
currentScope = ScopeRef.ROOT;
|
||||
asm.startChunk(currentScope, null, "File Data");
|
||||
addData(asm, ScopeRef.ROOT);
|
||||
addAbsoluteAddressData(asm, ScopeRef.ROOT);
|
||||
// Add all absolutely placed inline KickAsm
|
||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
for(Statement statement : block.getStatements()) {
|
||||
@ -310,6 +311,7 @@ public class Pass4CodeGeneration {
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
// TODO: Literal calculation should not be necessary anymore.
|
||||
ConstantLiteral literal = constantValue.calculateLiteral(getScope());
|
||||
if(literal instanceof ConstantString) {
|
||||
return true;
|
||||
@ -489,12 +491,60 @@ public class Pass4CodeGeneration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add constants with data and memory variables with data for a scope.
|
||||
* Added after the the code of the scope.
|
||||
* Add all constants with data that must be placed at an absolute address
|
||||
* Added at the end of the file
|
||||
*
|
||||
* @param asm The ASM program
|
||||
* @param scopeRef The scope
|
||||
*/
|
||||
private void addAbsoluteAddressData(AsmProgram asm, ScopeRef scopeRef) {
|
||||
Scope scope = program.getScope().getScope(scopeRef);
|
||||
Collection<Variable> scopeConstants = scope.getAllConstants(false);
|
||||
Set<String> added = new LinkedHashSet<>();
|
||||
// Add all constants arrays incl. strings with data
|
||||
for(Variable constantVar : scopeConstants) {
|
||||
if(hasData(constantVar)) {
|
||||
// Skip if already added
|
||||
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
|
||||
if(added.contains(asmName)) {
|
||||
continue;
|
||||
}
|
||||
// Skip if address is not absolute
|
||||
if(constantVar.getMemoryAddress()==null)
|
||||
continue;
|
||||
// Set segment
|
||||
setCurrentSegment(constantVar.getDataSegment(), asm);
|
||||
// Set absolute address
|
||||
asm.addLine(new AsmSetPc(asmName, AsmFormat.getAsmNumber(constantVar.getMemoryAddress())));
|
||||
// Add any comments
|
||||
generateComments(asm, constantVar.getComments());
|
||||
// Add any alignment
|
||||
Integer declaredAlignment = constantVar.getMemoryAlignment();
|
||||
if(declaredAlignment != null) {
|
||||
String alignment = AsmFormat.getAsmNumber(declaredAlignment);
|
||||
asm.addDataAlignment(alignment);
|
||||
}
|
||||
ConstantValue constantValue = constantVar.getInitValue();
|
||||
if(constantValue instanceof ConstantArray || constantValue instanceof ConstantString || constantValue instanceof ConstantStructValue) {
|
||||
AsmDataChunk asmDataChunk = new AsmDataChunk();
|
||||
addChunkData(asmDataChunk, constantValue, constantVar.getType(), constantVar.getArraySpec(), scopeRef);
|
||||
asmDataChunk.addToAsm(AsmFormat.asmFix(asmName), asm);
|
||||
} else {
|
||||
throw new InternalError("Constant Variable not handled " + constantVar.toString(program));
|
||||
}
|
||||
added.add(asmName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add constants with data and memory variables with data for a scope.
|
||||
* Added after the the code of the scope.
|
||||
*
|
||||
* @param asm The ASM program
|
||||
* @param scopeRef The scope
|
||||
*/
|
||||
private void addData(AsmProgram asm, ScopeRef scopeRef) {
|
||||
Scope scope = program.getScope().getScope(scopeRef);
|
||||
Collection<Variable> scopeConstants = scope.getAllConstants(false);
|
||||
@ -507,6 +557,9 @@ public class Pass4CodeGeneration {
|
||||
if(added.contains(asmName)) {
|
||||
continue;
|
||||
}
|
||||
// Skip if address is absolute
|
||||
if(constantVar.getMemoryAddress()!=null)
|
||||
continue;
|
||||
// Set segment
|
||||
setCurrentSegment(constantVar.getDataSegment(), asm);
|
||||
// Add any comments
|
||||
|
@ -660,6 +660,11 @@ public class TestPrograms {
|
||||
compileAndCompare("examples/kernalload/kernalload.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKrillLoad() throws IOException, URISyntaxException {
|
||||
compileAndCompare("examples/krillload/krillload.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstantWithPrePost() throws IOException, URISyntaxException {
|
||||
assertError("constant-prepost.c", "Constant value contains a pre/post-modifier");
|
||||
@ -1268,6 +1273,11 @@ public class TestPrograms {
|
||||
compileAndCompare("arrays-init-kasm-0.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArraysInitKasm1() throws IOException, URISyntaxException {
|
||||
compileAndCompare("arrays-init-kasm-1.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScreenCenterAngle() throws IOException, URISyntaxException {
|
||||
compileAndCompare("screen-center-angle.c");
|
||||
|
13
src/test/kc/arrays-init-kasm-1.c
Normal file
13
src/test/kc/arrays-init-kasm-1.c
Normal file
@ -0,0 +1,13 @@
|
||||
// Test initializing array using KickAssembler
|
||||
// Place array at hardcoded address
|
||||
|
||||
// Sinus table at an absolute address in memory
|
||||
__address(0x1000) char SINTAB[256] = kickasm {{
|
||||
.fill 256, 128 + 128*sin(i*2*PI/256)
|
||||
}};
|
||||
|
||||
char* SCREEN = 0x400;
|
||||
|
||||
void main() {
|
||||
SCREEN[0] = SINTAB[0];
|
||||
}
|
@ -7,10 +7,9 @@ char SIN[256] = kickasm {{
|
||||
}
|
||||
}};
|
||||
|
||||
char* SIN_SPRITE = 0x2800;
|
||||
kickasm(pc SIN_SPRITE) {{
|
||||
__address(0x3800) char SIN_SPRITE[0x40] = kickasm {{
|
||||
.fill $40, $ff
|
||||
}}
|
||||
}};
|
||||
|
||||
char sin_idx = 0;
|
||||
|
||||
|
@ -9,10 +9,30 @@ char* const PLAYFIELD_SCREEN_2 = 0x2c00;
|
||||
char* const PLAYFIELD_SPRITE_PTRS_1 = (PLAYFIELD_SCREEN_1+SPRITE_PTRS);
|
||||
// Screen Sprite pointers on screen 2
|
||||
char* const PLAYFIELD_SPRITE_PTRS_2 = (PLAYFIELD_SCREEN_2+SPRITE_PTRS);
|
||||
// Address of the sprites covering the playfield
|
||||
char* const PLAYFIELD_SPRITES = 0x3000;
|
||||
|
||||
// Sprites covering the playfield
|
||||
__address(0x3000) char PLAYFIELD_SPRITES[30*64] = kickasm(resource "playfield-sprites.png") {{
|
||||
.var sprites = LoadPicture("playfield-sprites.png", List().add($010101, $000000))
|
||||
// Put the sprites into memory
|
||||
.for(var sy=0;sy<10;sy++) {
|
||||
.var sprite_gfx_y = sy*20
|
||||
.for(var sx=0;sx<3;sx++) {
|
||||
.for (var y=0;y<21; y++) {
|
||||
.var gfx_y = sprite_gfx_y + mod(2100+y-sprite_gfx_y,21)
|
||||
.for (var c=0; c<3; c++) {
|
||||
.byte sprites.getSinglecolorByte(sx*3+c,gfx_y)
|
||||
}
|
||||
}
|
||||
.byte 0
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Address of the charset
|
||||
char* const PLAYFIELD_CHARSET = 0x2800;
|
||||
__address(0x2800) char PLAYFIELD_CHARSET[] = kickasm(resource "playfield-screen.imap") {{
|
||||
.fill 8,$00 // Place a filled char at the start of the charset
|
||||
.import binary "playfield-screen.imap"
|
||||
}};
|
||||
|
||||
// The size of the playfield
|
||||
const char PLAYFIELD_LINES = 22;
|
||||
|
@ -5,11 +5,6 @@
|
||||
#include "tetris-data.c"
|
||||
#include "tetris-pieces.c"
|
||||
|
||||
kickasm(pc PLAYFIELD_CHARSET, resource "playfield-screen.imap") {{
|
||||
.fill 8,$00 // Place a filled char at the start of the charset
|
||||
.import binary "playfield-screen.imap"
|
||||
}}
|
||||
|
||||
// Address of the original playscreen chars
|
||||
const char PLAYFIELD_SCREEN_ORIGINAL_WIDTH=32;
|
||||
const char PLAYFIELD_SCREEN_ORIGINAL[] = kickasm(resource "playfield-screen.iscr", resource "playfield-extended.col" ) {{
|
||||
|
@ -3,23 +3,6 @@
|
||||
#include <c64.h>
|
||||
#include "tetris-data.c"
|
||||
|
||||
kickasm(pc PLAYFIELD_SPRITES, resource "playfield-sprites.png") {{
|
||||
.var sprites = LoadPicture("playfield-sprites.png", List().add($010101, $000000))
|
||||
// Put the sprites into memory
|
||||
.for(var sy=0;sy<10;sy++) {
|
||||
.var sprite_gfx_y = sy*20
|
||||
.for(var sx=0;sx<3;sx++) {
|
||||
.for (var y=0;y<21; y++) {
|
||||
.var gfx_y = sprite_gfx_y + mod(2100+y-sprite_gfx_y,21)
|
||||
.for (var c=0; c<3; c++) {
|
||||
.byte sprites.getSinglecolorByte(sx*3+c,gfx_y)
|
||||
}
|
||||
}
|
||||
.byte 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
// Setup the sprites
|
||||
void sprites_init() {
|
||||
*SPRITES_ENABLE = %00001111;
|
||||
|
@ -438,13 +438,12 @@ void mulf_init() {
|
||||
*/
|
||||
|
||||
// A single sprite
|
||||
char* SPRITE = $3000;
|
||||
kickasm(pc SPRITE, resource "balloon.png") {{
|
||||
__address(0x3000) char SPRITE[] = kickasm(resource "balloon.png") {{
|
||||
.var pic = LoadPicture("balloon.png", List().add($000000, $ffffff))
|
||||
.for (var y=0; y<21; y++)
|
||||
.for (var x=0;x<3; x++)
|
||||
.byte pic.getSinglecolorByte(x,y)
|
||||
}}
|
||||
}};
|
||||
|
||||
// Perspective multiplication table containing (d/(z0-z)[z] for each z-value
|
||||
signed char align(0x100) PERSP_Z[0x100] = kickasm {{
|
||||
|
BIN
src/test/kc/examples/krillload/install-c64.prg
Normal file
BIN
src/test/kc/examples/krillload/install-c64.prg
Normal file
Binary file not shown.
62
src/test/kc/examples/krillload/krill.c
Executable file
62
src/test/kc/examples/krillload/krill.c
Executable file
@ -0,0 +1,62 @@
|
||||
// Krill's Loader for C64 v184
|
||||
// https://csdb.dk/release/?id=189130
|
||||
// Uses Install/Loader files built using the following command.
|
||||
// > make PLATFORM=c64 prg INSTALL=3400 RESIDENT=3000 ZP=e8
|
||||
// To place the loader routines elsewhere in memory: re-make the krill loader, update the 2 prg-files and update the __address() below.
|
||||
|
||||
// Reserve the zero-page addresses used by the Krill routines
|
||||
#pragma zp_reserve(0xe8..0xfd)
|
||||
|
||||
// The Krill loader routine that can load files.
|
||||
__address(0x3000) char KRILL_LOADER[] = kickasm(resource "loader-c64.prg") {{
|
||||
.import c64 "loader-c64.prg"
|
||||
}};
|
||||
|
||||
// The Krill Install routine that can install the drive-side code
|
||||
__address(0x3400) char KRILL_INSTALL[] = kickasm(resource "install-c64.prg") {{
|
||||
.import c64 "install-c64.prg"
|
||||
}};
|
||||
|
||||
// Status returned from the Krill functions
|
||||
enum KrillStatus {
|
||||
KRILL_OK = 0x00,
|
||||
KRILL_DEVICE_INCOMPATIBLE = 0xfb,
|
||||
KRILL_TOO_MANY_DEVICES = 0xfc,
|
||||
KRILL_GENERIC_KERNAL_ERROR = 0xfd,
|
||||
KRILL_DEVICE_NOT_PRESENT = 0xfe,
|
||||
KRILL_FILE_NOT_FOUND = 0xff
|
||||
};
|
||||
|
||||
// Install drive-side code portion(s) must be installed in the active drive.
|
||||
// Before the loader can operate, its drive-side code portion(s) must be installed in the drive(s).
|
||||
// The drive-side portion remains resident in the drive. After successful
|
||||
// installation, the install routine is not needed any more and may be overwritten.
|
||||
// The KERNAL ROM may be disabled and zeropage variables clobbered.
|
||||
// Returns the status of the installation
|
||||
enum KrillStatus krill_install() {
|
||||
enum KrillStatus* const status = 0xff;
|
||||
asm(clobbers "AXY") {
|
||||
jsr KRILL_INSTALL
|
||||
sta status
|
||||
}
|
||||
return *status;
|
||||
}
|
||||
|
||||
// Load a file from the active drive without decompression.
|
||||
// While loading using filenames with wildcards ("?" and "*") is not possible,
|
||||
// subsequent files following the previously-loaded file can be loaded via a
|
||||
// zero-length filename
|
||||
// - filename - The name of the file to load (zero-terminated in petscii encoding)
|
||||
// Returns the status of the load
|
||||
enum KrillStatus krill_loadraw(char* filename) {
|
||||
enum KrillStatus* const status = 0xff;
|
||||
char** const fname = 0xfe;
|
||||
*fname = filename;
|
||||
asm(clobbers "AXY") {
|
||||
ldx fname
|
||||
ldy fname+1
|
||||
jsr KRILL_LOADER
|
||||
sta status
|
||||
}
|
||||
return *status;
|
||||
}
|
49
src/test/kc/examples/krillload/krillload.c
Normal file
49
src/test/kc/examples/krillload/krillload.c
Normal file
@ -0,0 +1,49 @@
|
||||
// Tests Krill Loader
|
||||
// Load a file to memory using the Krill loader
|
||||
// The krillload.ld link file creates a D64 disk image containing the executable and the sprite.
|
||||
// To execute the program succesfully you must mount the D64 disk image and execute the krillload.PRG program
|
||||
#pragma link("krillload.ld")
|
||||
#pragma extension("d64")
|
||||
|
||||
// Encoding needed for filename
|
||||
#pragma encoding(petscii_mixed)
|
||||
|
||||
#include "krill.c"
|
||||
#include <c64.h>
|
||||
|
||||
// Sprite file
|
||||
#pragma data_seg(Sprite)
|
||||
// The sprite data
|
||||
export __address(0x2040) char SPRITE[0x40] = kickasm(resource "sprite.png") {{
|
||||
.var pic = LoadPicture("sprite.png", List().add($000000, $ffffff))
|
||||
.for (var y=0; y<21; y++)
|
||||
.for (var x=0;x<3; x++)
|
||||
.byte pic.getSinglecolorByte(x,y)
|
||||
}};
|
||||
|
||||
// Program file
|
||||
#pragma data_seg(Data)
|
||||
|
||||
char* const SCREEN = 0x0400;
|
||||
char* const SPRITES_PTR = SCREEN+SPRITE_PTRS;
|
||||
|
||||
void main() {
|
||||
// Install the Krill drive code
|
||||
char status = krill_install();
|
||||
if(status!=KRILL_OK) {
|
||||
VICII->BORDER_COLOR = 0x02;
|
||||
return;
|
||||
}
|
||||
// Load sprite file from disk
|
||||
status = krill_loadraw("sprite");
|
||||
if(status!=KRILL_OK) {
|
||||
VICII->BORDER_COLOR = 0x02;
|
||||
return;
|
||||
}
|
||||
// Show the loaded sprite on screen
|
||||
VICII->SPRITES_ENABLE = %00000001;
|
||||
SPRITES_PTR[0] = toSpritePtr(SPRITE);
|
||||
SPRITES_COLOR[0] = GREEN;
|
||||
SPRITES_XPOS[0] = 0x15;
|
||||
SPRITES_YPOS[0] = 0x33;
|
||||
}
|
13
src/test/kc/examples/krillload/krillload.ld
Normal file
13
src/test/kc/examples/krillload/krillload.ld
Normal file
@ -0,0 +1,13 @@
|
||||
// Create a D64 disk containing the program and a sprite file
|
||||
.disk [filename="%O", name="DISK", id=1] {
|
||||
[name="KRILLLOAD", type="prg", segments="Program"],
|
||||
[name="SPRITE", type="prg", segments="Sprite"]
|
||||
}
|
||||
.segmentdef Program [segments="Basic, Code, Data"]
|
||||
.segmentdef Basic [start=$0801]
|
||||
.segmentdef Code [start=$080d]
|
||||
.segmentdef Data [startAfter="Code"]
|
||||
.segmentdef Sprite
|
||||
.segment Basic
|
||||
:BasicUpstart(%E)
|
||||
.segment Code
|
BIN
src/test/kc/examples/krillload/loader-c64.prg
Normal file
BIN
src/test/kc/examples/krillload/loader-c64.prg
Normal file
Binary file not shown.
BIN
src/test/kc/examples/krillload/sprite.png
Normal file
BIN
src/test/kc/examples/krillload/sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -13,13 +13,12 @@ char align(0x100) YSIN[0x100] = kickasm {{
|
||||
.byte round(min+(ampl/2)+(ampl/2)*sin(toRadians(360*i/256)))
|
||||
}};
|
||||
|
||||
char* SPRITE = $2000;
|
||||
kickasm(pc SPRITE, resource "balloon.png") {{
|
||||
__address(0x2000) char SPRITE[] = kickasm(resource "balloon.png") {{
|
||||
.var pic = LoadPicture("balloon.png", List().add($000000, $ffffff))
|
||||
.for (var y=0; y<21; y++)
|
||||
.for (var x=0;x<3; x++)
|
||||
.byte pic.getSinglecolorByte(x,y)
|
||||
}}
|
||||
}};
|
||||
|
||||
void main() {
|
||||
asm { sei }
|
||||
|
@ -1,29 +1,26 @@
|
||||
// A simple SID music player playing music in the main loop.
|
||||
#include <c64.h>
|
||||
|
||||
char* const MUSIC = $1000;
|
||||
|
||||
// Load the SID
|
||||
kickasm(resource "toiletrensdyr.sid") {{
|
||||
// SID tune at an absolute address
|
||||
__address(0x1000) char MUSIC[] = kickasm(resource "toiletrensdyr.sid") {{
|
||||
.const music = LoadSid("toiletrensdyr.sid")
|
||||
}}
|
||||
|
||||
// Place the SID into memory
|
||||
kickasm(pc MUSIC) {{
|
||||
.fill music.size, music.getData(i)
|
||||
}}
|
||||
|
||||
}};
|
||||
// Pointer to the music init routine
|
||||
void()* musicInit = (void()*) MUSIC;
|
||||
// Pointer to the music play routine
|
||||
void()* musicPlay = (void()*) MUSIC+3;
|
||||
|
||||
// Play the music
|
||||
void main() {
|
||||
// Initialize the music
|
||||
asm { jsr music.init }
|
||||
(*musicInit)();
|
||||
do {
|
||||
// Wait for the RASTER
|
||||
do {} while (VICII->RASTER != $fd);
|
||||
(VICII->BORDER_COLOR)++;
|
||||
// Play the music
|
||||
asm { jsr music.play }
|
||||
(*musicPlay)();
|
||||
(VICII->BORDER_COLOR)--;
|
||||
} while (true);
|
||||
}
|
@ -1,25 +1,20 @@
|
||||
// A simple SID music player using RASTER IRQ
|
||||
#include <c64.h>
|
||||
|
||||
char* const MUSIC = $1000;
|
||||
|
||||
// Load the SID
|
||||
kickasm(resource "toiletrensdyr.sid") {{
|
||||
// SID tune at an absolute address
|
||||
__address(0x1000) char MUSIC[] = kickasm(resource "toiletrensdyr.sid") {{
|
||||
.const music = LoadSid("toiletrensdyr.sid")
|
||||
}}
|
||||
|
||||
// Place the SID into memory
|
||||
kickasm(pc MUSIC) {{
|
||||
.fill music.size, music.getData(i)
|
||||
}}
|
||||
|
||||
}};
|
||||
// Pointer to the music init routine
|
||||
void()* musicInit = (void()*) MUSIC;
|
||||
// Pointer to the music play routine
|
||||
void()* musicPlay = (void()*) MUSIC+3;
|
||||
|
||||
// Setup Raster IRQ and initialize SID player
|
||||
void main() {
|
||||
asm {
|
||||
sei
|
||||
jsr music.init
|
||||
}
|
||||
asm { sei }
|
||||
(*musicInit)();
|
||||
// Disable CIA 1 Timer IRQ
|
||||
CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR;
|
||||
// Set raster line to $fd
|
||||
@ -36,7 +31,7 @@ void main() {
|
||||
interrupt(kernel_keyboard) void irq_play() {
|
||||
(VICII->BORDER_COLOR)++;
|
||||
// Play SID
|
||||
asm { jsr music.play }
|
||||
(*musicPlay)();
|
||||
// Acknowledge the IRQ
|
||||
VICII->IRQ_STATUS = IRQ_RASTER;
|
||||
(VICII->BORDER_COLOR)--;
|
||||
|
@ -76,10 +76,9 @@ void anim() {
|
||||
}
|
||||
|
||||
// A single sprite
|
||||
char* SPRITE = $3000;
|
||||
kickasm(pc SPRITE, resource "balloon.png") {{
|
||||
__address(0x3000) char SPRITE[] = kickasm(resource "balloon.png") {{
|
||||
.var pic = LoadPicture("balloon.png", List().add($000000, $ffffff))
|
||||
.for (var y=0; y<21; y++)
|
||||
.for (var x=0;x<3; x++)
|
||||
.byte pic.getSinglecolorByte(x,y)
|
||||
}}
|
||||
}};
|
||||
|
@ -3,14 +3,13 @@
|
||||
#include <string.h>
|
||||
|
||||
char* SCREEN = $400;
|
||||
char* LOGO = $2000;
|
||||
kickasm(resource "logo.png", pc LOGO, bytes 6*40*8) {{
|
||||
__address(0x2000) char LOGO[6*40*8] = kickasm(resource "logo.png") {{
|
||||
.var logoPic = LoadPicture("logo.png", List().add($444444, $808080, $000000, $ffffff))
|
||||
.for (var y=0; y<6 ; y++)
|
||||
.for (var x=0;x<40; x++)
|
||||
.for(var cp=0; cp<8; cp++)
|
||||
.byte logoPic.getMulticolorByte(x,cp+y*8)
|
||||
}}
|
||||
}};
|
||||
|
||||
const unsigned int XSIN_SIZE = 512;
|
||||
|
||||
|
@ -2,15 +2,13 @@
|
||||
#include <string.h>
|
||||
|
||||
char* SCREEN = $400;
|
||||
char* LOGO = $2000;
|
||||
|
||||
kickasm(resource "logo.png", pc LOGO, bytes 6*40*8 ) {{
|
||||
__address(0x2000) char LOGO[6*40*8] = kickasm(resource "logo.png") {{
|
||||
.var logoPic = LoadPicture("logo.png", List().add($444444, $808080, $000000, $ffffff))
|
||||
.for (var y=0; y<6 ; y++)
|
||||
.for (var x=0;x<40; x++)
|
||||
.for(var cp=0; cp<8; cp++)
|
||||
.byte logoPic.getMulticolorByte(x,cp+y*8)
|
||||
}}
|
||||
}};
|
||||
|
||||
void main() {
|
||||
VICII->BORDER_COLOR = WHITE;
|
||||
|
@ -1,10 +1,8 @@
|
||||
// Example of inline kickasm data
|
||||
|
||||
byte* const sintab = $1000;
|
||||
kickasm(pc sintab) {{
|
||||
__address(0x1000) char sintab[] = kickasm {{
|
||||
.fill 25, 20 + 20*sin(toRadians(i*360/25))
|
||||
}}
|
||||
|
||||
}};
|
||||
|
||||
void main() {
|
||||
byte* screen = $400;
|
||||
|
@ -1,12 +1,11 @@
|
||||
// Example of inline kickasm resource data
|
||||
|
||||
byte* const SPRITE = $0c00;
|
||||
kickasm(pc SPRITE, resource "balloon.png") {{
|
||||
__address(0x0c00) byte SPRITE[] = kickasm(resource "balloon.png") {{
|
||||
.var pic = LoadPicture("balloon.png", List().add($000000, $ffffff))
|
||||
.for (var y=0; y<21; y++)
|
||||
.for (var x=0;x<3; x++)
|
||||
.byte pic.getSinglecolorByte(x,y)
|
||||
}}
|
||||
}};
|
||||
|
||||
byte* const SCREEN= $400;
|
||||
byte* const SPRITES_ENABLE = $d015;
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Test inline KickAssembler code with PC location specification
|
||||
|
||||
byte* TABLE = $2000;
|
||||
__address(0x2000) byte TABLE[] = kickasm {{
|
||||
.byte 1, 2, 3
|
||||
}};
|
||||
|
||||
void main() {
|
||||
byte* BORDER_COLOR = $d020;
|
||||
@ -10,6 +12,3 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
kickasm(pc TABLE) {{
|
||||
.byte 1, 2, 3
|
||||
}}
|
@ -28,9 +28,6 @@
|
||||
.label CIA2_TIMER_AB = $dd04
|
||||
.label SCREEN = $400
|
||||
.label COS = SIN+$40
|
||||
// A single sprite
|
||||
.label SPRITE = $3000
|
||||
// kickasm
|
||||
// sin(x) = cos(x+PI/2)
|
||||
main: {
|
||||
// asm
|
||||
@ -459,7 +456,7 @@ init: {
|
||||
ldx #0
|
||||
__b1:
|
||||
// sprites_ptr[i] = (char)(SPRITE/$40)
|
||||
lda #SPRITE/$40
|
||||
lda #$ff&SPRITE/$40
|
||||
sta sprites_ptr,x
|
||||
// SPRITES_COLOR[i] = GREEN
|
||||
lda #GREEN
|
||||
@ -635,8 +632,10 @@ SIN:
|
||||
// Positions to rotate
|
||||
xs: .byte -$46, -$46, -$46, 0, 0, $46, $46, $46
|
||||
ys: .byte -$46, 0, $46, -$46, $46, -$46, 0, $46
|
||||
.pc = SPRITE "SPRITE"
|
||||
.var pic = LoadPicture("balloon.png", List().add($000000, $ffffff))
|
||||
.pc = $3000 "SPRITE"
|
||||
// A single sprite
|
||||
SPRITE:
|
||||
.var pic = LoadPicture("balloon.png", List().add($000000, $ffffff))
|
||||
.for (var y=0; y<21; y++)
|
||||
.for (var x=0;x<3; x++)
|
||||
.byte pic.getSinglecolorByte(x,y)
|
||||
|
@ -29,11 +29,8 @@
|
||||
// Color Ram
|
||||
.label COLS = $d800
|
||||
.label SCREEN = $400
|
||||
.label LOGO = $2000
|
||||
// Remainder after unsigned 16-bit division
|
||||
.label rem16u = $12
|
||||
.label xsin_idx = 2
|
||||
// kickasm
|
||||
.label rem16u = $1d
|
||||
.label xsin_idx = $27
|
||||
main: {
|
||||
.const toD0181_return = (>(SCREEN&$3fff)*4)|(>LOGO)/4&$f
|
||||
// asm
|
||||
@ -86,9 +83,9 @@ main: {
|
||||
rts
|
||||
}
|
||||
loop: {
|
||||
.label __2 = $18
|
||||
.label __7 = $18
|
||||
.label xpos = $18
|
||||
.label __2 = $16
|
||||
.label __7 = $16
|
||||
.label xpos = $16
|
||||
lda #<0
|
||||
sta.z xsin_idx
|
||||
sta.z xsin_idx+1
|
||||
@ -145,13 +142,13 @@ loop: {
|
||||
dec VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
|
||||
jmp __b1
|
||||
}
|
||||
// render_logo(signed word zp($18) xpos)
|
||||
// render_logo(signed word zp($16) xpos)
|
||||
render_logo: {
|
||||
.label __2 = $1a
|
||||
.label xpos = $18
|
||||
.label x_char = $1c
|
||||
.label logo_idx = 4
|
||||
.label logo_idx_1 = 5
|
||||
.label __2 = $1d
|
||||
.label xpos = $16
|
||||
.label x_char = $18
|
||||
.label logo_idx = 2
|
||||
.label logo_idx_1 = 3
|
||||
// (char)xpos&7
|
||||
lda.z xpos
|
||||
and #7
|
||||
@ -318,19 +315,19 @@ render_logo: {
|
||||
// Generate signed int sinus table - with values in the range min-max.
|
||||
// sintab - the table to generate into
|
||||
// wavelength - the number of sinus points in a total sinus wavelength (the size of the table)
|
||||
// sin16s_gen2(signed word* zp($18) sintab)
|
||||
// sin16s_gen2(signed word* zp($14) sintab)
|
||||
sin16s_gen2: {
|
||||
.const min = -$140
|
||||
.const max = $140
|
||||
.label ampl = max-min
|
||||
.label __6 = $a
|
||||
.label __8 = $29
|
||||
.label step = $1d
|
||||
.label sintab = $18
|
||||
.label __6 = 8
|
||||
.label __8 = $1d
|
||||
.label step = $19
|
||||
.label sintab = $14
|
||||
// u[4.28]
|
||||
// Iterate over the table
|
||||
.label x = 6
|
||||
.label i = $16
|
||||
.label x = 4
|
||||
.label i = $27
|
||||
// div32u16u(PI2_u4f28, wavelength)
|
||||
jsr div32u16u
|
||||
// div32u16u(PI2_u4f28, wavelength)
|
||||
@ -418,13 +415,13 @@ sin16s_gen2: {
|
||||
}
|
||||
// Multiply of two signed ints to a signed long
|
||||
// Fixes offsets introduced by using unsigned multiplication
|
||||
// mul16s(signed word zp($14) a)
|
||||
// mul16s(signed word zp($12) a)
|
||||
mul16s: {
|
||||
.label __6 = $25
|
||||
.label __11 = $25
|
||||
.label m = $a
|
||||
.label return = $a
|
||||
.label a = $14
|
||||
.label __6 = $23
|
||||
.label __11 = $23
|
||||
.label m = 8
|
||||
.label return = 8
|
||||
.label a = $12
|
||||
// mul16u((unsigned int)a, (unsigned int) b)
|
||||
lda.z a
|
||||
sta.z mul16u.a
|
||||
@ -463,13 +460,13 @@ mul16s: {
|
||||
rts
|
||||
}
|
||||
// Perform binary multiplication of two unsigned 16-bit unsigned ints into a 32-bit unsigned long
|
||||
// mul16u(word zp($29) a, word zp($12) b)
|
||||
// mul16u(word zp($10) a, word zp($1d) b)
|
||||
mul16u: {
|
||||
.label mb = $e
|
||||
.label a = $29
|
||||
.label res = $a
|
||||
.label return = $a
|
||||
.label b = $12
|
||||
.label mb = $c
|
||||
.label a = $10
|
||||
.label res = 8
|
||||
.label return = 8
|
||||
.label b = $1d
|
||||
// mb = b
|
||||
lda.z b
|
||||
sta.z mb
|
||||
@ -527,20 +524,20 @@ mul16u: {
|
||||
// Calculate signed int sinus sin(x)
|
||||
// x: unsigned long input u[4.28] in the interval $00000000 - PI2_u4f28
|
||||
// result: signed int sin(x) s[0.15] - using the full range -$7fff - $7fff
|
||||
// sin16s(dword zp($e) x)
|
||||
// sin16s(dword zp($c) x)
|
||||
sin16s: {
|
||||
.label __4 = $21
|
||||
.label x = $e
|
||||
.label return = $14
|
||||
.label x1 = $25
|
||||
.label x2 = $1a
|
||||
.label x3 = $1a
|
||||
.label x3_6 = $27
|
||||
.label usinx = $14
|
||||
.label x4 = $1a
|
||||
.label x5 = $27
|
||||
.label x5_128 = $27
|
||||
.label sinx = $14
|
||||
.label __4 = $1f
|
||||
.label x = $c
|
||||
.label return = $12
|
||||
.label x1 = $23
|
||||
.label x2 = $16
|
||||
.label x3 = $16
|
||||
.label x3_6 = $25
|
||||
.label usinx = $12
|
||||
.label x4 = $16
|
||||
.label x5 = $25
|
||||
.label x5_128 = $25
|
||||
.label sinx = $12
|
||||
// if(x >= PI_u4f28 )
|
||||
lda.z x+3
|
||||
cmp #>PI_u4f28>>$10
|
||||
@ -738,14 +735,14 @@ sin16s: {
|
||||
}
|
||||
// Calculate val*val for two unsigned int values - the result is 16 selected bits of the 32-bit result.
|
||||
// The select parameter indicates how many of the highest bits of the 32-bit result to skip
|
||||
// mulu16_sel(word zp($1a) v1, word zp($12) v2, byte register(X) select)
|
||||
// mulu16_sel(word zp($16) v1, word zp($1d) v2, byte register(X) select)
|
||||
mulu16_sel: {
|
||||
.label __0 = $a
|
||||
.label __1 = $a
|
||||
.label v1 = $1a
|
||||
.label v2 = $12
|
||||
.label return = $27
|
||||
.label return_1 = $1a
|
||||
.label __0 = 8
|
||||
.label __1 = 8
|
||||
.label v1 = $16
|
||||
.label v2 = $1d
|
||||
.label return = $25
|
||||
.label return_1 = $16
|
||||
// mul16u(v1, v2)
|
||||
lda.z v1
|
||||
sta.z mul16u.a
|
||||
@ -775,9 +772,9 @@ mulu16_sel: {
|
||||
// Divide unsigned 32-bit unsigned long dividend with a 16-bit unsigned int divisor
|
||||
// The 16-bit unsigned int remainder can be found in rem16u after the division
|
||||
div32u16u: {
|
||||
.label quotient_hi = $27
|
||||
.label quotient_lo = $14
|
||||
.label return = $1d
|
||||
.label quotient_hi = $25
|
||||
.label quotient_lo = $12
|
||||
.label return = $19
|
||||
// divr16u(>dividend, divisor, 0)
|
||||
lda #<PI2_u4f28>>$10
|
||||
sta.z divr16u.dividend
|
||||
@ -817,12 +814,12 @@ div32u16u: {
|
||||
// Returns the quotient dividend/divisor.
|
||||
// The final remainder will be set into the global variable rem16u
|
||||
// Implemented using simple binary division
|
||||
// divr16u(word zp($29) dividend, word zp($12) rem)
|
||||
// divr16u(word zp($10) dividend, word zp($1d) rem)
|
||||
divr16u: {
|
||||
.label rem = $12
|
||||
.label dividend = $29
|
||||
.label quotient = $14
|
||||
.label return = $14
|
||||
.label rem = $1d
|
||||
.label dividend = $10
|
||||
.label quotient = $12
|
||||
.label return = $12
|
||||
ldx #0
|
||||
txa
|
||||
sta.z quotient
|
||||
@ -881,11 +878,11 @@ divr16u: {
|
||||
rts
|
||||
}
|
||||
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
|
||||
// memset(void* zp($16) str, byte register(X) c)
|
||||
// memset(void* zp($14) str, byte register(X) c)
|
||||
memset: {
|
||||
.label end = $29
|
||||
.label dst = $16
|
||||
.label str = $16
|
||||
.label end = $27
|
||||
.label dst = $14
|
||||
.label str = $14
|
||||
// end = (char*)str + num
|
||||
lda.z str
|
||||
clc
|
||||
@ -918,8 +915,9 @@ memset: {
|
||||
}
|
||||
.align $100
|
||||
xsin: .fill 2*XSIN_SIZE, 0
|
||||
.pc = LOGO "LOGO"
|
||||
.var logoPic = LoadPicture("logo.png", List().add($444444, $808080, $000000, $ffffff))
|
||||
.pc = $2000 "LOGO"
|
||||
LOGO:
|
||||
.var logoPic = LoadPicture("logo.png", List().add($444444, $808080, $000000, $ffffff))
|
||||
.for (var y=0; y<6 ; y++)
|
||||
.for (var x=0;x<40; x++)
|
||||
.for(var cp=0; cp<8; cp++)
|
||||
|
@ -21,8 +21,6 @@
|
||||
// Color Ram
|
||||
.label COLS = $d800
|
||||
.label SCREEN = $400
|
||||
.label LOGO = $2000
|
||||
// kickasm
|
||||
main: {
|
||||
.const toD0181_return = (>(SCREEN&$3fff)*4)|(>LOGO)/4&$f
|
||||
// VICII->BORDER_COLOR = WHITE
|
||||
@ -108,8 +106,9 @@ memset: {
|
||||
!:
|
||||
jmp __b2
|
||||
}
|
||||
.pc = LOGO "LOGO"
|
||||
.var logoPic = LoadPicture("logo.png", List().add($444444, $808080, $000000, $ffffff))
|
||||
.pc = $2000 "LOGO"
|
||||
LOGO:
|
||||
.var logoPic = LoadPicture("logo.png", List().add($444444, $808080, $000000, $ffffff))
|
||||
.for (var y=0; y<6 ; y++)
|
||||
.for (var x=0;x<40; x++)
|
||||
.for(var cp=0; cp<8; cp++)
|
||||
|
Loading…
x
Reference in New Issue
Block a user