c64: fullscrollgame-sdcc.c for sdcc4, fixed source line offset bug, test

This commit is contained in:
Steven Hugg committed 2026-10-03 15:42:54 +02:00
1 parent 83f5bcf256
commit 344cdd21f9
9 files changed
+60 -8

No files matched your search

+2 -1
View File
@@ -55,7 +55,8 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md).
### Build tools
- Many toolchains are now WASI builds: cc65 (pinned to 2021 6ac4aa4), DASM 2.20.17, acme, zmac, yasm and merlin32.
- SDCC 4 mos6502 backend: C64 presets (`hello-sdcc.c`, `screen_ram-sdcc.c`, `joymove-sdcc.c`, `siegegame-sdcc.c`, `sprite_collision-sdcc.c`); `load_header` platform param wraps the image as a DOS 3.3 binary (apple2) or a PRG with a `SYS` stub (c64).
- SDCC 4 mos6502 backend: C64 presets (`hello-sdcc.c`, `screen_ram-sdcc.c`, `joymove-sdcc.c`, `siegegame-sdcc.c`, `sprite_collision-sdcc.c`, `fullscrollgame-sdcc.c`); `load_header` platform param wraps the image as a DOS 3.3 binary (apple2) or a PRG with a `SYS` stub (c64).
- C64 `fullscrollgame.c` now takes its level data from `level2-data.c` (the same data as `level2.s`, in C) so the SDCC build can link it; `scrolling2.c` uses plain C copies under SDCC.
- Updated batari BASIC; sdcc now parses `#symbol` directives and fixes lost link errors.
- New build directives replace the legacy CFGFILE-style ones; build parameters no longer leak between builds.
- `#pragma compile("...")` and oscar64 symbol/listing parsing.
+17
View File
@@ -0,0 +1,17 @@
// fullscrollgame.c built with SDCC's mos6502 backend instead of cc65.
// The program is fullscrollgame.c; common.h supplies the C64 definitions SDCC lacks.
// (The C files are #included, since cc65 and SDCC objects can't be linked together,
// and the level data from level2.s is converted to C in level2-data.c.)
#include "common.h"
#include "common.c"
#include "scrolling.h"
#include "scrolling2.c"
#include "sprites.h"
#include "sprites.c"
#include "level2-data.c"
#include "fullscrollgame.c"
+3 -1
View File
@@ -1,4 +1,5 @@
#ifndef __SDCC
#include <stdio.h>
#include <conio.h>
#include <c64.h>
@@ -7,6 +8,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <joystick.h>
#endif
//#resource "c64-sid.cfg"
#define CFGFILE c64-sid.cfg
@@ -20,7 +22,7 @@
#include "sprites.h"
//#link "sprites.c"
//#link "level2.s"
//#link "level2-data.c"
#define CAMERA_OFFSET_X 158
#define CAMERA_OFFSET_Y 120
+10 -4
View File
@@ -43,6 +43,15 @@ void copy_color_ram_slow() {
memcpy(COLOR_RAM, colorbuf, COLS*ROWS);
}
void copy_to_hidden_buffer_slow() {
memcpy(hidbuf, visbuf, COLS*ROWS);
}
#ifdef __SDCC
// (SDCC build: no cc65 inline assembly, so use the plain C copies)
void copy_color_ram_fast() { copy_color_ram_slow(); }
void copy_to_hidden_buffer_fast() { copy_to_hidden_buffer_slow(); }
#else
void copy_color_ram_fast() {
// fast copy loop for upper 1/2 of color ram
asm("ldy #0");
@@ -63,10 +72,6 @@ void copy_color_ram_fast() {
asm("bne @loop2");
}
void copy_to_hidden_buffer_slow() {
memcpy(hidbuf, visbuf, COLS*ROWS);
}
void copy_to_hidden_buffer_fast() {
// self-modifying code
asm("ldy %v+1", visbuf);
@@ -101,6 +106,7 @@ void copy_to_hidden_buffer_fast() {
asm("@skip: iny");
asm("bne @loop");
}
#endif
void scroll_start(byte dir) {
if (scroll_seq == 0 && dir) {
+1
View File
@@ -48,6 +48,7 @@ const C64_PRESETS : Preset[] = [
{id:'lander.cpp', name:'Lunar Lander'},
{id:'hello-sdcc.c', name:'Hello World (SDCC)', category:'C (SDCC)'},
{id:'sprite_collision-sdcc.c', name:'Sprite Collision (SDCC)'},
{id:'fullscrollgame-sdcc.c', name:'Full-Scrolling Game (SDCC)'},
];
const C64_MEMORY_MAP = { main:[
+4 -1
View File
@@ -102,6 +102,7 @@ export function parseListing(code: string,
export function parseSourceLines(code: string, lineMatch, offsetMatch, funcMatch?, segMatch?) {
var lines = [];
var lastlinenum = 0;
var lastpath: string;
var state = { segment: '', func: '', funcbase: 0 };
for (var line of code.split(re_crlf)) {
parseSegFunc(line, segMatch, funcMatch, state);
@@ -109,6 +110,7 @@ export function parseSourceLines(code: string, lineMatch, offsetMatch, funcMatch
var linem = lineMatch.exec(line);
if (linem && linem[1]) {
lastlinenum = parseInt(linem[1]);
lastpath = linem[2]; // optional source file, for listings that mix files
} else if (lastlinenum) {
var linem = offsetMatch.exec(line);
if (linem && linem[1]) {
@@ -117,7 +119,8 @@ export function parseSourceLines(code: string, lineMatch, offsetMatch, funcMatch
line: lastlinenum,
offset: offset - state.funcbase,
segment: state.segment,
func: state.func
func: state.func,
...(lastpath ? { path: lastpath } : {})
});
lastlinenum = 0;
}
+7
View File
@@ -559,6 +559,13 @@ export function parseRSTListing(rstout: string, srcprefix: string) {
// the mos6502 backend writes `;<tab>file.c: 17: source`, with a space after the colon
const srcre = new RegExp(`^\\s+\\d+ ;\\s*(?:<stdin>|/*${name}\\.[^:\\s]+):\\s*(\\d+):`, 'i');
var srclines = parseSourceLines(rstout, srcre, /^\s*([0-9A-F]{4,8})/i);
if (!srclines.length) {
// the mos6502 backend names the file of every line, so a main file
// that #includes the others (the -sdcc.c wrappers) still gets a
// listing: tag each line with its file (see processListings);
// the pattern captures the line number first, then the file name
srclines = parseSourceLines(rstout, /^\s+\d+ ;\t(?=[^\s:]+: (\d+):)([^\s:]+)/, /^\s*([0-9A-F]{4,8})/i);
}
// TODO: you have to get rid of all source lines to get asm listing
return {
asmlines: srclines.length ? asmlines : null,
+1 -1
View File
@@ -132,7 +132,7 @@ describe('8bws CLI', function () {
assert.equal(bin[2] | bin[3] << 8, bin.length - 4);
assert.ok(bin.length > 4000); // the whole game, not just the wrapper
});
['hello', 'screen_ram', 'joymove', 'siegegame', 'sprite_collision'].forEach(function (name) {
['hello', 'sprite_collision'].forEach(function (name) {
it('should build the c64 ' + name + ' SDCC preset as a PRG', function () {
var out = path.join(os.tmpdir(), '8bws-c64-' + name + '-sdcc.prg');
cli('build', '-p', 'c64', 'presets/c64/' + name + '-sdcc.c', '-o', out);
+15
View File
@@ -28,8 +28,23 @@ const RST_MOS6502 = [
" 00000869 20 4E 08 [ 6] 138 \tjsr\t_putstr",
].join("\n");
// a main file that only #includes the others (e.g. fullscrollgame-sdcc.c)
const RST_MOS6502_INCLUDES = [
" 00000865 126 _main:",
" 128 ;\tgame.c: 18: putstr(message);",
" 00000865 A2 08 [ 2] 136 \tldx\t#>_message",
" 140 ;\tlib.c: 4: x++;",
" 00000869 E6 02 [ 5] 142 \tinc\t*_x",
].join("\n");
describe("sdcc listings", function () {
it("tags mos6502 lines with the included file they came from", function () {
const l = parseRSTListing(RST_MOS6502_INCLUDES, "game-sdcc");
assert.deepStrictEqual(l.lines.map((x) => [x.path, x.line, x.offset]),
[["game.c", 18, 0x865], ["lib.c", 4, 0x869]]);
});
it("maps mos6502 source comments", function () {
const l = parseRSTListing(RST_MOS6502, "hello");
assert.deepStrictEqual(l.lines.map((x) => [x.line, x.offset]), [[17, 0x865], [18, 0x865]]); // the function line maps to its label