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

Added support for header-files found in include-folders and C-files found in library-folders. The compiler auto-searches for the C-file in the defined lib search path whenever a H-file is included. Closes #387

This commit is contained in:
jespergravgaard 2020-04-11 21:15:34 +02:00
parent c150976b2f
commit a378e831e2
213 changed files with 1974 additions and 877 deletions

View File

@ -15,7 +15,8 @@ release:
paths:
- ./kickc/bin
- ./kickc/lib
- ./kickc/stdlib
- ./kickc/include
- ./kickc/lib
- ./kickc/fragment
- ./kickc/examples
- ./kickc/LICENSE*

View File

@ -12,8 +12,16 @@
</dependencySets>
<fileSets>
<fileSet>
<directory>src/main/kc/stdlib</directory>
<outputDirectory>stdlib</outputDirectory>
<directory>src/main/kc/include</directory>
<outputDirectory>include</outputDirectory>
<includes>
<include>*.c</include>
<include>*.h</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/kc/lib</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.c</include>
<include>*.h</include>

View File

@ -8,12 +8,12 @@ import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.SymbolRef;
import dk.camelot64.kickc.parser.CParser;
import dk.camelot64.kickc.parser.KickCLexer;
import dk.camelot64.kickc.parser.KickCParser;
import dk.camelot64.kickc.passes.*;
import dk.camelot64.kickc.preprocessor.CPreprocessor;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenSource;
import java.io.File;
import java.nio.file.Path;
@ -140,16 +140,20 @@ public class Compiler {
return program.getLog();
}
public void addImportPath(String path) {
program.getImportPaths().add(path);
public void addIncludePath(String path) {
program.getIncludePaths().add(path);
}
public void addLibraryPath(String path) {
program.getLibraryPaths().add(path);
}
public void preprocess(List<Path> cFiles) {
Path currentPath = new File(".").toPath();
CParser cParser = new CParser(program);
for(Path cFile : cFiles) {
final KickCLexer fileLexer = cParser.loadCFile(cFile.toString(), currentPath);
cParser.addSourceLast(fileLexer);
final TokenSource cFileTokens = cParser.loadCFile(cFile.toString(), currentPath, program.getIncludePaths(), false);
cParser.addSourceLast(cFileTokens);
}
final CPreprocessor preprocessor = cParser.getPreprocessor();
Token token = preprocessor.nextToken();
@ -175,9 +179,9 @@ public class Compiler {
}
program.setStatementSequence(new StatementSequence());
CParser cParser = new CParser(program);
for(Path file : cFiles) {
final KickCLexer fileLexer = cParser.loadCFile(file.toString(), currentPath);
cParser.addSourceLast(fileLexer);
for(Path cFile : cFiles) {
final TokenSource cFileTokens = cParser.loadCFile(cFile.toString(), currentPath, program.getIncludePaths(), false);
cParser.addSourceLast(cFileTokens);
}
// Parse the files

View File

@ -40,7 +40,10 @@ public class KickC implements Callable<Void> {
@CommandLine.Parameters(index = "0", arity = "0..n", description = "The C source files to compile.")
private List<Path> cFiles = null;
@CommandLine.Option(names = {"-I", "-libdir"}, description = "Path to a library folder, where the compiler looks for included files. This option can be repeated to add multiple library folders.")
@CommandLine.Option(names = {"-I", "-includedir"}, description = "Path to an include folder, where the compiler looks for included files. This option can be repeated to add multiple include folders.")
private List<Path> includeDir = null;
@CommandLine.Option(names = {"-L", "-libdir"}, description = "Path to a library folder, where the compiler looks for library files. This option can be repeated to add multiple library folders.")
private List<Path> libDir = null;
@CommandLine.Option(names = {"-F", "-fragmentdir"}, description = "Path to the ASM fragment folder, where the compiler looks for ASM fragments.")
@ -205,9 +208,15 @@ public class KickC implements Callable<Void> {
compiler.setTargetCpu(targetCpu);
}
if(includeDir != null) {
for(Path includePath : includeDir) {
compiler.addIncludePath(includePath.toString());
}
}
if(libDir != null) {
for(Path libPath : libDir) {
compiler.addImportPath(libPath.toString());
compiler.addLibraryPath(libPath.toString());
}
}

View File

@ -16,16 +16,23 @@ import java.util.List;
*/
public class SourceLoader {
public static File loadFile(String fileName, Path currentPath, Program program) {
List<String> searchPaths = new ArrayList<>();
/**
* Locate a file and load it. Looks through the current path and a set of search folder.
* @param fileName The file to look for.
* @param currentPath The current path. May be null.
* @param searchPaths The search paths to look through if the file is not found in the current path.
* @return The file if found. null if not.
*/
public static File loadFile(String fileName, Path currentPath, List<String> searchPaths) {
List<String> allSearchPaths = new ArrayList<>();
if(currentPath != null)
searchPaths.add(currentPath.toString());
searchPaths.addAll(program.getImportPaths());
for(String importPath : searchPaths) {
if(!importPath.endsWith("/")) {
importPath += "/";
allSearchPaths.add(currentPath.toString());
allSearchPaths.addAll(searchPaths);
for(String searchPath : allSearchPaths) {
if(!searchPath.endsWith("/")) {
searchPath += "/";
}
String filePath = importPath + fileName;
String filePath = searchPath + fileName;
//System.out.println("Looking for file "+filePath);
File file = new File(filePath);
if(file.exists()) {
@ -33,12 +40,15 @@ public class SourceLoader {
return file;
}
}
throw new CompileError("File not found " + fileName);
// Not found
return null;
}
public static void loadLinkScriptFile(String fileName, Path currentPath, Program program) {
try {
File file = loadFile(fileName, currentPath, program);
File file = loadFile(fileName, currentPath, program.getIncludePaths());
if(file==null)
throw new CompileError("File not found " + fileName);
Path filePath = file.toPath();
String linkScript = new String(Files.readAllBytes(filePath));
program.setLinkScript(filePath, linkScript);

View File

@ -21,10 +21,12 @@ public class Program {
/** The name of the primary file being compiled. PASS 0-5 (STATIC) */
private String primaryFileName;
/** Paths used for importing files. PASS 0 (STATIC) */
private List<String> importPaths;
/** Imported files. PASS 0 (STATIC) */
private List<String> imported;
/** Paths used for including files. PASS 0 (STATIC) */
private List<String> includePaths;
/** Paths used for library files. PASS 0 (STATIC) */
private List<String> libraryPaths;
/** All loaded H/C-files. PASS 0 (STATIC) */
private List<String> loadedFiles;
/** The target platform that the program is being build for. PASS 0-5 (STATIC) */
private TargetPlatform targetPlatform = TargetPlatform.DEFAULT;
@ -106,8 +108,9 @@ public class Program {
public Program() {
this.scope = new ProgramScope();
this.log = new CompileLog();
this.importPaths = new ArrayList<>();
this.imported = new ArrayList<>();
this.includePaths = new ArrayList<>();
this.libraryPaths = new ArrayList<>();
this.loadedFiles = new ArrayList<>();
this.asmResourceFiles = new ArrayList<>();
this.reservedZps = new ArrayList<>();
}
@ -116,8 +119,9 @@ public class Program {
* Clears all data that is only used in PASS 1
*/
public void endPass1() {
this.importPaths = null;
this.imported = null;
this.includePaths = null;
this.libraryPaths = null;
this.loadedFiles = null;
this.statementSequence = null;
this.procedureModifiedVars = null;
this.structVariableMemberUnwinding = null;
@ -249,12 +253,16 @@ public class Program {
this.fileComments = fileComments;
}
public List<String> getImportPaths() {
return importPaths;
public List<String> getIncludePaths() {
return includePaths;
}
public List<String> getImported() {
return imported;
public List<String> getLibraryPaths() {
return libraryPaths;
}
public List<String> getLoadedFiles() {
return loadedFiles;
}
public List<Path> getAsmResourceFiles() {

View File

@ -1,5 +1,6 @@
package dk.camelot64.kickc.parser;
import dk.camelot64.kickc.Compiler;
import dk.camelot64.kickc.SourceLoader;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.Program;
@ -93,6 +94,7 @@ public class CParser {
/**
* Get the preprocessor (usable for getting all preprocessed tokens).
*
* @return The preprocessor
*/
public CPreprocessor getPreprocessor() {
@ -147,15 +149,24 @@ public class CParser {
/**
* Loads a C-file (if it has not already been loaded).
*
* <p>
* The C-file is inserted into the C token stream at the current parse-point - so the parser will parse the entire content of the file before moving on.
*
* @param fileName The file name of the file
*/
public void includeCFile(String fileName, boolean isSystem) {
if(fileName.startsWith("\"") || fileName.startsWith("<")) {
fileName = fileName.substring(1, fileName.length() - 1);
}
final Path currentSourceFolderPath = isSystem ? null : getCurrentSourceFolderPath();
final KickCLexer lexer = loadCFile(fileName, currentSourceFolderPath);
addSourceFirst(lexer);
final TokenSource fileTokens = loadCFile(fileName, currentSourceFolderPath, program.getIncludePaths(), false);
if(fileName.endsWith(".h")) {
// The included file was a H-file - attempt to find the matching library C-file
String libFileName = Compiler.removeFileNameExtension(fileName) + ".c";
final TokenSource cLibFileTokens = loadCFile(libFileName, currentSourceFolderPath, program.getLibraryPaths(), true);
addSourceFirst(cLibFileTokens);
}
addSourceFirst(fileTokens);
}
/**
@ -163,20 +174,23 @@ public class CParser {
*
* @param fileName The file name of the file
* @param currentPath The path of the current folder (searched before the search path).
* @return The lexer to be inserted into the source-list using one of the {@link #addSourceFirst(KickCLexer)} / {@link #addSourceLast(KickCLexer)} methods.
* @param searchPaths The folders to look in if the files is not found in current path
* @return The lexer tokens to be inserted into the source-list using one of the {@link #addSourceFirst(TokenSource)} / {@link #addSourceLast(TokenSource)} methods.
*/
public KickCLexer loadCFile(String fileName, Path currentPath) {
public TokenSource loadCFile(String fileName, Path currentPath, List<String> searchPaths, boolean acceptFileNotFound) {
try {
if(fileName.startsWith("\"") || fileName.startsWith("<")) {
fileName = fileName.substring(1, fileName.length() - 1);
}
File file = SourceLoader.loadFile(fileName, currentPath, program);
List<String> imported = program.getImported();
if(imported.contains(file.getAbsolutePath())) {
File file = SourceLoader.loadFile(fileName, currentPath, searchPaths);
if(file == null)
if(acceptFileNotFound)
return null;
else
throw new CompileError("File not found " + fileName);
List<String> included = program.getLoadedFiles();
if(included.contains(file.getAbsolutePath())) {
return null;
}
final CharStream fileStream = CharStreams.fromPath(file.toPath().toAbsolutePath());
imported.add(file.getAbsolutePath());
included.add(file.getAbsolutePath());
if(program.getLog().isVerboseParse()) {
program.getLog().append("PARSING " + file.getPath().replace("\\", "/"));
program.getLog().append(fileStream.toString());
@ -217,21 +231,21 @@ public class CParser {
/**
* Add source code at the start of the token stream being parsed.
*
* @param lexer The lexer for reading the source tokens
* @param tokenSource The lexer for reading the source tokens
*/
public void addSourceFirst(KickCLexer lexer) {
if(lexer != null)
cTokenSource.addSourceFirst(lexer);
public void addSourceFirst(TokenSource tokenSource) {
if(tokenSource != null)
cTokenSource.addSourceFirst(tokenSource);
}
/**
* Add source code at the end of the token stream being parsed.
*
* @param lexer The lexer for reading the source tokens
* @param tokenSource The lexer for reading the source tokens
*/
public void addSourceLast(KickCLexer lexer) {
if(lexer != null)
cTokenSource.addSourceLast(lexer);
public void addSourceLast(TokenSource tokenSource) {
if(tokenSource != null)
cTokenSource.addSourceLast(tokenSource);
}

View File

@ -255,7 +255,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
if(existingSymbol!=null) {
// Already declared - check equality
if(!SymbolTypeConversion.procedureDeclarationMatch((Procedure) existingSymbol, procedure))
throw new CompileError("Error! Conflicting declarations for: "+procedure.getFullName(), StatementSource.procedureBegin(ctx));
throw new CompileError("Error! Conflicting declarations for: "+procedure.getFullName(), new StatementSource(ctx));
} else {
// Not declared before - add it
program.getScope().add(procedure);
@ -527,7 +527,9 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
String resourceName = resource.getText();
resourceName = resourceName.substring(1, resourceName.length() - 1);
Path currentPath = cParser.getSourceFolderPath(ctx);
File resourceFile = SourceLoader.loadFile(resourceName, currentPath, program);
File resourceFile = SourceLoader.loadFile(resourceName, currentPath, new ArrayList<>());
if(resourceFile==null)
throw new CompileError("File not found " + resourceName);
program.addAsmResourceFile(resourceFile.toPath());
if(program.getLog().isVerboseParse()) {
program.getLog().append("Added resource " + resourceFile.getPath().replace('\\', '/'));
@ -873,28 +875,34 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
final List<Directive> effectiveDirectives = varDecl.getEffectiveDirectives();
final List<Comment> declComments = varDecl.getDeclComments();
varDecl.exitVar();
if(isStructMember && (initializer != null))
throw new CompileError("Initializer not supported inside structs " + effectiveType.getTypeName(), statementSource);
if(initializer != null)
PrePostModifierHandler.addPreModifiers(this, initializer, statementSource);
RValue initValue = (initializer == null) ? null : (RValue) visit(initializer);
initValue = Initializers.constantify(initValue, new Initializers.ValueTypeSpec(effectiveType, effectiveArraySpec), program, statementSource);
VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, effectiveType, effectiveArraySpec, effectiveDirectives, currentDataSegment, variableBuilderConfig);
Variable variable = varBuilder.build();
boolean isPermanent = ScopeRef.ROOT.equals(variable.getScope().getRef()) || variable.isPermanent();
if(variable.isKindConstant() || (isPermanent && variable.isKindLoadStore() && Variable.MemoryArea.MAIN_MEMORY.equals(variable.getMemoryArea()) && initValue instanceof ConstantValue && !isStructMember && variable.getRegister() == null)) {
// Set initial value
ConstantValue constInitValue = getConstInitValue(initValue, initializer, statementSource);
variable.setInitValue(constInitValue);
// Add comments to constant
variable.setComments(ensureUnusedComments(declComments));
} else if(!variable.isKindConstant() && !isStructMember) {
Statement initStmt = new StatementAssignment(variable.getVariableRef(), initValue, true, statementSource, ensureUnusedComments(declComments));
sequence.addStatement(initStmt);
if(isStructMember && (initializer != null))
throw new CompileError("Initializer not supported inside structs " + effectiveType.getTypeName(), statementSource);
if(variable.isDeclarationOnly()) {
if(initializer != null) {
throw new CompileError("Initializer not allowed for extern variables " + varName, statementSource);
}
} else {
// Create a proper initializer
if(initializer != null)
PrePostModifierHandler.addPreModifiers(this, initializer, statementSource);
RValue initValue = (initializer == null) ? null : (RValue) visit(initializer);
initValue = Initializers.constantify(initValue, new Initializers.ValueTypeSpec(effectiveType, effectiveArraySpec), program, statementSource);
boolean isPermanent = ScopeRef.ROOT.equals(variable.getScope().getRef()) || variable.isPermanent();
if(variable.isKindConstant() || (isPermanent && variable.isKindLoadStore() && Variable.MemoryArea.MAIN_MEMORY.equals(variable.getMemoryArea()) && initValue instanceof ConstantValue && !isStructMember && variable.getRegister() == null)) {
// Set initial value
ConstantValue constInitValue = getConstInitValue(initValue, initializer, statementSource);
variable.setInitValue(constInitValue);
// Add comments to constant
variable.setComments(ensureUnusedComments(declComments));
} else if(!variable.isKindConstant() && !isStructMember) {
Statement initStmt = new StatementAssignment(variable.getVariableRef(), initValue, true, statementSource, ensureUnusedComments(declComments));
sequence.addStatement(initStmt);
}
if(initializer != null)
PrePostModifierHandler.addPostModifiers(this, initializer, statementSource);
}
if(initializer != null)
PrePostModifierHandler.addPostModifiers(this, initializer, statementSource);
} catch(CompileError e) {
throw new CompileError(e.getMessage(), declSource);
}

View File

@ -0,0 +1,12 @@
// Find atan2(x, y) using the CORDIC method
// See http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf
// Find the atan2(x, y) - which is the angle of the line from (0,0) to (x,y)
// Finding the angle requires a binary search using CORDIC_ITERATIONS_16
// Returns the angle in hex-degrees (0=0, 0x8000=PI, 0x10000=2*PI)
word atan2_16(signed word x, signed word y);
// Find the atan2(x, y) - which is the angle of the line from (0,0) to (x,y)
// Finding the angle requires a binary search using CORDIC_ITERATIONS_8
// Returns the angle in hex-degrees (0=0, 0x80=PI, 0x100=2*PI)
byte atan2_8(signed byte x, signed byte y);

View File

@ -0,0 +1,128 @@
// Library wrapping the BASIC floating point functions
// See https://www.c64-wiki.com/wiki/Floating_point_arithmetic
// See http://www.pagetable.com/c64rom/c64rom_sc.html
// Prepare MEM pointers for operations using MEM
inline void prepareMEM(unsigned int mem);
// FAC = word
// Set the FAC (floating point accumulator) to the integer value of a 16bit word
void setFAC(unsigned int w);
// word = FAC
// Get the value of the FAC (floating point accumulator) as an integer 16bit word
// Destroys the value in the FAC in the process
unsigned int getFAC();
// ARG = FAC
// Set the ARG (floating point argument) to the value of the FAC (floating point accumulator)
void setARGtoFAC();
// FAC = ARG
// Set the FAC (floating point accumulator) to the value of the ARG (floating point argument)
void setFACtoARG();
// MEM = FAC
// Stores the value of the FAC to memory
// Stores 5 bytes (means it is necessary to allocate 5 bytes to avoid clobbering other data using eg. byte[] mem = {0, 0, 0, 0, 0};)
void setMEMtoFAC(char* mem);
// FAC = MEM
// Set the FAC to value from MEM (float saved in memory)
// Reads 5 bytes
void setFACtoMEM(char* mem);
// FAC = PI/2
// Set the FAC to PI/2
// Reads 5 bytes from the BASIC ROM
void setFACtoPIhalf();
// FAC = 2*PI
// Set the FAC to 2*PI
// Reads 5 bytes from the BASIC ROM
void setFACto2PI();
// ARG = MEM
// Load the ARG from memory
// Reads 5 bytes
void setARGtoMEM(char* mem);
// FAC = MEM+FAC
// Set FAC to MEM (float saved in memory) plus FAC (float accumulator)
// Reads 5 bytes from memory
void addMEMtoFAC(char* mem);
// FAC = ARG+FAC
// Add ARG (floating point argument) to FAC (floating point accumulator)
void addARGtoFAC();
// FAC = MEM-FAC
// Set FAC to MEM (float saved in memory) minus FAC (float accumulator)
// Reads 5 bytes from memory
void subFACfromMEM(char* mem);
// FAC = ARG-FAC
// Set FAC to ARG minus FAC
void subFACfromARG();
// FAC = MEM/FAC
// Set FAC to MEM (float saved in memory) divided by FAC (float accumulator)
// Reads 5 bytes from memory
void divMEMbyFAC(char* mem);
// FAC = MEM*FAC
// Set FAC to MEM (float saved in memory) multiplied by FAC (float accumulator)
// Reads 5 bytes from memory
void mulFACbyMEM(char* mem);
// FAC = MEM^FAC
// Set FAC to MEM (float saved in memory) raised to power of FAC (float accumulator)
// Reads 5 bytes from memory
void pwrMEMbyFAC(char* mem);
// FAC = int(FAC)
// Set FAC to integer part of the FAC - int(FAC)
// The integer part is defined as the next lower integer - like java floor()
void intFAC();
// FAC = sin(FAC)
// Set FAC to sinus of the FAC - sin(FAC)
// Sinus is calculated on radians (0-2*PI)
void sinFAC();
// FAC = cos(FAC)
// Set FAC to cosinus of the FAC - cos(FAC)
// Cosinus is calculated on radians (0-2*PI)
void cosFAC();
// FAC = tan(FAC)
// Set FAC to the tangens of FAC - tan(FAC)
// Tangens is calculated on radians (0-2*PI)
void tanFAC();
// FAC = atn(FAC)
// Set FAC to the arc tangens of FAC - atn(FAC)
// Arc Tangens is calculated on radians (0-2*PI)
void atnFAC();
// FAC = sqr(FAC)
// Set FAC to the square root of FAC - sqr(FAC)
void sqrFAC();
// FAC = exp(FAC)
// Set FAC to the exponential function of FAC - exp(FAC)
// Exp is based on the natural logarithm e=2.71828183
void expFAC();
// FAC = log(FAC)
// Set FAC to the logarithm of FAC - log(FAC)
// Log is based on the natural logarithm e=2.71828183
void logFAC();
// FAC = FAC/10
// Set FAC to FAC divided by 10
void divFACby10();
// FAC = FAC*10
// Set FAC to FAC multiplied by 10
void mulFACby10();

View File

@ -0,0 +1,13 @@
// Plot and line drawing routines for HIRES bitmaps
// Currently it can only plot on the first 256 x-positions.
// Initialize the bitmap plotter tables for a specific bitmap
void bitmap_init(byte* bitmap);
// Clear all graphics on the bitmap
void bitmap_clear();
void bitmap_plot(byte x, byte y);
// Draw a line on the bitmap
void bitmap_line(byte x0, byte x1, byte y0, byte y1);

View File

@ -0,0 +1,23 @@
// Simple single-color (320x200) bitmap routines
#include <string.h>
// Initialize bitmap plotting tables
void bitmap_init(byte* gfx, byte* screen);
// Clear all graphics on the bitmap
// bgcol - the background color to fill the screen with
// fgcol - the foreground color to fill the screen with
void bitmap_clear(byte bgcol, byte fgcol);
// Plot a single dot in the bitmap
void bitmap_plot(word x, byte y);
// Draw a line on the bitmap using bresenhams algorithm
void bitmap_line(word x1, word y1, word x2, word y2);
// Get the absolute value of a 16-bit unsigned number treated as a signed number.
word abs_u16(word w);
// Get the sign of a 16-bit unsigned number treated as a signed number.
// Returns unsigned -1 if the number is
word sgn_u16(word w);

224
src/main/kc/include/c64.h Normal file
View File

@ -0,0 +1,224 @@
// Commodore 64 Registers and Constants
// Processor port data direction register
char* const PROCPORT_DDR = $00;
// Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written
const char PROCPORT_DDR_MEMORY_MASK = %00000111;
// Processor Port Register controlling RAM/ROM configuration and the datasette
char* const PROCPORT = $01;
// RAM in all three areas $A000, $D000, $E000
const char PROCPORT_RAM_ALL = %00000000;
// RAM in $A000, $E000 I/O in $D000
const char PROCPORT_RAM_IO = %00000101;
// RAM in $A000, $E000 CHAR ROM in $D000
const char PROCPORT_RAM_CHARROM = %00000001;
// RAM in $A000, I/O in $D000, KERNEL in $E000
const char PROCPORT_KERNEL_IO = %00000110;
// BASIC in $A000, I/O in $D000, KERNEL in $E000
const char PROCPORT_BASIC_KERNEL_IO = %00000111;
// The address of the CHARGEN character set
char* const CHARGEN = $d000;
// Positions of the border (in sprite positions)
const char BORDER_XPOS_LEFT=24;
const unsigned int BORDER_XPOS_RIGHT=344;
const char BORDER_YPOS_TOP=50;
const char BORDER_YPOS_BOTTOM=250;
// The offset of the sprite pointers from the screen start address
const unsigned int SPRITE_PTRS = $3f8;
char* const SPRITES_XPOS = $d000;
char* const SPRITES_YPOS = $d001;
char* const SPRITES_XMSB = $d010;
char* const RASTER = $d012;
char* const SPRITES_ENABLE = $d015;
char* const SPRITES_EXPAND_Y = $d017;
char* const SPRITES_PRIORITY = $d01b;
char* const SPRITES_MC = $d01c;
char* const SPRITES_EXPAND_X = $d01d;
char* const BORDERCOL = $d020;
char* const BGCOL = $d021;
char* const BGCOL1 = $d021;
char* const BGCOL2 = $d022;
char* const BGCOL3 = $d023;
char* const BGCOL4 = $d024;
char* const SPRITES_MC1 = $d025;
char* const SPRITES_MC2 = $d026;
char* const SPRITES_COLS = $d027;
char* const VIC_CONTROL = $d011;
char* const D011 = $d011;
const char VIC_RST8 = %10000000;
const char VIC_ECM = %01000000;
const char VIC_BMM = %00100000;
const char VIC_DEN = %00010000;
const char VIC_RSEL = %00001000;
char* const VIC_CONTROL2 = $d016;
char* const D016 = $d016;
const char VIC_MCM = %00010000;
const char VIC_CSEL = %00001000;
char* const D018 = $d018;
char* const VIC_MEMORY = $d018;
char* const LIGHTPEN_X = $d013;
char* const LIGHTPEN_Y = $d014;
// VIC II IRQ Status Register
char* const IRQ_STATUS = $d019;
// VIC II IRQ Enable Register
char* const IRQ_ENABLE = $d01a;
// Bits for the IRQ Status/Enable Registers
const char IRQ_RASTER = %00000001;
const char IRQ_COLLISION_BG = %00000010;
const char IRQ_COLLISION_SPRITE = %00000100;
const char IRQ_LIGHTPEN = %00001000;
// Color Ram
char* const COLS = $d800;
// CIA#1 Port A: keyboard matrix columns and joystick #2
char* const CIA1_PORT_A = $dc00;
// CIA#1 Port B: keyboard matrix rows and joystick #1.
char* const CIA1_PORT_B = $dc01;
// CIA #1 Port A data direction register.
char* const CIA1_PORT_A_DDR = $dc02;
// CIA #1 Port B data direction register.
char* const CIA1_PORT_B_DDR = $dc03;
// CIA #1 Timer A Value
unsigned int* const CIA1_TIMER_A = $dc04;
// CIA #1 Timer B Value
unsigned int* const CIA1_TIMER_B = $dc06;
// CIA #1 Time-of-day real-time-clock tenth seconds (BCD)
char* const CIA1_TOD_10THS = $dc08;
// CIA #1 Time-of-day real-time-clock seconds (BCD)
char* const CIA1_TOD_SEC = $dc09;
// CIA #1 Time-of-day real-time-clock minutes (BCD)
char* const CIA1_TOD_MIN = $dc0a;
// CIA #1 Time-of-day real-time-clock hours (BCD)
char* const CIA1_TOD_HOURS = $dc0b;
// CIA #1 Serial Shift Register
char* const CIA1_SERIAL_SHIFT_OUT = $dc0c;
// CIA#1 Interrupt Status & Control Register
char* const CIA1_INTERRUPT = $dc0d;
// CIA#1 Timer A Control Register
char* const CIA1_TIMER_A_CONTROL = $dc0e;
// CIA#1 Timer B Control Register
char* const CIA1_TIMER_B_CONTROL = $dc0f;
// CIA#2 Port A: Serial bus, RS-232, VIC memory bank
char* const CIA2_PORT_A = $dd00;
// CIA#2 Port B: RS-232
char* const CIA2_PORT_B = $dd01;
// CIA #2 Port A data direction register.
char* const CIA2_PORT_A_DDR = $dd02;
// CIA #2 Port B data direction register.
char* const CIA2_PORT_B_DDR = $dd03;
// CIA #2 Timer A+B Value (32-bit)
unsigned long* const CIA2_TIMER_AB = $dd04;
// CIA #2 Timer A Value (16-bit)
unsigned int* const CIA2_TIMER_A = $dd04;
// CIA #2 Timer B Value (16-bit)
unsigned int* const CIA2_TIMER_B = $dd06;
// CIA #2 Time-of-day real-time-clock tenth seconds (BCD)
char* const CIA2_TOD_10THS = $dd08;
// CIA #2 Time-of-day real-time-clock seconds (BCD)
char* const CIA2_TOD_SEC = $dd09;
// CIA #2 Time-of-day real-time-clock minutes (BCD)
char* const CIA2_TOD_MIN = $dd0a;
// CIA #2 Time-of-day real-time-clock hours (BCD)
char* const CIA2_TOD_HOURS = $dd0b;
// CIA #2 Serial Shift Register
char* const CIA2_SERIAL_SHIFT_OUT = $dd0c;
// CIA #2 Interrupt Status & Control Register
char* const CIA2_INTERRUPT = $dd0d;
// CIA #2 Timer A Control Register
char* const CIA2_TIMER_A_CONTROL = $dd0e;
// CIA #2 Timer B Control Register
char* const CIA2_TIMER_B_CONTROL = $dd0f;
// Value that disables all CIA interrupts when stored to the CIA Interrupt registers
const char CIA_INTERRUPT_CLEAR = $7f;
// Timer Control - Start/stop timer (0:stop, 1: start)
const char CIA_TIMER_CONTROL_STOP = 0b00000000;
// Timer Control - Start/stop timer (0:stop, 1: start)
const char CIA_TIMER_CONTROL_START = 0b00000001;
// Timer Control - Time CONTINUOUS/ONE-SHOT (0:CONTINUOUS, 1: ONE-SHOT)
const char CIA_TIMER_CONTROL_CONTINUOUS = 0b00000000;
// Timer Control - Time CONTINUOUS/ONE-SHOT (0:CONTINUOUS, 1: ONE-SHOT)
const char CIA_TIMER_CONTROL_ONESHOT = 0b00001000;
// Timer A Control - Timer counts (0:system cycles, 1: CNT pulses)
const char CIA_TIMER_CONTROL_A_COUNT_CYCLES = 0b00000000;
// Timer A Control - Timer counts (0:system cycles, 1: CNT pulses)
const char CIA_TIMER_CONTROL_A_COUNT_CNT = 0b00100000;
// Timer A Control - Serial Port Mode (0: Serial Port Input, 1: Serial Port Output)
const char CIA_TIMER_CONTROL_A_SERIAL_IN = 0b00000000;
// Timer A Control - Serial Port Mode (0: Serial Port Input, 1: Serial Port Output)
const char CIA_TIMER_CONTROL_A_SERIAL_OUT = 0b01000000;
// Timer A Control - time-of-day clock Mode (0: 60Hz, 1: 50Hz)
const char CIA_TIMER_CONTROL_A_TOD_60HZ = 0b00000000;
// Timer A Control - time-of-day clock Mode (0: 60Hz, 1: 50Hz)
const char CIA_TIMER_CONTROL_A_TOD_50HZ = 0b10000000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const char CIA_TIMER_CONTROL_B_COUNT_CYCLES = 0b00000000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const char CIA_TIMER_CONTROL_B_COUNT_CNT = 0b00100000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const char CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A = 0b01000000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const char CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A_CNT = 0b01100000;
// Timer B Control - time-of-day write mode (0: TOD clock, 1: TOD alarm)
const char CIA_TIMER_CONTROL_B_TOD_CLOCK_SET = 0b00000000;
// Timer B Control - time-of-day write mode (0: TOD clock, 1: TOD alarm)
const char CIA_TIMER_CONTROL_B_TOD_ALARM_SET = 0b10000000;
// The vector used when the KERNAL serves IRQ interrupts
void()** const KERNEL_IRQ = $0314;
// The vector used when the KERNAL serves NMI interrupts
void()** const KERNEL_NMI = $0318;
// The vector used when the HARDWARE serves IRQ interrupts
void()** const HARDWARE_IRQ = $fffe;
// The SID volume
char* const SID_VOLUME = $d418;
// The colors of the C64
const char BLACK = $0;
const char WHITE = $1;
const char RED = $2;
const char CYAN = $3;
const char PURPLE = $4;
const char GREEN = $5;
const char BLUE = $6;
const char YELLOW = $7;
const char ORANGE = $8;
const char BROWN = $9;
const char PINK = $a;
const char DARK_GREY= $b;
const char GREY = $c;
const char LIGHT_GREEN = $d;
const char LIGHT_BLUE = $e;
const char LIGHT_GREY = $f;
// Get the value to store into D018 to display a specific screen and charset/bitmap
// Optimized for ASM from (char)((((unsigned int)screen&0x3fff)/$40)|(((unsigned int)charset&$3fff)/$400));
inline char toD018(char* screen, char* gfx);
// Get the value to store into DD00 (CIA 2 port A) to choose a specific VIC bank
// Optimized for ASM from %00000011 ^ (char)((unsigned int)gfx/$4000)
inline char toDd00(char* gfx);
// Get the sprite pointer for a sprite.
// The sprite pointer is the index of the sprite within the graphics bank and equal to the sprite (char)(sprite_addr/64)
// The sprite pointers are stored SCREEN+$3f8+sprite_id to set the pointer of each sprite
inline char toSpritePtr(char* sprite);
// Select a specific VIC graphics bank by setting the CIA 2 port A ($dd00) as needed
inline void vicSelectGfxBank(char* gfx);

View File

@ -4,7 +4,8 @@
// (J) https://www.c64-wiki.com/wiki/C64DTV_Programming_Guide
// (H) http://dtvhacking.cbm8bit.com/dtv_wiki/images/d/d9/Dtv_registers_full.txt
#include <c64.c>
#include <c64dtv.h>
#include <c64.h>
// Feature enables or disables the extra C64 DTV features
byte* const DTV_FEATURE = $d03f;
@ -63,19 +64,7 @@ byte* const DTV_GRAPHICS_HICOL_BANK = $d03e;
// Set the memory pointed to by CPU BANK 1 SEGMENT ($4000-$7fff)
// This sets which actual memory is addressed when the CPU reads/writes to $4000-$7fff
// The actual memory addressed will be $4000*cpuSegmentIdx
void dtvSetCpuBankSegment1(byte cpuBankIdx) {
// Move CPU BANK 1 SEGMENT ($4000-$7fff)
byte* cpuBank = $ff;
*cpuBank = cpuBankIdx;
asm {
// SAC $dd - A register points to 13 BANK 1 segment
.byte $32, $dd
// LDA $ff - Set CPU BANK 1 SEGMENT ($4000-$7fff) to ($ff)*$4000
lda $ff
// SAC $00 - A register points to 0 ACCUMULATOR
.byte $32, $00
}
}
void dtvSetCpuBankSegment1(byte cpuBankIdx);
// Blitter Source A Start
byte* const DTV_BLITTER_SRCA_LO = $d320;

View File

@ -0,0 +1,66 @@
// Simple binary division implementation
// Follows the C99 standard by truncating toward zero on negative results.
// See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf section 6.5.5
// Remainder after signed 8 bit division
extern char rem8u;
// Performs division on two 8 bit unsigned bytes
// Returns dividend/divisor.
// The remainder will be set into the global variable rem8u
// Implemented using simple binary division
char div8u(char dividend, char divisor);
// Performs division on two 8 bit unsigned bytes and an initial remainder
// Returns dividend/divisor.
// The final remainder will be set into the global variable rem8u
// Implemented using simple binary division
char divr8u(char dividend, char divisor, char rem);
// Remainder after unsigned 16-bit division
extern unsigned int rem16u;
// Performs division on two 16 bit unsigned words and an initial remainder
// Returns the quotient dividend/divisor.
// The final remainder will be set into the global variable rem16u
// Implemented using simple binary division
unsigned int divr16u(unsigned int dividend, unsigned int divisor, unsigned int rem);
// Performs division on two 16 bit unsigned words
// Returns the quotient dividend/divisor.
// The remainder will be set into the global variable rem16u
// Implemented using simple binary division
unsigned int div16u(unsigned int dividend, unsigned int divisor);
// Divide unsigned 32-bit dword dividend with a 16-bit word divisor
// The 16-bit word remainder can be found in rem16u after the division
unsigned long div32u16u(unsigned long dividend, unsigned int divisor);
// Remainder after signed 8 bit division
extern signed char rem8s;
// Perform division on two signed 8-bit numbers
// Returns dividend/divisor.
// The remainder will be set into the global variable rem8s.
// Implemented using simple binary division
// Follows the C99 standard by truncating toward zero on negative results.
// See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf section 6.5.5
signed char div8s(signed char dividend, signed char divisor);
// Remainder after signed 16 bit division
extern int rem16s;
// Perform division on two signed 16-bit numbers with an initial remainder.
// Returns dividend/divisor. The remainder will be set into the global variable rem16s.
// Implemented using simple binary division
// Follows the C99 standard by truncating toward zero on negative results.
// See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf section 6.5.5
int divr16s(int dividend, int divisor, int rem);
// Perform division on two signed 16-bit numbers
// Returns dividend/divisor.
// The remainder will be set into the global variable rem16s.
// Implemented using simple binary division
// Follows the C99 standard by truncating toward zero on negative results.
// See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf section 6.5.5
int div16s(int dividend, int divisor);

View File

@ -0,0 +1,34 @@
// Library Implementation of the Seriously Fast Multiplication
// See http://codebase64.org/doku.php?id=base:seriously_fast_multiplication
// Utilizes the fact that a*b = ((a+b)/2)^2 - ((a-b)/2)^2
// Initialize the mulf_sqr multiplication tables with f(x)=int(x*x/4)
void mulf_init();
// Prepare for fast multiply with an unsigned byte to a word result
void mulf8u_prepare(byte a);
// Calculate fast multiply with a prepared unsigned byte to a word result
// The prepared number is set by calling mulf8u_prepare(byte a)
word mulf8u_prepared(byte b);
// Fast multiply two unsigned bytes to a word result
word mulf8u(byte a, byte b);
// Prepare for fast multiply with an signed byte to a word result
inline void mulf8s_prepare(signed byte a);
// Calculate fast multiply with a prepared unsigned byte to a word result
// The prepared number is set by calling mulf8s_prepare(byte a)
signed word mulf8s_prepared(signed byte b);
// Fast multiply two signed bytes to a word result
signed word mulf8s(signed byte a, signed byte b);
// Fast multiply two unsigned words to a double word result
// Done in assembler to utilize fast addition A+X
dword mulf16u(word a, word b);
// Fast multiply two signed words to a signed double word result
// Fixes offsets introduced by using unsigned multiplication
signed dword mulf16s(signed word a, signed word b);

View File

@ -0,0 +1,136 @@
// Simple Keyboard Input Library
// C64 Keyboard Matrix Reference - from http://codebase64.org/doku.php?id=base:reading_the_keyboard
// Keyboard Codes are %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7)
// +----+----------------------+-------------------------------------------------------------------------------------------------------+
// | | Write | Read $dc01 (C64 screen code in parenthesis): |
// |row:| $dc00: row bits +------------+------------+------------+------------+------------+------------+------------+------------+
// | | | BIT 7 | BIT 6 | BIT 5 | BIT 4 | BIT 3 | BIT 2 | BIT 1 | BIT 0 |
// +----+----------------------+------------+------------+------------+------------+------------+------------+------------+------------+
// |0. | #%11111110 (254/$fe) | DOWN ($ )| F5 ($ )| F3 ($ )| F1 ($ )| F7 ($ )| RIGHT ($ )| RETURN($ )|DELETE ($ )|
// |1. | #%11111101 (253/$fd) |LEFT-SH($ )| e ($05)| s ($13)| z ($1a)| 4 ($34)| a ($01)| w ($17)| 3 ($33)|
// |2. | #%11111011 (251/$fb) | x ($18)| t ($14)| f ($06)| c ($03)| 6 ($36)| d ($04)| r ($12)| 5 ($35)|
// |3. | #%11110111 (247/$f7) | v ($16)| u ($15)| h ($08)| b ($02)| 8 ($38)| g ($07)| y ($19)| 7 ($37)|
// |4. | #%11101111 (239/$ef) | n ($0e)| o ($0f)| k ($0b)| m ($0d)| 0 ($30)| j ($0a)| i ($09)| 9 ($39)|
// |5. | #%11011111 (223/$df) | , ($2c)| @ ($00)| : ($3a)| . ($2e)| - ($2d)| l ($0c)| p ($10)| + ($2b)|
// |6. | #%10111111 (191/$bf) | / ($2f)| ^ ($1e)| = ($3d)|RGHT-SH($ )| HOME ($ )| ; ($3b)| * ($2a)| £ ($1c)|
// |7. | #%01111111 (127/$7f) | STOP ($ )| q ($11)|COMMODR($ )| SPACE ($20)| 2 ($32)|CONTROL($ )| <- ($1f)| 1 ($31)|
// +----+----------------------+------------+------------+------------+------------+------------+------------+------------+------------+
// Keyboard Codes for all 63 keys.
// Keyboard Codes are %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7).
// See C64 Keyboard Matrix Reference http://codebase64.org/doku.php?id=base:reading_the_keyboard
const byte KEY_DEL = $00;
const byte KEY_RETURN = $01;
const byte KEY_CRSR_RIGHT = $02;
const byte KEY_F7 = $03;
const byte KEY_F1 = $04;
const byte KEY_F3 = $05;
const byte KEY_F5 = $06;
const byte KEY_CRSR_DOWN = $07;
const byte KEY_3 = $08;
const byte KEY_W = $09;
const byte KEY_A = $0a;
const byte KEY_4 = $0b;
const byte KEY_Z = $0c;
const byte KEY_S = $0d;
const byte KEY_E = $0e;
const byte KEY_LSHIFT = $0f;
const byte KEY_5 = $10;
const byte KEY_R = $11;
const byte KEY_D = $12;
const byte KEY_6 = $13;
const byte KEY_C = $14;
const byte KEY_F = $15;
const byte KEY_T = $16;
const byte KEY_X = $17;
const byte KEY_7 = $18;
const byte KEY_Y = $19;
const byte KEY_G = $1a;
const byte KEY_8 = $1b;
const byte KEY_B = $1c;
const byte KEY_H = $1d;
const byte KEY_U = $1e;
const byte KEY_V = $1f;
const byte KEY_9 = $20;
const byte KEY_I = $21;
const byte KEY_J = $22;
const byte KEY_0 = $23;
const byte KEY_M = $24;
const byte KEY_K = $25;
const byte KEY_O = $26;
const byte KEY_N = $27;
const byte KEY_PLUS = $28;
const byte KEY_P = $29;
const byte KEY_L = $2a;
const byte KEY_MINUS = $2b;
const byte KEY_DOT = $2c;
const byte KEY_COLON = $2d;
const byte KEY_AT = $2e;
const byte KEY_COMMA = $2f;
const byte KEY_POUND = $30;
const byte KEY_ASTERISK = $31;
const byte KEY_SEMICOLON = $32;
const byte KEY_HOME = $33;
const byte KEY_RSHIFT = $34;
const byte KEY_EQUALS = $35;
const byte KEY_ARROW_UP = $36;
const byte KEY_SLASH = $37;
const byte KEY_1 = $38;
const byte KEY_ARROW_LEFT = $39;
const byte KEY_CTRL = $3a;
const byte KEY_2 = $3b;
const byte KEY_SPACE = $3c;
const byte KEY_COMMODORE = $3d;
const byte KEY_Q = $3e;
const byte KEY_RUNSTOP = $3f;
// Initialize keyboard reading by setting CIA#$ Data Direction Registers
void keyboard_init();
// Read a single row of the keyboard matrix
// The row ID (0-7) of the keyboard matrix row to read. See the C64 key matrix for row IDs.
// Returns the keys pressed on the row as bits according to the C64 key matrix.
// Notice: If the C64 normal interrupt is still running it will occasionally interrupt right between the read & write
// leading to erroneous readings. You must disable kill the normal interrupt or sei/cli around calls to the keyboard matrix reader.
byte keyboard_matrix_read(byte rowid);
// Determines whether a specific key is currently pressed by accessing the matrix directly
// The key is a keyboard code defined from the keyboard matrix by %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7)
// All keys exist as as KEY_XXX constants.
// Returns zero if the key is not pressed and a non-zero value if the key is currently pressed
byte keyboard_key_pressed(byte key);
// Get the keycode corresponding to a specific screen code character
// ch is the character to get the key code for ($00-$3f)
// Returns the key code corresponding to the passed character. Only characters with a non-shifted key are handled.
// If there is no non-shifted key representing the char $3f is returned (representing RUN/STOP) .
byte keyboard_get_keycode(byte ch);
// Current keyboard modifiers (left shift, right shift, ctrl, commodore)
extern byte keyboard_modifiers;
// Left shift is pressed
extern const byte KEY_MODIFIER_LSHIFT;
// Right shift is pressed
extern const byte KEY_MODIFIER_RSHIFT;
// CTRL is pressed
extern const byte KEY_MODIFIER_CTRL;
// Commodore is pressed
extern const byte KEY_MODIFIER_COMMODORE;
// Any shift is pressed
extern const byte KEY_MODIFIER_SHIFT;
// Scans the entire matrix to determine which keys have been pressed/depressed.
// Generates keyboard events into the event buffer. Events can be read using keyboard_event_get().
// Handles debounce and only generates events when the status of a key changes.
// Also stores current status of modifiers in keyboard_modifiers.
void keyboard_event_scan();
// Determine if a specific key is currently pressed based on the last keyboard_event_scan()
// Returns 0 is not pressed and non-0 if pressed
byte keyboard_event_pressed(byte keycode);
// Get the next event from the keyboard event buffer.
// Returns $ff if there is no event waiting. As all events are <$7f it is enough to examine bit 7 when determining if there is any event to process.
// The buffer is filled by keyboard_event_scan()
byte keyboard_event_get();

View File

@ -0,0 +1,66 @@
// A flexible sprite multiplexer routine for 32 sprites.
// Usage:
// - Once:
// - plexInit(screen): Initialize the data structures and set the screen address
// Each frame:
// - Set x-pos, y-pos and pointer in PLEX_XPOS[id], PLEX_YPOS[id], PLEX_PTR[id]
// - plexSort() Sorts the sprites according to y-positions and prepares for showing them. This uses an insertion sort that is quite fast when the relative order of the sprites does not change very much.
// - plexShowSprite() Shows the next sprite by copying values from PLEX_XXX[] to an actual sprite. Actual sprites are used round-robin. This should be called once for each of the 24 virtual sprites.
// - plexFreeNextYpos() Returns the Y-position where the next sprite is available to be shown (ie. the next pos where the next sprite is no longer in use showing something else).
// - plexShowNextYpos() Returns the Y-position of the next sprite to show.
//
// In practice a good method is to wait until the raster is beyond plexFreeNextYpos() and then call plexShowSprite(). Repeat until all 32 sprites have been shown.
// TODO: Let the caller specify the number of sprites to use (or add PLEX_ENABLE[PLEX_COUNT])
#include <c64.h>
// The number of sprites in the multiplexer
extern const char PLEX_COUNT;
// The x-positions of the multiplexer sprites (0x000-0x1ff)
extern unsigned int PLEX_XPOS[PLEX_COUNT];
// The y-positions of the multiplexer sprites.
extern char PLEX_YPOS[PLEX_COUNT];
// The sprite pointers for the multiplexed sprites
extern char PLEX_PTR[PLEX_COUNT];
// The address of the sprite pointers on the current screen (screen+0x3f8).
extern char* PLEX_SCREEN_PTR;
// Indexes of the plex-sprites sorted by sprite y-position. Each call to plexSort() will fix the sorting if changes to the Y-positions have ruined it.
extern char PLEX_SORTED_IDX[PLEX_COUNT];
// Initialize the multiplexer data structures
void plexInit(char* screen);
// Set the address of the current screen used for setting sprite pointers (at screen+0x3f8)
inline void plexSetScreen(char* screen);
// Ensure that the indices in PLEX_SORTED_IDX is sorted based on the y-positions in PLEX_YPOS
// Assumes that the positions are nearly sorted already (as each sprite just moves a bit)
// Uses an insertion sort:
// 1. Moves a marker (m) from the start to end of the array. Every time the marker moves forward all elements before the marker are sorted correctly.
// 2a. If the next element after the marker is larger that the current element
// the marker can be moved forwards (as the sorting is correct).
// 2b. If the next element after the marker is smaller than the current element:
// elements before the marker are shifted right one at a time until encountering one smaller than the current one.
// It is then inserted at the spot. Now the marker can move forward.
void plexSort();
// Show the next sprite.
// plexSort() prepares showing the sprites
void plexShowSprite();
// Get the y-position of the next sprite to show
inline char plexShowNextYpos();
// Prepare for a new frame. Initialize free to zero for all sprites.
inline void plexFreePrepare();
// Get the Y-position where the next sprite to be shown is free to use.
inline char plexFreeNextYpos();
// Update the data structure to reflect that a sprite has been shown. This sprite will be free again after 21 lines.
inline void plexFreeAdd(char ypos);

View File

@ -0,0 +1,19 @@
// Simple binary multiplication implementation
// Perform binary multiplication of two unsigned 8-bit bytes into a 16-bit unsigned word
unsigned int mul8u(char a, char b);
// Multiply of two signed bytes to a signed word
// Fixes offsets introduced by using unsigned multiplication
int mul8s(signed char a, signed char b);
// Multiply a signed byte and an unsigned byte (into a signed word)
// Fixes offsets introduced by using unsigned multiplication
int mul8su(signed char a, char b);
// Perform binary multiplication of two unsigned 16-bit words into a 32-bit unsigned double word
unsigned long mul16u(unsigned int a, unsigned int b);
// Multiply of two signed words to a signed double word
// Fixes offsets introduced by using unsigned multiplication
signed long mul16s(int a, int b);

View File

@ -0,0 +1,76 @@
#include <print.h>
// Print a number of zero-terminated strings, each followed by a newline.
// The sequence of lines is terminated by another zero.
void print_str_lines(byte* str);
// Print a zero-terminated string followed by a newline
void print_str_ln(byte* str);
// Print a zero-terminated string
void print_str(byte* str);
// Print a string at a specific screen position
void print_str_at(byte* str, byte* at);
// Print a newline
void print_ln();
// Print a int as HEX
void print_sword(signed word w);
// Print a signed byte as HEX
void print_sbyte(signed byte b);
// Prints a signed byte as HEX at a specific position on the screen
// row and col are 0-based indices
inline void print_sbyte_pos(signed byte sb, byte row, byte col);
// Print a signed byte as hex at a specific screen position
void print_sbyte_at(signed byte b, byte* at);
// Print a word as HEX
void print_word(word w);
// Print a byte as DECIMAL
void print_byte_decimal(byte b);
// Print a word as DECIMAL
void print_word_decimal(word w);
// Print a word as HEX at a specific position
void print_word_at(word w, byte* at);
// Print a dword as HEX
void print_dword(unsigned dword dw);
// Print a unsigned long as DECIMAL
void print_dword_decimal(unsigned dword w);
// Print a unsigned long as HEX at a specific position
void print_dword_at(unsigned dword dw, byte* at);
// Print a signed long as HEX
void print_sdword(signed dword dw);
// Print a byte as HEX
void print_byte(byte b);
// Prints a byte as HEX at a specific position on the screen
// row and col are 0-based indices
void print_byte_pos(byte b, byte row, byte col);
// Print a byte as HEX at a specific position
void print_byte_at(byte b, byte* at);
// Print a single char
void print_char(byte ch);
// Print a single char
void print_char_at(byte ch, byte* at);
// Clear the screen. Also resets current line/char cursor.
void print_cls();
// Set the screen to print on. Also resets current line/char cursor.
void print_set_screen(byte* screen);

View File

@ -0,0 +1,52 @@
// Sinus Generator functions using only multiplication, addition and bit shifting
// Uses a single division for converting the wavelength to a reciprocal.
// Generates sinus using the series sin(x) = x - x^/3! + x^-5! - x^7/7! ...
// Uses the approximation sin(x) = x - x^/6 + x^/128
// Optimization possibility: Use symmetries when generating sinustables. wavelength%2==0 -> mirror symmetry over PI, wavelength%4==0 -> mirror symmetry over PI/2.
// PI*2 in u[4.28] format
extern const unsigned long PI2_u4f28;
// PI in u[4.28] format
extern const unsigned long PI_u4f28;
// PI/2 in u[4.28] format
extern const unsigned long PI_HALF_u4f28;
// PI*2 in u[4.12] format
extern const unsigned int PI2_u4f12;
// PI in u[4.12] format
extern const unsigned int PI_u4f12;
// PI/2 in u[4.12] format
extern const unsigned int PI_HALF_u4f12;
// Generate signed (large) word sinus table - on the full -$7fff - $7fff range
// sintab - the table to generate into
// wavelength - the number of sinus points in a total sinus wavelength (the size of the table)
void sin16s_gen(int* sintab, unsigned int wavelength);
// Generate signed word 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)
void sin16s_gen2(int* sintab, unsigned int wavelength, int min, int max);
// Generate signed byte sinus table - on the full -$7f - $7f range
// sintab - the table to generate into
// wavelength - the number of sinus points in a total sinus wavelength (the size of the table)
void sin8s_gen(signed char* sintab, unsigned int wavelength);
// Calculate signed word sinus sin(x)
// x: unsigned dword input u[4.28] in the interval $00000000 - PI2_u4f28
// result: signed word sin(x) s[0.15] - using the full range -$7fff - $7fff
int sin16s(unsigned long x);
// Calculate signed byte sinus sin(x)
// x: unsigned word input u[4.12] in the interval $0000 - PI2_u4f12
// result: signed byte sin(x) s[0.7] - using the full range -$7f - $7f
signed char sin8s(unsigned int x);
// Calculate val*val for two unsigned word 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
unsigned int mulu16_sel(unsigned int v1, unsigned int v2, char select);
// Calculate val*val for two unsigned byte values - the result is 8 selected bits of the 16-bit result.
// The select parameter indicates how many of the highest bits of the 16-bit result to skip
char mulu8_sel(char v1, char v2, char select);

14
src/main/kc/include/sqr.h Normal file
View File

@ -0,0 +1,14 @@
// Table-based implementation of integer square sqr() and square root sqrt()
// Initialize squares table
// Uses iterative formula (x+1)^2 = x^2 + 2*x + 1
void init_squares();
// Find the square of a byte value
// Uses a table of squares that must be initialized by calling init_squares()
word sqr(byte val);
// Find the (integer) square root of a word value
// If the square is not an integer then it returns the largest integer N where N*N <= val
// Uses a table of squares that must be initialized by calling init_squares()
byte sqrt(word val);

View File

@ -0,0 +1,41 @@
// C standard library stdlib.h
// Implementation of functions found int C stdlib.h / stdlib.c
#include <string.h>
// Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.
// The content of the newly allocated block of memory is not initialized, remaining with indeterminate values.
void* malloc(unsigned int size);
// A block of memory previously allocated by a call to malloc is deallocated, making it available again for further allocations.
// If ptr is a null pointer, the function does nothing.
void free(void* ptr);
// Allocates memory and returns a pointer to it. Sets allocated memory to zero.
// - nitems This is the number of elements to be allocated.
// - size This is the size of elements.
void *calloc(size_t nitems, size_t size);
// Searches an array of nitems unsigned words, the initial member of which is pointed to by base, for a member that matches the value key.
// - key - The value to look for
// - items - Pointer to the start of the array to search in
// - num - The number of items in the array
// Returns pointer to an entry in the array that matches the search key
unsigned int* bsearch16u(unsigned int key, unsigned int* items, char num);
// The different supported radix
enum RADIX { BINARY=2, OCTAL=8, DECIMAL=10, HEXADECIMAL=16 };
// Converts unsigned number value to a string representing it in RADIX format.
// If the leading digits are zero they are not included in the string.
// - value : The number to be converted to RADIX
// - buffer : receives the string representing the number and zero-termination.
// - radix : The radix to convert the number to (from the enum RADIX)
void utoa(unsigned int value, char* buffer, enum RADIX radix);
// Converts unsigned number value to a string representing it in RADIX format.
// If the leading digits are zero they are not included in the string.
// - value : The number to be converted to RADIX
// - buffer : receives the string representing the number and zero-termination.
// - radix : The radix to convert the number to (from the enum RADIX)
void ultoa(unsigned long value, char* buffer, enum RADIX radix);

View File

@ -0,0 +1,21 @@
// C standard library string.h
// Functions to manipulate C strings and arrays.
typedef unsigned int size_t ;
// Copy block of memory (forwards)
// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.
void* memcpy( void* destination, void* source, size_t num );
// Move block of memory
// Copies the values of num bytes from the location pointed by source to the memory block pointed by destination. Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.
void* memmove( void* destination, void* source, size_t num );
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
void *memset(void *str, char c, size_t num);
// Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
char* strcpy( char* destination, char* source );
// Computes the length of the string str up to but not including the terminating null character.
size_t strlen(char *str);

View File

@ -1,8 +1,6 @@
// C standard library time.h
// Functions to get and manipulate date and time information.
#include <c64.c>
// Type suitable for storing the processor time.
typedef unsigned long clock_t;
@ -21,17 +19,8 @@ const unsigned long CLOCKS_PER_INIT = 0x12;
// Returns the processor clock time used since the beginning of an implementation defined era (normally the beginning of the program).
// This uses CIA #2 Timer A+B on the C64, and must be initialized using clock_start()
clock_t clock(void) {
return 0xffffffff - *CIA2_TIMER_AB;
}
clock_t clock(void);
// Reset & start the processor clock time. The value can be read using clock().
// This uses CIA #2 Timer A+B on the C64
void clock_start(void) {
// Setup CIA#2 timer A to count (down) CPU cycles
*CIA2_TIMER_A_CONTROL = CIA_TIMER_CONTROL_STOP | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_A_COUNT_CYCLES;
*CIA2_TIMER_B_CONTROL = CIA_TIMER_CONTROL_STOP | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A;
*CIA2_TIMER_AB = 0xffffffff;
*CIA2_TIMER_B_CONTROL = CIA_TIMER_CONTROL_START | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A;
*CIA2_TIMER_A_CONTROL = CIA_TIMER_CONTROL_START | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_A_COUNT_CYCLES;
}
void clock_start(void);

View File

@ -1,6 +1,8 @@
// Find atan2(x, y) using the CORDIC method
// See http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf
#include <atan2.h>
// The number of iterations performed during 16-bit CORDIC atan2 calculation
const byte CORDIC_ITERATIONS_16 = 15;

View File

@ -2,6 +2,8 @@
// See https://www.c64-wiki.com/wiki/Floating_point_arithmetic
// See http://www.pagetable.com/c64rom/c64rom_sc.html
#include <basic-floats.h>
// Zeropage addresses used to hold lo/hi-bytes of addresses of float numbers in MEM
char* const memLo = 0xfe;
char* const memHi = 0xff;

View File

@ -1,6 +1,8 @@
// Plot and line drawing routines for HIRES bitmaps
// Currently it can only plot on the first 256 x-positions.
#include <bitmap-draw.h>
// Tables for the plotter - initialized by calling bitmap_draw_init();
const byte bitmap_plot_xlo[256];
const byte bitmap_plot_xhi[256];

View File

@ -1,5 +1,6 @@
// Simple single-color (320x200) bitmap routines
#include <string.c>
#include <bitmap2.h>
#include <string.h>
// The adddress of the bitmap screen (used for colors)
byte* bitmap_screen;

27
src/main/kc/lib/c64.c Normal file
View File

@ -0,0 +1,27 @@
// Commodore 64 Registers and Constants
#include <c64.h>
// Get the value to store into D018 to display a specific screen and charset/bitmap
// Optimized for ASM from (byte)((((word)screen&$3fff)/$40)|(((word)charset&$3fff)/$400));
inline char toD018(char* screen, char* gfx) {
return (>((((unsigned int)screen&$3fff)*4)))|(((>((unsigned int)gfx))/4)&$f);
}
// Get the value to store into DD00 (CIA 2 port A) to choose a specific VIC bank
// Optimized for ASM from %00000011 ^ (byte)((word)gfx/$4000)
inline char toDd00(char* gfx) {
return %00000011 ^ (>((unsigned int)gfx))/$40;
}
// Get the sprite pointer for a sprite.
// The sprite pointer is the index of the sprite within the graphics bank and equal to the sprite (byte)(sprite_addr/64)
// The sprite pointers are stored SCREEN+$3f8+sprite_id to set the pointer of each sprite
inline char toSpritePtr(char* sprite) {
return (char)(((unsigned int)sprite)/$40);
}
// Select a specific VIC graphics bank by setting the CIA 2 port A ($dd00) as needed
inline void vicSelectGfxBank(char* gfx) {
*CIA2_PORT_A_DDR = %00000011;
*CIA2_PORT_A = toDd00(gfx);
}

24
src/main/kc/lib/c64dtv.c Normal file
View File

@ -0,0 +1,24 @@
// C64 DTV version 2 Registers and Constants
//
// Sources
// (J) https://www.c64-wiki.com/wiki/C64DTV_Programming_Guide
// (H) http://dtvhacking.cbm8bit.com/dtv_wiki/images/d/d9/Dtv_registers_full.txt
#include <c64dtv.h>
// Set the memory pointed to by CPU BANK 1 SEGMENT ($4000-$7fff)
// This sets which actual memory is addressed when the CPU reads/writes to $4000-$7fff
// The actual memory addressed will be $4000*cpuSegmentIdx
void dtvSetCpuBankSegment1(byte cpuBankIdx) {
// Move CPU BANK 1 SEGMENT ($4000-$7fff)
byte* cpuBank = $ff;
*cpuBank = cpuBankIdx;
asm {
// SAC $dd - A register points to 13 BANK 1 segment
.byte $32, $dd
// LDA $ff - Set CPU BANK 1 SEGMENT ($4000-$7fff) to ($ff)*$4000
lda $ff
// SAC $00 - A register points to 0 ACCUMULATOR
.byte $32, $00
}
}

View File

@ -2,6 +2,8 @@
// Follows the C99 standard by truncating toward zero on negative results.
// See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf section 6.5.5
#include <division.h>
// Remainder after signed 8 bit division
byte rem8u =0;

View File

@ -2,6 +2,8 @@
// See http://codebase64.org/doku.php?id=base:seriously_fast_multiplication
// Utilizes the fact that a*b = ((a+b)/2)^2 - ((a-b)/2)^2
#include <fastmultiply.h>
// mulf_sqr tables will contain f(x)=int(x*x/4) and g(x) = f(x-255).
// <f(x) = <(( x * x )/4)
byte align($100) mulf_sqr1_lo[512];

View File

@ -16,75 +16,8 @@
// |7. | #%01111111 (127/$7f) | STOP ($ )| q ($11)|COMMODR($ )| SPACE ($20)| 2 ($32)|CONTROL($ )| <- ($1f)| 1 ($31)|
// +----+----------------------+------------+------------+------------+------------+------------+------------+------------+------------+
#include <c64.c>
// Keyboard Codes for all 63 keys.
// Keyboard Codes are %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7).
// See C64 Keyboard Matrix Reference http://codebase64.org/doku.php?id=base:reading_the_keyboard
const byte KEY_DEL = $00;
const byte KEY_RETURN = $01;
const byte KEY_CRSR_RIGHT = $02;
const byte KEY_F7 = $03;
const byte KEY_F1 = $04;
const byte KEY_F3 = $05;
const byte KEY_F5 = $06;
const byte KEY_CRSR_DOWN = $07;
const byte KEY_3 = $08;
const byte KEY_W = $09;
const byte KEY_A = $0a;
const byte KEY_4 = $0b;
const byte KEY_Z = $0c;
const byte KEY_S = $0d;
const byte KEY_E = $0e;
const byte KEY_LSHIFT = $0f;
const byte KEY_5 = $10;
const byte KEY_R = $11;
const byte KEY_D = $12;
const byte KEY_6 = $13;
const byte KEY_C = $14;
const byte KEY_F = $15;
const byte KEY_T = $16;
const byte KEY_X = $17;
const byte KEY_7 = $18;
const byte KEY_Y = $19;
const byte KEY_G = $1a;
const byte KEY_8 = $1b;
const byte KEY_B = $1c;
const byte KEY_H = $1d;
const byte KEY_U = $1e;
const byte KEY_V = $1f;
const byte KEY_9 = $20;
const byte KEY_I = $21;
const byte KEY_J = $22;
const byte KEY_0 = $23;
const byte KEY_M = $24;
const byte KEY_K = $25;
const byte KEY_O = $26;
const byte KEY_N = $27;
const byte KEY_PLUS = $28;
const byte KEY_P = $29;
const byte KEY_L = $2a;
const byte KEY_MINUS = $2b;
const byte KEY_DOT = $2c;
const byte KEY_COLON = $2d;
const byte KEY_AT = $2e;
const byte KEY_COMMA = $2f;
const byte KEY_POUND = $30;
const byte KEY_ASTERISK = $31;
const byte KEY_SEMICOLON = $32;
const byte KEY_HOME = $33;
const byte KEY_RSHIFT = $34;
const byte KEY_EQUALS = $35;
const byte KEY_ARROW_UP = $36;
const byte KEY_SLASH = $37;
const byte KEY_1 = $38;
const byte KEY_ARROW_LEFT = $39;
const byte KEY_CTRL = $3a;
const byte KEY_2 = $3b;
const byte KEY_SPACE = $3c;
const byte KEY_COMMODORE = $3d;
const byte KEY_Q = $3e;
const byte KEY_RUNSTOP = $3f;
#include <keyboard.h>
#include <c64.h>
// Keycodes for each screen code character from $00-$3f.
// Chars that do not have an unmodified keycode return $3f (representing RUN/STOP).

View File

@ -12,7 +12,8 @@
// In practice a good method is to wait until the raster is beyond plexFreeNextYpos() and then call plexShowSprite(). Repeat until all 32 sprites have been shown.
// TODO: Let the caller specify the number of sprites to use (or add PLEX_ENABLE[PLEX_COUNT])
#include <c64.c>
#include <multiplexer.h>
#include <c64.h>
// The number of sprites in the multiplexer
const char PLEX_COUNT = 32;

View File

@ -1,5 +1,7 @@
// Simple binary multiplication implementation
#include <multiply.h>
// Perform binary multiplication of two unsigned 8-bit bytes into a 16-bit unsigned word
word mul8u(byte a, byte b) {
word res = 0;

View File

@ -1,5 +1,5 @@
#include <stdlib.c>
#include <string.c>
#include <stdlib.h>
#include <string.h>
byte* print_screen = $0400;
byte* print_line_cursor = print_screen;

View File

@ -4,8 +4,9 @@
// Uses the approximation sin(x) = x - x^/6 + x^/128
// Optimization possibility: Use symmetries when generating sinustables. wavelength%2==0 -> mirror symmetry over PI, wavelength%4==0 -> mirror symmetry over PI/2.
#include <division.c>
#include <multiply.c>
#include <sinus.h>
#include <division.h>
#include <multiply.h>
// PI*2 in u[4.28] format
const dword PI2_u4f28 = $6487ed51;

View File

@ -1,6 +1,7 @@
// Table-based implementation of integer square sqr() and square root sqrt()
#include <stdlib.c>
#include <sqr.h>
#include <stdlib.h>
// The number of squares to pre-calculate. Limits what values sqr() can calculate and the result of sqrt()
byte NUM_SQUARES = 0xff;

View File

@ -1,6 +1,7 @@
// C standard library stdlib.h
// Implementation of functions found int C stdlib.h / stdlib.c
#include <string.c>
#include <stdlib.h>
#include <string.h>
// Top of the heap used by malloc()
unsigned char* HEAP_TOP = 0xa000;
@ -52,8 +53,6 @@ unsigned int* bsearch16u(unsigned int key, unsigned int* items, char num) {
return *items<=key?items:items-1;
}
// The different supported radix
enum RADIX { BINARY=2, OCTAL=8, DECIMAL=10, HEXADECIMAL=16 };
// The digits used for numbers
char DIGITS[] = "0123456789abcdef"z;

View File

@ -1,7 +1,6 @@
// C standard library string.h
// Functions to manipulate C strings and arrays.
typedef word size_t ;
#include <string.h>
// Copy block of memory (forwards)
// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.

21
src/main/kc/lib/time.c Normal file
View File

@ -0,0 +1,21 @@
// C standard library time.h
// Functions to get and manipulate date and time information.
#include <time.h>
#include <c64.h>
// Returns the processor clock time used since the beginning of an implementation defined era (normally the beginning of the program).
// This uses CIA #2 Timer A+B on the C64, and must be initialized using clock_start()
clock_t clock(void) {
return 0xffffffff - *CIA2_TIMER_AB;
}
// Reset & start the processor clock time. The value can be read using clock().
// This uses CIA #2 Timer A+B on the C64
void clock_start(void) {
// Setup CIA#2 timer A to count (down) CPU cycles
*CIA2_TIMER_A_CONTROL = CIA_TIMER_CONTROL_STOP | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_A_COUNT_CYCLES;
*CIA2_TIMER_B_CONTROL = CIA_TIMER_CONTROL_STOP | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A;
*CIA2_TIMER_AB = 0xffffffff;
*CIA2_TIMER_B_CONTROL = CIA_TIMER_CONTROL_START | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A;
*CIA2_TIMER_A_CONTROL = CIA_TIMER_CONTROL_START | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_A_COUNT_CYCLES;
}

View File

@ -1,233 +0,0 @@
// Commodore 64 Registers and Constants
// Processor port data direction register
byte* const PROCPORT_DDR = $00;
// Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written
const byte PROCPORT_DDR_MEMORY_MASK = %00000111;
// Processor Port Register controlling RAM/ROM configuration and the datasette
byte* const PROCPORT = $01;
// RAM in all three areas $A000, $D000, $E000
const byte PROCPORT_RAM_ALL = %00000000;
// RAM in $A000, $E000 I/O in $D000
const byte PROCPORT_RAM_IO = %00000101;
// RAM in $A000, $E000 CHAR ROM in $D000
const byte PROCPORT_RAM_CHARROM = %00000001;
// RAM in $A000, I/O in $D000, KERNEL in $E000
const byte PROCPORT_KERNEL_IO = %00000110;
// BASIC in $A000, I/O in $D000, KERNEL in $E000
const byte PROCPORT_BASIC_KERNEL_IO = %00000111;
// The address of the CHARGEN character set
byte* const CHARGEN = $d000;
// Positions of the border (in sprite positions)
const byte BORDER_XPOS_LEFT=24;
const word BORDER_XPOS_RIGHT=344;
const byte BORDER_YPOS_TOP=50;
const byte BORDER_YPOS_BOTTOM=250;
// The offset of the sprite pointers from the screen start address
const word SPRITE_PTRS = $3f8;
byte* const SPRITES_XPOS = $d000;
byte* const SPRITES_YPOS = $d001;
byte* const SPRITES_XMSB = $d010;
byte* const RASTER = $d012;
byte* const SPRITES_ENABLE = $d015;
byte* const SPRITES_EXPAND_Y = $d017;
byte* const SPRITES_PRIORITY = $d01b;
byte* const SPRITES_MC = $d01c;
byte* const SPRITES_EXPAND_X = $d01d;
byte* const BORDERCOL = $d020;
byte* const BGCOL = $d021;
byte* const BGCOL1 = $d021;
byte* const BGCOL2 = $d022;
byte* const BGCOL3 = $d023;
byte* const BGCOL4 = $d024;
byte* const SPRITES_MC1 = $d025;
byte* const SPRITES_MC2 = $d026;
byte* const SPRITES_COLS = $d027;
byte* const VIC_CONTROL = $d011;
byte* const D011 = $d011;
const byte VIC_RST8 = %10000000;
const byte VIC_ECM = %01000000;
const byte VIC_BMM = %00100000;
const byte VIC_DEN = %00010000;
const byte VIC_RSEL = %00001000;
byte* const VIC_CONTROL2 = $d016;
byte* const D016 = $d016;
const byte VIC_MCM = %00010000;
const byte VIC_CSEL = %00001000;
byte* const D018 = $d018;
byte* const VIC_MEMORY = $d018;
byte* const LIGHTPEN_X = $d013;
byte* const LIGHTPEN_Y = $d014;
// VIC II IRQ Status Register
byte* const IRQ_STATUS = $d019;
// VIC II IRQ Enable Register
byte* const IRQ_ENABLE = $d01a;
// Bits for the IRQ Status/Enable Registers
const byte IRQ_RASTER = %00000001;
const byte IRQ_COLLISION_BG = %00000010;
const byte IRQ_COLLISION_SPRITE = %00000100;
const byte IRQ_LIGHTPEN = %00001000;
// Color Ram
byte* const COLS = $d800;
// CIA#1 Port A: keyboard matrix columns and joystick #2
byte* const CIA1_PORT_A = $dc00;
// CIA#1 Port B: keyboard matrix rows and joystick #1.
byte* const CIA1_PORT_B = $dc01;
// CIA #1 Port A data direction register.
byte* const CIA1_PORT_A_DDR = $dc02;
// CIA #1 Port B data direction register.
byte* const CIA1_PORT_B_DDR = $dc03;
// CIA #1 Timer A Value
word* const CIA1_TIMER_A = $dc04;
// CIA #1 Timer B Value
word* const CIA1_TIMER_B = $dc06;
// CIA #1 Time-of-day real-time-clock tenth seconds (BCD)
byte* const CIA1_TOD_10THS = $dc08;
// CIA #1 Time-of-day real-time-clock seconds (BCD)
byte* const CIA1_TOD_SEC = $dc09;
// CIA #1 Time-of-day real-time-clock minutes (BCD)
byte* const CIA1_TOD_MIN = $dc0a;
// CIA #1 Time-of-day real-time-clock hours (BCD)
byte* const CIA1_TOD_HOURS = $dc0b;
// CIA #1 Serial Shift Register
byte* const CIA1_SERIAL_SHIFT_OUT = $dc0c;
// CIA#1 Interrupt Status & Control Register
byte* const CIA1_INTERRUPT = $dc0d;
// CIA#1 Timer A Control Register
byte* const CIA1_TIMER_A_CONTROL = $dc0e;
// CIA#1 Timer B Control Register
byte* const CIA1_TIMER_B_CONTROL = $dc0f;
// CIA#2 Port A: Serial bus, RS-232, VIC memory bank
byte* const CIA2_PORT_A = $dd00;
// CIA#2 Port B: RS-232
byte* const CIA2_PORT_B = $dd01;
// CIA #2 Port A data direction register.
byte* const CIA2_PORT_A_DDR = $dd02;
// CIA #2 Port B data direction register.
byte* const CIA2_PORT_B_DDR = $dd03;
// CIA #2 Timer A+B Value (32-bit)
dword* const CIA2_TIMER_AB = $dd04;
// CIA #2 Timer A Value (16-bit)
word* const CIA2_TIMER_A = $dd04;
// CIA #2 Timer B Value (16-bit)
word* const CIA2_TIMER_B = $dd06;
// CIA #2 Time-of-day real-time-clock tenth seconds (BCD)
byte* const CIA2_TOD_10THS = $dd08;
// CIA #2 Time-of-day real-time-clock seconds (BCD)
byte* const CIA2_TOD_SEC = $dd09;
// CIA #2 Time-of-day real-time-clock minutes (BCD)
byte* const CIA2_TOD_MIN = $dd0a;
// CIA #2 Time-of-day real-time-clock hours (BCD)
byte* const CIA2_TOD_HOURS = $dd0b;
// CIA #2 Serial Shift Register
byte* const CIA2_SERIAL_SHIFT_OUT = $dd0c;
// CIA #2 Interrupt Status & Control Register
byte* const CIA2_INTERRUPT = $dd0d;
// CIA #2 Timer A Control Register
byte* const CIA2_TIMER_A_CONTROL = $dd0e;
// CIA #2 Timer B Control Register
byte* const CIA2_TIMER_B_CONTROL = $dd0f;
// Value that disables all CIA interrupts when stored to the CIA Interrupt registers
const byte CIA_INTERRUPT_CLEAR = $7f;
// Timer Control - Start/stop timer (0:stop, 1: start)
const byte CIA_TIMER_CONTROL_STOP = 0b00000000;
// Timer Control - Start/stop timer (0:stop, 1: start)
const byte CIA_TIMER_CONTROL_START = 0b00000001;
// Timer Control - Time CONTINUOUS/ONE-SHOT (0:CONTINUOUS, 1: ONE-SHOT)
const byte CIA_TIMER_CONTROL_CONTINUOUS = 0b00000000;
// Timer Control - Time CONTINUOUS/ONE-SHOT (0:CONTINUOUS, 1: ONE-SHOT)
const byte CIA_TIMER_CONTROL_ONESHOT = 0b00001000;
// Timer A Control - Timer counts (0:system cycles, 1: CNT pulses)
const byte CIA_TIMER_CONTROL_A_COUNT_CYCLES = 0b00000000;
// Timer A Control - Timer counts (0:system cycles, 1: CNT pulses)
const byte CIA_TIMER_CONTROL_A_COUNT_CNT = 0b00100000;
// Timer A Control - Serial Port Mode (0: Serial Port Input, 1: Serial Port Output)
const byte CIA_TIMER_CONTROL_A_SERIAL_IN = 0b00000000;
// Timer A Control - Serial Port Mode (0: Serial Port Input, 1: Serial Port Output)
const byte CIA_TIMER_CONTROL_A_SERIAL_OUT = 0b01000000;
// Timer A Control - time-of-day clock Mode (0: 60Hz, 1: 50Hz)
const byte CIA_TIMER_CONTROL_A_TOD_60HZ = 0b00000000;
// Timer A Control - time-of-day clock Mode (0: 60Hz, 1: 50Hz)
const byte CIA_TIMER_CONTROL_A_TOD_50HZ = 0b10000000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const byte CIA_TIMER_CONTROL_B_COUNT_CYCLES = 0b00000000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const byte CIA_TIMER_CONTROL_B_COUNT_CNT = 0b00100000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const byte CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A = 0b01000000;
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
const byte CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A_CNT = 0b01100000;
// Timer B Control - time-of-day write mode (0: TOD clock, 1: TOD alarm)
const byte CIA_TIMER_CONTROL_B_TOD_CLOCK_SET = 0b00000000;
// Timer B Control - time-of-day write mode (0: TOD clock, 1: TOD alarm)
const byte CIA_TIMER_CONTROL_B_TOD_ALARM_SET = 0b10000000;
// The vector used when the KERNAL serves IRQ interrupts
void()** const KERNEL_IRQ = $0314;
// The vector used when the KERNAL serves NMI interrupts
void()** const KERNEL_NMI = $0318;
// The vector used when the HARDWARE serves IRQ interrupts
void()** const HARDWARE_IRQ = $fffe;
// The SID volume
byte* const SID_VOLUME = $d418;
// The colors of the C64
const byte BLACK = $0;
const byte WHITE = $1;
const byte RED = $2;
const byte CYAN = $3;
const byte PURPLE = $4;
const byte GREEN = $5;
const byte BLUE = $6;
const byte YELLOW = $7;
const byte ORANGE = $8;
const byte BROWN = $9;
const byte PINK = $a;
const byte DARK_GREY= $b;
const byte GREY = $c;
const byte LIGHT_GREEN = $d;
const byte LIGHT_BLUE = $e;
const byte LIGHT_GREY = $f;
// Get the value to store into D018 to display a specific screen and charset/bitmap
// Optimized for ASM from (byte)((((word)screen&$3fff)/$40)|(((word)charset&$3fff)/$400));
inline byte toD018(byte* screen, byte* gfx) {
return (>((((word)screen&$3fff)*4)))|(((>((word)gfx))/4)&$f);
}
// Get the value to store into DD00 (CIA 2 port A) to choose a specific VIC bank
// Optimized for ASM from %00000011 ^ (byte)((word)gfx/$4000)
inline byte toDd00(byte* gfx) {
return %00000011 ^ (>((word)gfx))/$40;
}
// Get the sprite pointer for a sprite.
// The sprite pointer is the index of the sprite within the graphics bank and equal to the sprite (byte)(sprite_addr/64)
// The sprite pointers are stored SCREEN+$3f8+sprite_id to set the pointer of each sprite
inline byte toSpritePtr(byte* sprite) {
return (byte)(((word)sprite)/$40);
}
// Select a specific VIC graphics bank by setting the CIA 2 port A ($dd00) as needed
inline void vicSelectGfxBank(byte* gfx) {
*CIA2_PORT_A_DDR = %00000011;
*CIA2_PORT_A = toDd00(gfx);
}

View File

@ -4,7 +4,8 @@ REM KICKC HOME
set KICKC_HOME=%~dp0..
echo KICKC_HOME=%KICKC_HOME%
REM KCLIB HOME
set KICKCLIB_HOME=%KICKC_HOME%\stdlib
set KICKC_STDLIB=%KICKC_HOME%\lib
set KICKC_STDINCLUDE=%KICKC_HOME%\include
echo KICKCLIB_HOME=%KICKCLIB_HOME%
set KICKC_FRAGMENT_HOME=%KICKC_HOME%\fragment
echo KICKC_FRAGMENT_HOME=%KICKC_FRAGMENT_HOME%
@ -14,5 +15,5 @@ REM KICKC_JAR
for %%I in ( %KICKC_HOME%\lib\kickc-*.jar ) do set KICKC_JAR=%%I
echo KICKC_JAR=%KICKC_JAR%
echo java -jar %KICKC_JAR% -I %KICKCLIB_HOME% -F %KICKC_FRAGMENT_HOME% %*
java -jar %KICKC_JAR% -I %KICKCLIB_HOME% -F %KICKC_FRAGMENT_HOME% %*
echo java -jar %KICKC_JAR% -I %KICKC_STDINCLUDE% -L %KICKC_STDLIB% -F %KICKC_FRAGMENT_HOME% %*
java -jar %KICKC_JAR% -I %KICKC_STDINCLUDE% -L %KICKC_STDLIB% -F %KICKC_FRAGMENT_HOME% %*

View File

@ -3,8 +3,10 @@
# JAVA HOME
# KICKC HOME
export KICKC_HOME="$(dirname $0)/.."
# KCLIB HOME
export KICKC_STDLIB_HOME="$KICKC_HOME/stdlib"
# STANDARD INCLUDE
export KICKC_STD_INCLUDE="$KICKC_HOME/include"
# STANDARD LIBRARY
export KICKC_STD_LIB="$KICKC_HOME/lib"
# FRAGMENTS HOME
export KICKC_FRAGMENT_HOME="$KICKC_HOME/fragment"
# KICKASSEMBLER HOME
@ -19,5 +21,5 @@ while [[ "$#" -gt 0 ]]; do case $1 in
*) export PARAM="$PARAM $1"; shift;;
esac; done
echo java -jar $KICKC_JAR -F $KICKC_FRAGMENT_HOME $PARAM -I $KICKC_STDLIB_HOME
java -jar $KICKC_JAR -F $KICKC_FRAGMENT_HOME $PARAM -I $KICKC_STDLIB_HOME
echo java -jar $KICKC_JAR -F $KICKC_FRAGMENT_HOME $PARAM -I $KICKC_STD_INCLUDE -L $KICKC_STD_LIB
java -jar $KICKC_JAR -F $KICKC_FRAGMENT_HOME $PARAM -I $KICKC_STD_INCLUDE -L $KICKC_STD_LIB

View File

@ -32,13 +32,19 @@ import static org.junit.Assert.assertTrue;
*/
public class TestPrograms {
final String stdlibPath = "src/main/kc/stdlib";
final String stdIncludePath = "src/main/kc/include";
final String stdLibPath = "src/main/kc/lib";
final String testPath = "src/test/kc";
final String refPath = "src/test/ref/";
public TestPrograms() {
}
@Test
public void testIncludes3() throws IOException, URISyntaxException {
compileAndCompare("complex/includes/includes-3.c");
}
@Test
public void testIncludes2() throws IOException, URISyntaxException {
compileAndCompare("complex/includes/includes-2.c");
@ -4023,8 +4029,9 @@ public class TestPrograms {
if(compileLog != null) {
compiler.setLog(compileLog);
}
compiler.addImportPath(stdlibPath);
compiler.addImportPath(testPath);
compiler.addIncludePath(stdIncludePath);
compiler.addIncludePath(testPath);
compiler.addLibraryPath(stdLibPath);
compiler.initAsmFragmentSynthesizer(asmFragmentSynthesizer);
if(upliftCombinations != null) {
compiler.setUpliftCombinations(upliftCombinations);

View File

@ -1,4 +1,4 @@
#include <c64.c>
#include <c64.h>
byte* const SCREEN = $400;
byte* const BITMAP = $2000;

View File

@ -2,7 +2,7 @@
// Coded by Richard-William Loerakker
// Original Source https://bcaorganizer.blogspot.com/p/c-program-for_21.html?fbclid=IwAR0iL8pYcCqhCPa6LmtQ9qej-YonYVepY2cBegYRIWO0l8RPeOnTVniMAac
#include <c64.c>
#include <c64.h>
byte* const SCREEN = $400;
byte* const BITMAP = $2000;

View File

@ -1,8 +1,8 @@
// Illustrates problem with bitmap-draw.kc line()
// Reported by Janne Johansson
#include <c64.c>
#include <bitmap-draw.c>
#include <c64.h>
#include <bitmap-draw.h>
byte* const SCREEN = $400;
byte* const BITMAP = $2000;

View File

@ -1,8 +1,8 @@
// Shows that bitmap2.kc line() does not have the same problem as bitmap-draw.kc
// See bitmap-line-anim-1.kc
#include <c64.c>
#include <bitmap2.c>
#include <c64.h>
#include <bitmap2.h>
byte* const SCREEN = $400;
byte* const BITMAP = $2000;

View File

@ -1,7 +1,7 @@
// Tests the simple bitmap plotter - and counts plots per frame in an IRQ
// Plots simple plots
#include <c64.c>
#include <bitmap2.c>
#include <c64.h>
#include <bitmap2.h>
byte* BITMAP = 0x2000;
byte* SCREEN = 0x0400;

View File

@ -1,9 +1,9 @@
// Tests the simple bitmap plotter - and counts plots per frame in an IRQ
// Plots a fullscreen elipsis
#include <c64.c>
#include <sinus.c>
#include <multiply.c>
#include <bitmap2.c>
#include <c64.h>
#include <sinus.h>
#include <multiply.h>
#include <bitmap2.h>
byte* BITMAP = 0x2000;
byte* SCREEN = 0x0400;

View File

@ -1,9 +1,9 @@
// Tests the simple bitmap plotter - and counts plots per frame in an IRQ
// Plots a spiral
#include <c64.c>
#include <sinus.c>
#include <multiply.c>
#include <bitmap2.c>
#include <c64.h>
#include <sinus.h>
#include <multiply.h>
#include <bitmap2.h>
byte* BITMAP = 0x2000;
byte* SCREEN = 0x0400;

View File

@ -1,8 +1,8 @@
// Tests the simple bitmap plotter
// Plots a few lines using the bresenham line algorithm
#include <c64.c>
#include <bitmap2.c>
#include <print.c>
#include <c64.h>
#include <bitmap2.h>
#include <print.h>
byte* BITMAP = 0x2000;
byte* SCREEN = 0x0400;

View File

@ -1,6 +1,6 @@
// Tests the different standard C types
#include <print.c>
#include <print.h>
void main() {
print_cls();

View File

@ -1,5 +1,5 @@
// C64DTV 8bpp charmode stretcher
#include <c64dtv.c>
#include <c64dtv.h>
// Plane with the screen
byte* const SCREEN = $7c00;

View File

@ -1,5 +1,5 @@
// C64DTV 8bpp charmode stretcher
#include <c64dtv.c>
#include <c64dtv.h>
// Plane with all pixels
byte* const CHUNKY = $8000;

View File

@ -1,6 +1,6 @@
// Fill a box on the screen using the blitter
#include <c64dtv.c>
#include <c64dtv.h>
byte* const SCREEN = $400;
const byte SRCA[] = "camelot rules!";

View File

@ -1,4 +1,4 @@
#include <c64dtv.c>
#include <c64dtv.h>
byte* const SCREEN = $400;
const byte SRCA[] = { 'c', 'a', 'm', 'e', 'l', 'o', 't', '!', ' '};

View File

@ -1,6 +1,6 @@
// Test C64DTV v2 256-colors and the 16-color redefinable palette
#include <c64dtv.c>
#include <c64dtv.h>
void main() {
asm { sei }

View File

@ -1,8 +1,8 @@
// Interactive Explorer for C64DTV Screen Modes
#include <c64dtv.c>
#include <print.c>
#include <keyboard.c>
#include <bitmap-draw.c>
#include <c64dtv.h>
#include <print.h>
#include <keyboard.h>
#include <bitmap-draw.h>
void main() {
asm { sei } // Disable normal interrupt (prevent keyboard reading glitches and allows to hide basic/kernal)

View File

@ -1,8 +1,8 @@
// Exploring C64DTV Screen Modes
#include <c64dtv.c>
#include <print.c>
#include <keyboard.c>
#include <bitmap-draw.c>
#include <c64dtv.h>
#include <print.h>
#include <keyboard.h>
#include <bitmap-draw.h>
void main() {
asm { sei } // Disable normal interrupt (prevent keyboard reading glitches and allows to hide basic/kernal)

View File

@ -1,8 +1,8 @@
// Counting cycles using a CIA timer
#include <c64.c>
#include <time.c>
#include <print.c>
#include <c64.h>
#include <time.h>
#include <print.h>
byte* const SCREEN = 0x0400;

View File

@ -1,8 +1,8 @@
// Setup and run a simple CIA-timer
#include <c64.c>
#include <time.c>
#include <print.c>
#include <c64.h>
#include <time.h>
#include <print.h>
byte* const SCREEN = 0x0400;

View File

@ -1,9 +1,9 @@
// Clears start screen throwing around the letters (by turning them into sprites)
#include <stdlib.c>
#include <sqr.c>
#include <atan2.c>
#include <multiply.c>
#include <c64.c>
#include <stdlib.h>
#include <sqr.h>
#include <atan2.h>
#include <multiply.h>
#include <c64.h>
// Generate debug code (raster time usage etc.)
const bool DEBUG = false;

View File

@ -1,6 +1,6 @@
// Includes a system library - ignores the local file with the same name
#include <string.c>
#include <string.h>
char* STR = "camelot!";

View File

@ -0,0 +1,14 @@
// A file using a library
#include "lib.h"
const char* SCREEN = 0x0400;
void main() {
char i = 0;
SCREEN[i++] = value;
value = 'x';
SCREEN[i++] = value;
}

View File

@ -0,0 +1,6 @@
// The library containing the char value definition
#include "lib.h"
// The char value
char value = 'a';

View File

@ -0,0 +1,4 @@
// Library declaring a char value
// The char value
extern char value;

View File

@ -1,8 +1,8 @@
// Display MEDUSA PETSCII by Buzz_clik
// https://csdb.dk/release/?id=178673
#include <c64.c>
#include <string.c>
#include <c64.h>
#include <string.h>
char MEDUSA_SCREEN[1000] = kickasm(resource "medusas.prg" ) {{
.var fileScreen = LoadBinary("medusas.prg", BF_C64FILE)

View File

@ -1,10 +1,10 @@
// Pre-calculated bobs inside a charset (pre-moved to all x/y-combinations)
#include <c64.c>
#include <string.c>
#include <keyboard.c>
#include <time.c>
#include <print.c>
#include <fastmultiply.c>
#include <c64.h>
#include <string.h>
#include <keyboard.h>
#include <time.h>
#include <print.h>
#include <fastmultiply.h>
// The prototype BOB (a 3x3 char image with a bob image in the upper 2x2 chars)
// The chars are layout as follows with data in chars 0, 1, 3, 4 initially

View File

@ -1,10 +1,10 @@
// Pre-calculated bobs inside a charset (pre-moved to all x/y-combinations)
#include <c64.c>
#include <string.c>
#include <keyboard.c>
#include <time.c>
#include <print.c>
#include <fastmultiply.c>
#include <c64.h>
#include <string.h>
#include <keyboard.h>
#include <time.h>
#include <print.h>
#include <fastmultiply.h>
// The prototype BOB (a 3x3 char image with a bob image in the upper 2x2 chars)
// The chars are layout as follows with data in chars 0, 1, 3, 4 initially

View File

@ -1,9 +1,9 @@
// Same animation using a multiplexer
#include <c64.c>
#include <multiplexer.c>
#include <fastmultiply.c>
#include <string.c>
#include <keyboard.c>
#include <c64.h>
#include <multiplexer.h>
#include <fastmultiply.h>
#include <string.h>
#include <keyboard.h>
// The BOB sprite
align(0x1000) char SPRITE[] = kickasm(resource "smiley.png") {{

View File

@ -1,11 +1,11 @@
// Show a few simple splines using the splines library
#include "splines.c"
#include <bitmap2.c>
#include <time.c>
#include <print.c>
#include <fastmultiply.c>
#include <c64.c>
#include <bitmap2.h>
#include <time.h>
#include <print.h>
#include <fastmultiply.h>
#include <c64.h>
char* const PRINT_SCREEN = 0x0400;
char* const BITMAP_SCREEN = 0x5c00;

View File

@ -1,6 +1,6 @@
// Put a 2x2 font into sprites and show it on screen
#include <c64.c>
#include <multiplexer.c>
#include <c64.h>
#include <multiplexer.h>
char * const CHARSET_DEFAULT = 0x1000;
char * const SPRITES = 0x3000;

View File

@ -1,6 +1,6 @@
// Tetris Game for the Commodore 64
// A sprite multiplexer covering the playfield with a black layer to allow for 3 single-pixel colors
#include <c64.c>
#include <c64.h>
#include "tetris-data.c"
kickasm(pc PLAYFIELD_SPRITES, resource "playfield-sprites.png") {{

View File

@ -2,8 +2,8 @@
// The tetris game tries to match NES tetris gameplay pretty closely
// Source: https://meatfighter.com/nintendotetrisai/
#include <c64.c>
#include <keyboard.c>
#include <c64.h>
#include <keyboard.h>
#include "sid.c"
#include "tetris-data.c"
#include "tetris-render.c"

View File

@ -1,5 +1,5 @@
#include <c64.c>
#include <print.c>
#include <c64.h>
#include <print.h>
byte* SCREEN = $0400;

View File

@ -2,7 +2,7 @@
// Each function of the kernal is a no-args function
// The functions are placed in the SYSCALLS table surrounded by JMP and NOP
#include <string.c>
#include <string.h>
#pragma link("xmega65.ld")

View File

@ -1,4 +1,4 @@
#include <print.c>
#include <print.h>
byte* const BGCOL = $d021;
const byte GREEN = 5;
const byte RED = 2 ;

View File

@ -2,9 +2,9 @@
// See http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf
#include "font-hex.c"
#include <atan2.c>
#include <c64.c>
#include <print.c>
#include <atan2.h>
#include <c64.h>
#include <print.h>
byte* const CHARSET = 0x2000;
byte* const SCREEN = 0x2800;

View File

@ -2,8 +2,8 @@
// See http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf
#include "font-hex.c"
#include <atan2.c>
#include <c64.c>
#include <atan2.h>
#include <c64.h>
byte* const CHARSET = 0x2000;
byte* const SCREEN = 0x2800;

View File

@ -1,8 +1,8 @@
// Find atan2(x, y) using the CORDIC method
// See http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf
#include "font-hex.c"
#include <atan2.c>
#include <c64.c>
#include <atan2.h>
#include <c64.h>
byte* const CHARSET = 0x2000;
byte* const SCREEN = 0x2800;

View File

@ -2,8 +2,8 @@
// See http://bsvi.ru/uploads/CORDIC--_10EBA/cordic.pdf
#include "font-hex.c"
#include <c64.c>
#include <atan2.c>
#include <c64.h>
#include <atan2.h>
byte* const CHARSET = 0x2000;

View File

@ -2,7 +2,7 @@
// Missing definition
// The definition
extern char * const SCREEN = 0x0400;
extern char * const SCREEN;
//Second definition
char idx;

View File

@ -1,7 +1,7 @@
// Tests problem writing/reading joystick encountered by Danny Spijksma
// https://www.protovision.games/hardw/build4player.php?language=en&fbclid=IwAR1MJLgQjOU0zVa0ax2aNeGa-xVbE9IGY9zC6b6eInTV4HQzoUAoCPoXu14
#include <c64.c>
#include <c64.h>
char* const SCREEN = 0x0400;
void main() {
(*CIA2_PORT_B) &= 0x7f;

View File

@ -1,6 +1,6 @@
// Show default font on screen
#include <string.c>
#include <string.h>
byte* SCREEN = 0x0400;

View File

@ -4,7 +4,7 @@
* Based on facebook post from
*/
#include <print.c>
#include <print.h>
void main () {
print_cls();

View File

@ -2,8 +2,8 @@
// Based on:
// - C= Hacking Magazine Issue 8. http://www.ffd2.com/fridge/chacking/c=hacking8.txt
// - Codebase64 Article http://codebase64.org/doku.php?id=base:3d_rotation
#include <c64.c>
#include <print.c>
#include <c64.h>
#include <print.h>
// The rotated point - updated by calling rotate_matrix()
signed char* xr = $f0;

View File

@ -2,8 +2,8 @@
// Based on:
// - C= Hacking Magazine Issue 8. http://www.ffd2.com/fridge/chacking/c=hacking8.txt
// - Codebase64 Article http://codebase64.org/doku.php?id=base:3d_rotation
#include <c64.c>
#include <print.c>
#include <c64.h>
#include <print.h>
// The rotated point - updated by calling rotate()
signed char* xr = $f0;

View File

@ -1,5 +1,5 @@
#include <c64.c>
#include <bitmap-draw.c>
#include <c64.h>
#include <bitmap-draw.h>
char* const SCREEN = $400;
char* const BITMAP = $2000;

View File

@ -1,6 +1,6 @@
// Allows analysis of the CHARGEN ROM font
#include <c64.c>
#include <keyboard.c>
#include <c64.h>
#include <keyboard.h>
char* SCREEN = $400;

View File

@ -6,7 +6,7 @@
// - http://codebase64.org/doku.php?id=base:seriously_fast_multiplication
// - http://codebase64.org/doku.php?id=magazines:chacking16
#include <print.c>
#include <print.h>
signed char vals[] = {-95, -64, -32, -16, 0, 16, 32, 64, 95};

View File

@ -4,7 +4,7 @@
// Ported to KickC by Jesper Gravgaard.
// Original source https://github.com/cc65/cc65/blob/master/samples/fire.c
#include <c64.c>
#include <c64.h>
#include "sid.c"
unsigned char* SCREEN1 = 0x3800;

View File

@ -1,7 +1,7 @@
// Creates a 2x2 font from the system CHARGEN font and compress it by identifying identical chars
#include <c64.c>
#include <string.c>
#include <c64.h>
#include <string.h>
char* const SCREEN = 0x0400;
char* const FONT_ORIGINAL = 0x2000;

View File

@ -1,4 +1,5 @@
#include <print.c>
#include <print.h>
void main() {
print_str("hello world!");
print_ln();

View File

@ -1,5 +1,5 @@
// A raster IRQ that opens the top/bottom border.
#include <c64.c>
#include <c64.h>
char* const GHOST_BYTE = $3fff;

View File

@ -3,8 +3,8 @@
// To execute the program succesfully you must mount the D64 disk image and execute the kernalload.PRG program
#pragma link("kernalload.ld")
#include <string.c>
#include <c64.c>
#include <string.h>
#include <c64.h>
// Sprite file
#pragma data_seg(Sprite)

Some files were not shown because too many files have changed in this diff Show More