mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-24 08:02:27 +00:00
sdcc: added apple2 and c64 support for sdcc 4; serial command for devel-6502; fixed test
This commit is contained in:
1 parent
17baa12308
commit
55709205c1
30 files changed
+487
-26
No files matched your search
+4
-1
@@ -55,6 +55,7 @@ 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).
|
||||
- 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.
|
||||
@@ -64,7 +65,9 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md).
|
||||
- cc65 `a2.lo.s` (Apple II lo-res TGI) patched in `8bitworkshop-compilers/patches/cc65-apple2-lo-tgi.patch`: a stray `bpl :+` in `INSTALL` made `tgi_install(a2_lo_tgi)` crash on a non-enhanced Apple II.
|
||||
- Presets adjusted for the newer cc65: NES `chase` closes its zero-page `#pragma` region, C64 `side_scroller` uses an unsigned fixed-point constant, and the Atari vector presets no longer recurse into `main()`. `neslib.h` no longer redefines `NULL` if `stddef.h` already did.
|
||||
- SDCC: both 3.6.5 and 4.x are bundled; 3.6.5 is the default (several times faster to build) and `//#tooldef c sdcc=4` opts in to 4.x (the default is `SDCC_DEFAULT_VERSION` in `toolmeta.ts`). Programs without an `opt_code*` pragma now build with `--no-peep --nolospre --max-allocs-per-node 500`, including Game Boy, which previously always got the full optimizer. See *SDCC versions* in the build directives docs.
|
||||
- SDCC 4 mos6502 backend (experimental): a `-sdcc.c` file on `devel-6502` (see `presets/devel-6502/hello-sdcc.c`) builds with sdcc, `sdas6500` and `sdld6808`. Apple II and other 6502 platforms need their own crt0 and memory map.
|
||||
- SDCC 4 mos6502 backend (experimental): a `.sdcc` or `-sdcc.c` file on `devel-6502` or `apple2` builds with sdcc, `sdas6500` and `sdld6808` (see `hello-sdcc.c` in those presets). Apple II programs link as a DOS 3.3 binary at `$803`. `.sdcc` now selects SDCC on every Z80 and 6502 platform.
|
||||
- `//#tooldef ld code_start=ADDR` and `data_start=ADDR` move SDCC's code and RAM data. The Apple II preset `cosmic-sdcc.c` builds the Cosmic Impalas game with SDCC by including `cosmic.c`, which now has `__SDCC` alternatives for `conio` and inline assembly.
|
||||
- `devel-6502` runs headless in the `8bws` CLI, and the new `serial` run-script command prints what the program sent to its serial port.
|
||||
- Build is now only `esbuild` through `scripts/build.mjs`; code split into a headless `src/common/projectcore.ts` / `toolselect.ts`.
|
||||
- New `buildpresets` script that builds and runs every preset.
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Cosmic Impalas built with SDCC's mos6502 backend instead of cc65.
|
||||
// The game is cosmic.c; this file picks the compiler and memory layout.
|
||||
|
||||
// the hires page 1 buffer is $2000-$3FFF, so the program loads above it
|
||||
//#tooldef ld code_start=0x4000
|
||||
|
||||
#include "cosmic.c"
|
||||
@@ -1,8 +1,10 @@
|
||||
|
||||
// reserve space for the HGR1 screen buffer
|
||||
//#tooldef ld cfgfile=apple2-hgr.cfg
|
||||
#ifndef __SDCC
|
||||
#pragma data-name(push,"HGR")
|
||||
#pragma data-name(pop)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An Apple ][ port of the Cosmic Impalas game
|
||||
@@ -11,9 +13,11 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#ifndef __SDCC
|
||||
#include <conio.h>
|
||||
#include <apple2.h>
|
||||
#include <peekpoke.h>
|
||||
#endif
|
||||
|
||||
// type aliases for byte/signed byte/unsigned 16-bit
|
||||
typedef unsigned char byte;
|
||||
@@ -21,7 +25,18 @@ typedef signed char sbyte;
|
||||
typedef unsigned short word;
|
||||
|
||||
// peeks, pokes, and strobes
|
||||
#ifdef __SDCC
|
||||
// (building with sdcc, see cosmic-sdcc.c: no conio, so read the keyboard directly)
|
||||
#define STROBE(addr) ((void)*(volatile byte*)(addr))
|
||||
byte kbhit(void) { return *(volatile byte*)0xc000 & 0x80; }
|
||||
char cgetc(void) {
|
||||
char key = *(volatile byte*)0xc000 & 0x7f;
|
||||
STROBE(0xc010); // clear the keyboard strobe
|
||||
return key;
|
||||
}
|
||||
#else
|
||||
#define STROBE(addr) __asm__ ("sta %w", addr)
|
||||
#endif
|
||||
|
||||
// speaker click
|
||||
#define CLICK STROBE(0xc030)
|
||||
@@ -232,11 +247,24 @@ void draw_bcd_word(word bcd, byte col, byte row, byte vert) {
|
||||
|
||||
// add two 4-digit BCD words
|
||||
word bcd_add(word a, word b) {
|
||||
#ifdef __SDCC
|
||||
// add one digit at a time, in plain C
|
||||
word result = 0;
|
||||
byte shift, carry = 0;
|
||||
for (shift = 0; shift < 16; shift += 4) {
|
||||
byte d = ((a >> shift) & 0xf) + ((b >> shift) & 0xf) + carry;
|
||||
carry = d > 9;
|
||||
if (carry) d -= 10;
|
||||
result |= (word)d << shift;
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
word result;
|
||||
__asm__ ("sed"); // set decimal (BCD) mode
|
||||
result = a+b;
|
||||
__asm__ ("cld"); // clear BCD mode
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Hello World in C for the Apple ][ using SDCC's mos6502 backend.
|
||||
// (The -sdcc.c suffix selects SDCC instead of cc65.) The program is linked
|
||||
// as a DOS 3.3 binary at $803; there is no C library or conio, so we poke
|
||||
// the text screen directly.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define TEXT_PAGE1 ((volatile uint8_t *)0x400)
|
||||
|
||||
const char message[] = "HELLO, SDCC 6502!";
|
||||
|
||||
uint8_t row = 10; // initialized data is copied to RAM at startup
|
||||
|
||||
// text rows are interleaved: three groups of 8 rows, 40 bytes apart,
|
||||
// each row of a group $80 bytes after the previous
|
||||
volatile uint8_t *row_address(uint8_t r) {
|
||||
return TEXT_PAGE1 + (r & 7) * 0x80 + (r >> 3) * 40;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
uint8_t i, r;
|
||||
volatile uint8_t *p;
|
||||
for (r = 0; r < 24; r++) { // clear the screen
|
||||
p = row_address(r);
|
||||
for (i = 0; i < 40; i++) p[i] = 0xa0;
|
||||
}
|
||||
p = row_address(row) + 11;
|
||||
for (i = 0; message[i]; i++)
|
||||
p[i] = message[i] | 0x80; // normal video: high bit set
|
||||
while (1);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Minimal C64 definitions for SDCC's mos6502 backend, which has no c64.h,
|
||||
// conio.h or joystick.h like cc65 does. Included by common.h under __SDCC.
|
||||
|
||||
#ifndef _C64_SDCC_H
|
||||
#define _C64_SDCC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PEEK(addr) (*(volatile uint8_t*)(addr))
|
||||
#define POKE(addr,val) (*(volatile uint8_t*)(addr) = (val))
|
||||
|
||||
#define COLOR_BLACK 0
|
||||
#define COLOR_WHITE 1
|
||||
#define COLOR_RED 2
|
||||
#define COLOR_CYAN 3
|
||||
#define COLOR_PURPLE 4
|
||||
#define COLOR_GREEN 5
|
||||
#define COLOR_BLUE 6
|
||||
#define COLOR_YELLOW 7
|
||||
#define COLOR_ORANGE 8
|
||||
#define COLOR_BROWN 9
|
||||
#define COLOR_LIGHTRED 10
|
||||
#define COLOR_GRAY1 11
|
||||
#define COLOR_GRAY2 12
|
||||
#define COLOR_LIGHTGREEN 13
|
||||
#define COLOR_LIGHTBLUE 14
|
||||
#define COLOR_GRAY3 15
|
||||
|
||||
// VIC-II registers, same field names as cc65's struct __vic2
|
||||
struct __vic2 {
|
||||
struct { uint8_t x, y; } spr_pos[8];
|
||||
uint8_t spr_hi_x, ctrl1, rasterline, strobe_x, strobe_y;
|
||||
uint8_t spr_ena, ctrl2, spr_exp_y, addr, irr, imr;
|
||||
uint8_t spr_bg_prio, spr_mcolor, spr_exp_x, spr_coll, spr_bg_coll;
|
||||
uint8_t bordercolor, bgcolor0, bgcolor1, bgcolor2, bgcolor3;
|
||||
uint8_t spr_mcolor0, spr_mcolor1;
|
||||
uint8_t spr_color[8];
|
||||
};
|
||||
|
||||
// 6526 CIA registers
|
||||
struct __6526 {
|
||||
uint8_t pra, prb, ddra, ddrb, ta_lo, ta_hi, tb_lo, tb_hi;
|
||||
uint8_t tod_10, tod_sec, tod_min, tod_hour, sdr, icr, cra, crb;
|
||||
};
|
||||
|
||||
#define VIC (*(volatile struct __vic2*)0xd000)
|
||||
#define CIA1 (*(volatile struct __6526*)0xdc00)
|
||||
#define CIA2 (*(volatile struct __6526*)0xdd00)
|
||||
#define COLOR_RAM ((volatile uint8_t*)0xd800)
|
||||
|
||||
// joystick: bits are active low on CIA1 (port 2 = pra, port 1 = prb)
|
||||
#define JOY_UP_MASK 0x01
|
||||
#define JOY_DOWN_MASK 0x02
|
||||
#define JOY_LEFT_MASK 0x04
|
||||
#define JOY_RIGHT_MASK 0x08
|
||||
#define JOY_BTN_1_MASK 0x10
|
||||
#define JOY_UP(v) ((v) & JOY_UP_MASK)
|
||||
#define JOY_DOWN(v) ((v) & JOY_DOWN_MASK)
|
||||
#define JOY_LEFT(v) ((v) & JOY_LEFT_MASK)
|
||||
#define JOY_RIGHT(v) ((v) & JOY_RIGHT_MASK)
|
||||
#define JOY_BTN_1(v) ((v) & JOY_BTN_1_MASK)
|
||||
#define joy_install(drv)
|
||||
#define joy_static_stddrv 0
|
||||
// (the KERNAL's keyboard scan also drives these ports, so stop it while reading)
|
||||
static uint8_t joy_read(uint8_t port) {
|
||||
uint8_t v;
|
||||
__asm__("sei");
|
||||
if (port) { CIA1.ddra = 0; v = CIA1.pra; CIA1.ddra = 0xff; } // joystick 2
|
||||
else { CIA1.ddrb = 0; v = CIA1.prb; } // joystick 1
|
||||
__asm__("cli");
|
||||
return ~v & 0x1f;
|
||||
}
|
||||
|
||||
// conio-like text output straight to screen RAM (screen codes, not PETSCII)
|
||||
static uint8_t _cx, _cy, _ccolor = COLOR_LIGHTBLUE, _crev;
|
||||
|
||||
static void clrscr(void) {
|
||||
uint16_t i;
|
||||
for (i = 0; i < 1000; i++) POKE(0x400 + i, 32);
|
||||
_cx = _cy = 0;
|
||||
}
|
||||
|
||||
static uint8_t _screencode(char c) {
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return c & 0x1f;
|
||||
if (c == '#') return 0xa3; // the uppercase character set has no plain '#' glyph; use reverse video
|
||||
return c; // digits, punctuation and space match ASCII
|
||||
}
|
||||
|
||||
static void textcolor(uint8_t c) { _ccolor = c; }
|
||||
static uint8_t revers(uint8_t on) { uint8_t old = _crev; _crev = on ? 0x80 : 0; return old; }
|
||||
|
||||
static void cputcxy(uint8_t x, uint8_t y, char c) {
|
||||
uint16_t ofs = y * 40 + x;
|
||||
POKE(0x400 + ofs, _screencode(c) | _crev);
|
||||
POKE(0xd800 + ofs, _ccolor);
|
||||
}
|
||||
|
||||
static void cputsxy(uint8_t x, uint8_t y, const char* s) {
|
||||
while (*s) cputcxy(x++, y, *s++);
|
||||
}
|
||||
|
||||
static uint8_t kbhit(void) { return PEEK(0xc6); } // KERNAL keyboard buffer count
|
||||
|
||||
#endif
|
||||
+15
-1
@@ -1,12 +1,17 @@
|
||||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#ifdef __SDCC
|
||||
// SDCC has no cc65 headers, so use our own subset (see c64-sdcc.h)
|
||||
#include "c64-sdcc.h"
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <peekpoke.h>
|
||||
#include <string.h>
|
||||
#include <c64.h>
|
||||
#endif
|
||||
|
||||
#ifdef __CC65__
|
||||
#include <conio.h>
|
||||
@@ -16,7 +21,13 @@
|
||||
typedef uint8_t byte; // 8-bit unsigned
|
||||
typedef int8_t sbyte; // 8-bit signed
|
||||
typedef uint16_t word; // 16-bit unsigned
|
||||
#ifdef __SDCC
|
||||
typedef uint8_t bool;
|
||||
#define false 0
|
||||
#define true 1
|
||||
#else
|
||||
typedef enum { false, true } bool; // boolean
|
||||
#endif
|
||||
|
||||
#define COLS 40 // total # of columns
|
||||
#define ROWS 25 // total # of rows
|
||||
@@ -127,7 +138,7 @@ char* get_screen_memory();
|
||||
char __fastcall__ poll_keyboard();
|
||||
#endif
|
||||
|
||||
#ifndef __CC65__
|
||||
#if !defined(__CC65__) && !defined(__SDCC)
|
||||
inline void clrscr() {
|
||||
__asm__ volatile ("jsr $E544" : : : "a","x","y"); // regs clobbered
|
||||
}
|
||||
@@ -135,6 +146,9 @@ inline void waitvsync() {
|
||||
raster_wait(255);
|
||||
}
|
||||
#endif
|
||||
#ifdef __SDCC
|
||||
static void waitvsync(void) { raster_wait(255); }
|
||||
#endif
|
||||
|
||||
// for use with set_irq()
|
||||
// sets up the VIC to send raster interrupts
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Hello World in C for the Commodore 64 using SDCC's mos6502 backend.
|
||||
// (The -sdcc.c suffix selects SDCC instead of cc65.) The program is a PRG
|
||||
// with a BASIC "SYS 2061" stub; there is no C library or conio, so we poke
|
||||
// screen RAM and color RAM directly.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SCREEN ((volatile uint8_t *)0x400)
|
||||
#define COLOR_RAM ((volatile uint8_t *)0xd800)
|
||||
#define BORDER (*(volatile uint8_t *)0xd020)
|
||||
#define BACKGROUND (*(volatile uint8_t *)0xd021)
|
||||
|
||||
// screen codes (not ASCII): letters are 1-26, space is 32
|
||||
const uint8_t message[] = { 8,5,12,12,15, 32, 19,4,3,3, 32, 54,53,48,50 };
|
||||
|
||||
uint8_t color = 7; // initialized data is copied to RAM at startup
|
||||
|
||||
void main(void) {
|
||||
uint16_t i;
|
||||
uint8_t c;
|
||||
BORDER = 6;
|
||||
BACKGROUND = 0;
|
||||
for (i = 0; i < 1000; i++) { // clear the screen
|
||||
SCREEN[i] = 32;
|
||||
COLOR_RAM[i] = 1;
|
||||
}
|
||||
for (c = 0; c < sizeof(message); c++) {
|
||||
SCREEN[12*40 + 12 + c] = message[c];
|
||||
COLOR_RAM[12*40 + 12 + c] = color;
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
@@ -20,6 +20,9 @@ void main(void) {
|
||||
byte bgcoll; // sprite background collision flags
|
||||
byte joy; // joystick flags
|
||||
|
||||
#ifdef __SDCC
|
||||
clrscr(); // (cc65's startup code clears the screen, SDCC's does not)
|
||||
#endif
|
||||
// copy sprite pattern to RAM address 0x3800
|
||||
memcpy((char*)0x3800, SPRITE_DATA, sizeof(SPRITE_DATA));
|
||||
// set sprite #0 shape entry (224)
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#include "common.h"
|
||||
//#link "common.c"
|
||||
|
||||
#ifndef __SDCC
|
||||
#include <cbm_screen_charmap.h>
|
||||
#endif
|
||||
|
||||
void main(void) {
|
||||
unsigned int i;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// sprite_collision.c built with SDCC's mos6502 backend instead of cc65.
|
||||
// The program is sprite_collision.c; common.h supplies the C64 definitions SDCC lacks.
|
||||
// (common.c and sprites.c are #included, since cc65 and SDCC objects can't be linked together.)
|
||||
|
||||
#include "common.h"
|
||||
#include "common.c"
|
||||
|
||||
#include "sprites.h"
|
||||
#include "sprites.c"
|
||||
|
||||
#include "sprite_collision.c"
|
||||
@@ -0,0 +1,22 @@
|
||||
processor 6502
|
||||
org $8000
|
||||
|
||||
Start
|
||||
; ADD YOUR CODE HERE
|
||||
rts
|
||||
|
||||
org $ffe0
|
||||
NMIVec
|
||||
RESVec
|
||||
BRKVec
|
||||
sei
|
||||
cld
|
||||
ldx #$ff
|
||||
txs
|
||||
jsr Start
|
||||
jmp .
|
||||
|
||||
org $fffa
|
||||
word NMIVec ; NMI
|
||||
word RESVec ; reset
|
||||
word BRKVec ; BRK
|
||||
@@ -523,7 +523,7 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
|
||||
return disassemble6502(pc, read(pc), read(pc + 1), read(pc + 2));
|
||||
}
|
||||
getToolForFilename = getToolForFilename_6502;
|
||||
getDefaultExtensions() { return [".c", ".cpp", ".o64", ".acme", ".xa", ".ca65", ".dasm", ".ecs", ".wiz"]; };
|
||||
getDefaultExtensions() { return [".c", ".sdcc", ".cpp", ".o64", ".acme", ".xa", ".ca65", ".dasm", ".ecs", ".wiz"]; };
|
||||
|
||||
getDebugCategories() {
|
||||
return ['CPU', 'ZPRAM', 'Stack'];
|
||||
@@ -643,7 +643,7 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform {
|
||||
}
|
||||
|
||||
getToolForFilename = getToolForFilename_z80;
|
||||
getDefaultExtensions() { return [".c", ".ns", ".s", ".scc", ".sgb", ".z", ".wiz"]; };
|
||||
getDefaultExtensions() { return [".c", ".sdcc", ".ns", ".s", ".scc", ".sgb", ".z", ".wiz"]; };
|
||||
// TODO: Z80 opcode metadata
|
||||
//this.getOpcodeMetadata = function() { }
|
||||
|
||||
|
||||
@@ -421,6 +421,12 @@ export class EmuCore {
|
||||
get frameRate(): number { return this.video ? this.video.frameRate : 60; }
|
||||
saveState(): EmuState | null { return this.platform.saveState ? this.platform.saveState() : null; }
|
||||
|
||||
/** Text a platform's program has sent to a serial port, or null if it has none. */
|
||||
getSerialOutput(): string | null {
|
||||
const p: any = this.platform;
|
||||
return typeof p.getSerialOutput === 'function' ? p.getSerialOutput() : null;
|
||||
}
|
||||
|
||||
getDebugInfo(): DebugSection[] {
|
||||
const state = this.saveState();
|
||||
const p: any = this.platform;
|
||||
|
||||
@@ -410,7 +410,7 @@ export const TOOL_META: { [id: string]: ToolMeta } = {
|
||||
|
||||
sdcc: {
|
||||
id: 'sdcc', name: 'SDCC', kind: 'compiler', arch: 'z80',
|
||||
extensions: ['.c', '.h'],
|
||||
extensions: ['.c', '.h', '.sdcc'],
|
||||
includeDirs: ['/share/sdcc/include', '/include'],
|
||||
editorStyle: 'text/x-csrc',
|
||||
helpURL: 'http://sdcc.sourceforge.net/doc/sdccman.pdf',
|
||||
|
||||
@@ -12,7 +12,7 @@ export type ToolSelector = (fn: string) => string;
|
||||
|
||||
export function getToolForFilename_6502(fn: string): string {
|
||||
if (fn.endsWith("-llvm.c")) return "remote:llvm-mos";
|
||||
if (fn.endsWith("-sdcc.c")) return "sdcc"; // SDCC 4.x mos6502 backend
|
||||
if (fn.endsWith("-sdcc.c") || fn.endsWith(".sdcc")) return "sdcc"; // SDCC 4.x mos6502 backend
|
||||
if (fn.endsWith(".c")) return "cc65";
|
||||
if (fn.endsWith(".h")) return "cc65";
|
||||
if (fn.endsWith(".s")) return "ca65";
|
||||
@@ -30,7 +30,7 @@ export function getToolForFilename_6502(fn: string): string {
|
||||
}
|
||||
|
||||
export function getToolForFilename_z80(fn: string): string {
|
||||
if (fn.endsWith(".c")) return "sdcc";
|
||||
if (fn.endsWith(".c") || fn.endsWith(".sdcc")) return "sdcc";
|
||||
if (fn.endsWith(".h")) return "sdcc";
|
||||
if (fn.endsWith(".s")) return "sdasz80";
|
||||
if (fn.endsWith(".sgb")) return "sdasgb";
|
||||
@@ -61,7 +61,6 @@ export function getToolForFilename_arm32(fn: string): string {
|
||||
export function getToolForFilename_vcs(fn: string): string {
|
||||
if (fn.endsWith(".cc2600")) return "cc2600";
|
||||
if (fn.endsWith("-llvm.c")) return "remote:llvm-mos";
|
||||
if (fn.endsWith("-sdcc.c")) return "sdcc"; // SDCC 4.x mos6502 backend
|
||||
if (fn.endsWith(".wiz")) return "wiz";
|
||||
if (fn.endsWith(".bb") || fn.endsWith(".bas")) return "bataribasic";
|
||||
if (fn.endsWith(".ca65")) return "ca65";
|
||||
|
||||
@@ -135,11 +135,13 @@ The supported parameters are:
|
||||
| --- | --- | --- |
|
||||
| `ld` | `cfgfile` | linker configuration file to use (e.g. for CC65's ld65) |
|
||||
| `ld` | `libargs` | comma-separated list of library/symbol arguments |
|
||||
| `ld` | `code_start`, `data_start` | where SDCC's linker places the code and the RAM data (e.g. `0x4000`) |
|
||||
| `c` | `sdcc` | SDCC version, `3` or `4` (see *SDCC versions* below) |
|
||||
|
||||
~~~c
|
||||
//#tooldef ld cfgfile=apple2-hgr2.cfg
|
||||
//#tooldef ld libargs=,nes.lib
|
||||
//#tooldef ld code_start=0x4000
|
||||
//#tooldef c sdcc=3
|
||||
~~~
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ const APPLE2_PRESETS: Preset[] = [
|
||||
// {id:'tb_6502.s', name:'Tom Bombem (assembler game)'},
|
||||
{ id: 'dos33bin.a', name: "DOS 3.3 Binary" },
|
||||
{ id: 'applesinglebin.a', name: "AppleSingle Binary" },
|
||||
{ id: 'hello-sdcc.c', name: 'Hello World (SDCC)', category: "C (SDCC)" },
|
||||
{ id: 'cosmic-sdcc.c', name: 'Cosmic Impalas (SDCC)' },
|
||||
];
|
||||
|
||||
/// MAME support
|
||||
@@ -51,7 +53,7 @@ class Apple2MAMEPlatform extends BaseMAME6502Platform implements Platform {
|
||||
}
|
||||
|
||||
getOpcodeMetadata = getOpcodeMetadata_6502;
|
||||
getDefaultExtensions() { return [".c", ".cpp", ".acme", ".xa", ".ca65", ".dasm", ".ecs", ".wiz"]; }
|
||||
getDefaultExtensions() { return [".c", ".sdcc", ".cpp", ".acme", ".xa", ".ca65", ".dasm", ".ecs", ".wiz"]; }
|
||||
getToolForFilename = getToolForFilename_apple2;
|
||||
|
||||
getPresets() { return APPLE2_PRESETS; }
|
||||
|
||||
@@ -46,6 +46,8 @@ const C64_PRESETS : Preset[] = [
|
||||
{id:'hello.cpp', name:'Hello Oscar64', category:'C++ (Oscar64)'},
|
||||
{id:'particles.cpp', name:'Fireworks'},
|
||||
{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)'},
|
||||
];
|
||||
|
||||
const C64_MEMORY_MAP = { main:[
|
||||
|
||||
+27
-4
@@ -12,7 +12,26 @@ var DEVEL_6502_PRESETS = [
|
||||
{id:'hello-sdcc.c', name:'Hello World (SDCC C)'},
|
||||
];
|
||||
|
||||
class SerialInOutViewer {
|
||||
/** What the serial harness draws on: the TeleType viewer, or nothing (headless). */
|
||||
interface SerialViewer {
|
||||
tty : { addtext(s: string, flags: number): void, newline(): void, clear(): void,
|
||||
saveState(): any, loadState(state: any): void };
|
||||
start(): void;
|
||||
reset(): void;
|
||||
saveState(): any;
|
||||
loadState(state: any): void;
|
||||
}
|
||||
|
||||
/** For the CLI and tests, which have no DOM: the output is kept by the harness. */
|
||||
class HeadlessSerialViewer implements SerialViewer {
|
||||
tty = { addtext() {}, newline() {}, clear() {}, saveState() { return {}; }, loadState() {} };
|
||||
start() {}
|
||||
reset() {}
|
||||
saveState() { return {}; }
|
||||
loadState() {}
|
||||
}
|
||||
|
||||
class SerialInOutViewer implements SerialViewer {
|
||||
div : HTMLElement;
|
||||
tty : TeleType;
|
||||
|
||||
@@ -48,7 +67,7 @@ function byteToASCII(b: number) : string {
|
||||
|
||||
export class SerialTestHarness implements SerialIOInterface {
|
||||
|
||||
viewer : SerialInOutViewer;
|
||||
viewer : SerialViewer;
|
||||
bufferedRead : boolean = true;
|
||||
cyclesPerByte = 1000000/(57600/8); // 138.88888 cycles
|
||||
maxOutputBytes = 4096;
|
||||
@@ -117,11 +136,12 @@ export class SerialTestHarness implements SerialIOInterface {
|
||||
class Devel6502Platform extends Base6502MachinePlatform<Devel6502> implements Platform {
|
||||
|
||||
serial : SerialTestHarness;
|
||||
serview : SerialInOutViewer;
|
||||
serview : SerialViewer;
|
||||
|
||||
constructor(mainElement: HTMLElement) {
|
||||
super(mainElement);
|
||||
this.serview = new SerialInOutViewer(mainElement);
|
||||
// no element: running headless (the 8bws CLI)
|
||||
this.serview = mainElement ? new SerialInOutViewer(mainElement) : new HeadlessSerialViewer();
|
||||
}
|
||||
|
||||
async start() {
|
||||
@@ -165,6 +185,9 @@ class Devel6502Platform extends Base6502MachinePlatform<Devel6502> implements Pl
|
||||
// TODO: reload tty UI
|
||||
}
|
||||
|
||||
/** Everything the program has sent to the serial port (for the CLI's `serial` command). */
|
||||
getSerialOutput() { return byteArrayToString(this.serial.outputBytes); }
|
||||
|
||||
newMachine() { return new Devel6502(); }
|
||||
getPresets() { return DEVEL_6502_PRESETS; }
|
||||
readAddress(a) { return this.machine.readConst(a); }
|
||||
|
||||
@@ -103,6 +103,8 @@ const SKIP_PLATFORMS = [
|
||||
'astrocade-arcade',
|
||||
'mcr',
|
||||
'vectrex',
|
||||
'devel',
|
||||
'devel-6502',
|
||||
];
|
||||
|
||||
// build these, but don't run them: the emulator needs a real browser
|
||||
|
||||
@@ -43,6 +43,7 @@ export const RUN_SCRIPT_HELP = [
|
||||
' keydown KEY / keyup KEY - raw key down/up events',
|
||||
' mem START [LEN] - hexdump memory (default 16 bytes)',
|
||||
' screen [START] [COLS] [ROWS]- decode screen RAM to text (default $0400 40x25)',
|
||||
' serial - print what the program sent to its serial port (devel-6502)',
|
||||
' pc [N] - print PC + disassembly of N instructions',
|
||||
' info - platform/machine debug info',
|
||||
' reset - reset the emulator',
|
||||
@@ -485,6 +486,13 @@ export class RunScript {
|
||||
}
|
||||
}
|
||||
|
||||
cmdSerial() {
|
||||
const text = this.target.getSerialOutput();
|
||||
if (text == null) throw new Error(`'${this.target.id}' has no serial output`);
|
||||
this.log(`serial output (${text.length} bytes):`);
|
||||
this.out(text.endsWith('\n') ? text : text + '\n');
|
||||
}
|
||||
|
||||
cmdPC(tokens: string[]) {
|
||||
this.target.settle();
|
||||
const pc = this.target.getPC();
|
||||
@@ -534,6 +542,7 @@ const COMMANDS: { [name: string]: Command } = {
|
||||
'keyup': RunScript.prototype.cmdKeyUp,
|
||||
'mem': RunScript.prototype.cmdMem,
|
||||
'screen': RunScript.prototype.cmdScreen,
|
||||
'serial': RunScript.prototype.cmdSerial,
|
||||
'pc': RunScript.prototype.cmdPC,
|
||||
'info': RunScript.prototype.cmdInfo,
|
||||
'reset': RunScript.prototype.cmdReset,
|
||||
|
||||
@@ -466,8 +466,8 @@ export function applyAsmProjectParams(params) {
|
||||
*
|
||||
* //#symbol [<phase>] NAME[=VALUE] phase in c|as|ld (default c)
|
||||
* //#flag <phase> <args...> raw argv for one phase
|
||||
* //#tooldef <phase> NAME=VALUE typed knob (linker: cfgfile, libargs;
|
||||
* compiler: sdcc=3|4)
|
||||
* //#tooldef <phase> NAME=VALUE typed knob (linker: cfgfile, libargs,
|
||||
* code_start, data_start; compiler: sdcc=3|4)
|
||||
*
|
||||
* Symbols are phase-scoped because they are different things: a preprocessor
|
||||
* macro (`c`) takes text, while an assembler/linker global (`as`/`ld`) is an
|
||||
@@ -733,6 +733,11 @@ export function applyBuildDirectives(dir: SourceDirectives, params) {
|
||||
params.cfgfile = td.value;
|
||||
} else if (td.phase === 'linker' && td.name === 'libargs') {
|
||||
params.libargs = td.value.split(',').filter((s) => s !== '');
|
||||
} else if (td.phase === 'linker' && (td.name === 'code_start' || td.name === 'data_start')) {
|
||||
// where sdld puts the code (and the DOS 3.3 header's load address) and the RAM data
|
||||
const addr = Number(td.value);
|
||||
if (Number.isInteger(addr) && addr >= 0 && addr < 0x10000) params[td.name] = addr;
|
||||
else dir.errors.push(`#tooldef ld ${td.name}: expected an address, got '${td.value}'`);
|
||||
} else if (td.phase === 'compiler' && td.name === 'sdcc') {
|
||||
// SDCC major version; every sdcc/sdas/sdld step of the build reads it
|
||||
if (td.value === '3' || td.value === '4') params.sdcc_version = parseInt(td.value);
|
||||
|
||||
@@ -221,6 +221,11 @@ export var PLATFORM_PARAMS = {
|
||||
libargs: [ '--lib-path', '/share/target/apple2/drv', 'apple2.lib'],
|
||||
__CODE_RUN__: 16384,
|
||||
code_start: 0x803,
|
||||
// for SDCC (-sdcc.c): a DOS 3.3 binary at $803 with data above the program
|
||||
rom_size: 0x9400 - 0x803,
|
||||
data_start: 0x9400,
|
||||
zp_start: 0x60,
|
||||
load_header: 'dos33',
|
||||
acmeargs: ['-f', 'apple'],
|
||||
},
|
||||
'apple2-e': {
|
||||
@@ -301,6 +306,12 @@ export var PLATFORM_PARAMS = {
|
||||
cfgfile: 'c64.cfg', // SYS 2061
|
||||
libargs: ['openroms-compat.o', 'c64.lib'],
|
||||
extra_link_files: ['openroms-compat.o'],
|
||||
// for SDCC (-sdcc.c): a PRG with a BASIC SYS stub, code at $80D, data in upper RAM
|
||||
code_start: 0x80d,
|
||||
rom_size: 0xa000 - 0x80d,
|
||||
data_start: 0xc000,
|
||||
zp_start: 0x2,
|
||||
load_header: 'prg',
|
||||
acmeargs: ['-f', 'cbm'],
|
||||
//extra_link_files: ['c64-cart.cfg'],
|
||||
},
|
||||
|
||||
@@ -209,6 +209,53 @@ export function parseIHX(ihx: string, rom_start: number, rom_size: number, error
|
||||
return output;
|
||||
}
|
||||
|
||||
/** End offset, within [rom_start, rom_start + rom_size), of the highest data record. */
|
||||
export function ihxExtent(ihx: string, rom_start: number, rom_size: number): number {
|
||||
let end = 0, upper = 0;
|
||||
for (const s of ihx.split("\n")) {
|
||||
if (s[0] != ':') continue;
|
||||
const arr = hexToArray(s, 1);
|
||||
if (arr[3] == 0) {
|
||||
const offset = upper + (arr[1] << 8) + arr[2] - rom_start;
|
||||
if (offset >= 0 && offset + arr[0] <= rom_size) end = Math.max(end, offset + arr[0]);
|
||||
} else if (arr[3] == 4) {
|
||||
upper = ((arr[4] << 8) | arr[5]) << 16;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `load_header` platform param: how a loadable file wraps the linked
|
||||
* image (trimmed to the program's end, `length` bytes, linked at `start`).
|
||||
* dos33 Apple II binary: load address and length (2 bytes each, little
|
||||
* endian); the loader checks length == file size - 4
|
||||
* prg Commodore PRG: load address $0801, then the BASIC line
|
||||
* `10 SYS <start>` that runs the program, which must be linked at
|
||||
* $080D (right after that stub)
|
||||
*/
|
||||
export function loadHeader(kind: string, image: Uint8Array, start: number, length: number): Uint8Array<ArrayBuffer> {
|
||||
var header: number[];
|
||||
switch (kind) {
|
||||
case 'dos33':
|
||||
header = [start & 0xff, start >> 8, length & 0xff, length >> 8];
|
||||
break;
|
||||
case 'prg': {
|
||||
const sys = [...String(start)].map((c) => c.charCodeAt(0));
|
||||
const end = 0x801 + 4 + 1 + sys.length + 1; // link, line number, SYS token, digits, NUL
|
||||
header = [0x01, 0x08, end & 0xff, end >> 8, 0x0a, 0x00, 0x9e, ...sys, 0, 0, 0];
|
||||
if (0x801 + header.length - 2 !== start) throw new Error(`load_header prg: code must start at $${(0x801 + header.length - 2).toString(16)}, not $${start.toString(16)}`);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`unknown load_header '${kind}'`);
|
||||
}
|
||||
const out = new Uint8Array(new ArrayBuffer(header.length + length));
|
||||
out.set(header);
|
||||
out.set(image.subarray(0, length), header.length);
|
||||
return out;
|
||||
}
|
||||
|
||||
function errorMatcherSDASZ80(path: string, errors: WorkerError[]) {
|
||||
//?ASxxxx-Error-<o> in line 1 of main.asm null
|
||||
// <o> .org in REL area or directive / mnemonic error
|
||||
@@ -432,6 +479,10 @@ async function linkSDLD(step: BuildStep, ld: 'sdldz80' | 'sdld6808') {
|
||||
if (errors.length) {
|
||||
return { errors: errors };
|
||||
}
|
||||
if (params.load_header) {
|
||||
const start = params.rom_start !== undefined ? params.rom_start : params.code_start;
|
||||
binout = loadHeader(params.load_header, binout, start, ihxExtent(hexout, start, params.rom_size));
|
||||
}
|
||||
// parse listings
|
||||
var listings: CodeListingMap = {};
|
||||
for (var fn of step.files) {
|
||||
@@ -499,13 +550,14 @@ async function linkSDLD(step: BuildStep, ld: 'sdldz80' | 'sdld6808') {
|
||||
* source comments, source lines. `srcprefix` is the source file name without
|
||||
* its extension: SDCC 3.6.5 writes `;<stdin>:N:` comments, SDCC 4.x writes
|
||||
* `;file.c:N: <source>` (with mcpp's `//` path prefix), and 4.x addresses
|
||||
* have 8 hex digits instead of 4.
|
||||
* have 8 hex digits instead of 4. The mos6502 backend writes `;\tfile.c: N: <source>`.
|
||||
*/
|
||||
export function parseRSTListing(rstout: string, srcprefix: string) {
|
||||
// 0000 21 02 00 [10] 52 ld hl, #2
|
||||
var asmlines = parseListing(rstout, /^\s*([0-9A-F]{4,8})\s+([0-9A-F][0-9A-F r]*[0-9A-F])\s+\[([0-9 ]+)\]?\s+(\d+) (.*)/i, 4, 1, 2, 3);
|
||||
const name = srcprefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const srcre = new RegExp(`^\\s+\\d+ ;(?:<stdin>|/*${name}\\.[^:\\s]+):(\\d+):`, 'i');
|
||||
// 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);
|
||||
// TODO: you have to get rid of all source lines to get asm listing
|
||||
return {
|
||||
@@ -597,9 +649,9 @@ export async function compileSDCC(step: BuildStep): Promise<BuildStepResult> {
|
||||
// if "#pragma opt_code" found do not disable optimziations
|
||||
if (!/^\s*#pragma\s+opt_code/m.exec(code)) {
|
||||
args.push.apply(args, [
|
||||
'--max-allocs-per-node', '500',
|
||||
'--no-peep',
|
||||
'--nolospre'
|
||||
'--nolospre',
|
||||
'--max-allocs-per-node', '500',
|
||||
]);
|
||||
}
|
||||
if (params.extra_compile_args) {
|
||||
|
||||
@@ -124,6 +124,30 @@ describe('8bws CLI', function () {
|
||||
assert.ok(r.success, r.error);
|
||||
assert.equal(r.data.frames, 10);
|
||||
});
|
||||
it('should build cosmic.c with SDCC from a header-only wrapper', function () {
|
||||
var out = path.join(os.tmpdir(), '8bws-cosmic-sdcc.bin');
|
||||
cli('build', '-p', 'apple2', 'presets/apple2/cosmic-sdcc.c', '-o', out);
|
||||
var bin = fs.readFileSync(out);
|
||||
assert.equal(bin[0] | bin[1] << 8, 0x4000); // //#tooldef ld code_start=0x4000
|
||||
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) {
|
||||
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);
|
||||
var bin = fs.readFileSync(out);
|
||||
assert.equal(bin[0] | bin[1] << 8, 0x801); // load address, then 10 SYS 2061
|
||||
assert.equal(bin[6], 0x9e);
|
||||
});
|
||||
});
|
||||
it('should print the serial output of a devel-6502 program', function () {
|
||||
var out = cli('run', '-p', 'devel-6502', 'presets/devel-6502/hello-sdcc.c', '-e', 'run 5; serial');
|
||||
assert.ok(out.includes('Hello, SDCC 6502!\nagain\nagain\nagain'));
|
||||
});
|
||||
it('should reject serial on a platform without a serial port', function () {
|
||||
cliFails('run', '-p', 'nes', 'presets/nes/hello.c', '-e', 'run 1; serial');
|
||||
});
|
||||
it('should build and run a directory', function () {
|
||||
var dir = fs.mkdtempSync(path.join(os.tmpdir(), '8bws-dir-'));
|
||||
fs.copyFileSync('presets/nes/hello.c', path.join(dir, 'hello.c'));
|
||||
|
||||
@@ -245,6 +245,25 @@ describe('Worker', function() {
|
||||
done();
|
||||
}, 32768, 0, 0);
|
||||
});
|
||||
it('should link SDCC mos6502 as a DOS 3.3 binary on apple2', function(done) {
|
||||
var csource = ab2str(fs.readFileSync('presets/apple2/hello-sdcc.c'));
|
||||
compile('sdcc', csource, 'apple2', function(err, msg) {
|
||||
var bin = msg.output;
|
||||
assert.equal(bin[0] | bin[1] << 8, 0x803); // load address
|
||||
assert.equal(bin[2] | bin[3] << 8, bin.length - 4); // length: what the loader checks
|
||||
assert.ok(bin.length < 1024); // trimmed to the program
|
||||
done();
|
||||
}, 377, 0, 0);
|
||||
});
|
||||
it('should link SDCC mos6502 as a PRG with a BASIC stub on c64', function(done) {
|
||||
var csource = ab2str(fs.readFileSync('presets/c64/hello-sdcc.c'));
|
||||
compile('sdcc', csource, 'c64', function(err, msg) {
|
||||
var bin = msg.output;
|
||||
assert.deepEqual(Array.from(bin.slice(0, 12)), [1,8, 0x0b,8, 10,0, 0x9e, 0x32,0x30,0x36,0x31, 0]); // 10 SYS 2061
|
||||
assert.ok(bin.length < 1024); // trimmed to the program
|
||||
done();
|
||||
}, 332, 0, 0);
|
||||
});
|
||||
it('should compile oscar64 and return listings/symbols/segments', async function() {
|
||||
var msgs = [{code:'#include <stdio.h>\nint main() { printf("FOO"); return 0; }', platform:'c64', tool:'oscar64', path:'main.c', mainfile:true}];
|
||||
var result = await new Promise(function(resolve, reject) {
|
||||
|
||||
@@ -257,6 +257,16 @@ describe("explicit build directives (//#symbol, //#flag, //#tooldef)", function
|
||||
assert.strictEqual(params.sdcc_version, 3);
|
||||
});
|
||||
|
||||
it("#tooldef ld code_start and data_start take addresses", function () {
|
||||
const params: any = {};
|
||||
applyDirectives("//#tooldef ld code_start=0x4000\n//#tooldef ld data_start=36864\n", params);
|
||||
assert.strictEqual(params.code_start, 0x4000);
|
||||
assert.strictEqual(params.data_start, 0x9000);
|
||||
const dir = applyDirectives("//#tooldef ld code_start=nowhere\n//#tooldef ld data_start=0x10000\n", params);
|
||||
assert.strictEqual(dir.errors.length, 2);
|
||||
assert.strictEqual(params.code_start, 0x4000);
|
||||
});
|
||||
|
||||
it("#tooldef rejects unknown knobs", function () {
|
||||
const params = c64Params();
|
||||
const dir = applyDirectives("//#tooldef ld nonsense=1\n", params);
|
||||
|
||||
@@ -18,8 +18,24 @@ const RST_SDCC4 = [
|
||||
" 0000056F CD 40 00 [17] 1278 \tcall\t_inline_fn",
|
||||
].join("\n");
|
||||
|
||||
// the mos6502 backend: a tab and a space around the line number, [ 2] cycles
|
||||
const RST_MOS6502 = [
|
||||
" 117 ;\thello.c: 17: void main(void) {",
|
||||
" 118 ;\tgenLabel",
|
||||
" 00000865 126 _main:",
|
||||
" 128 ;\thello.c: 18: putstr(message);",
|
||||
" 00000865 A2 08 [ 2] 136 \tldx\t#>_message",
|
||||
" 00000869 20 4E 08 [ 6] 138 \tjsr\t_putstr",
|
||||
].join("\n");
|
||||
|
||||
describe("sdcc listings", function () {
|
||||
|
||||
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
|
||||
assert.deepStrictEqual(l.asmlines.map((x) => x.offset), [0x865, 0x869]);
|
||||
});
|
||||
|
||||
it("maps SDCC 3.6.5 <stdin> source comments", function () {
|
||||
const l = parseRSTListing(RST_SDCC3, "skeleton");
|
||||
assert.deepStrictEqual(l.lines.map((x) => [x.line, x.offset]), [[172, 0x56c]]);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from "assert";
|
||||
import { describe, it } from "mocha";
|
||||
import { sdccTarget } from "../../src/worker/tools/sdcc";
|
||||
import { getToolForFilename_6502 } from "../../src/common/toolselect";
|
||||
import { sdccTarget, loadHeader } from "../../src/worker/tools/sdcc";
|
||||
import { getToolForFilename_6502, getToolForFilename_z80, getToolForFilename_apple2 } from "../../src/common/toolselect";
|
||||
|
||||
describe("sdcc targets", function () {
|
||||
|
||||
@@ -25,4 +25,20 @@ describe("sdcc targets", function () {
|
||||
assert.strictEqual(getToolForFilename_6502('hello.c'), 'cc65');
|
||||
});
|
||||
|
||||
it("selects SDCC for .sdcc files", function () {
|
||||
assert.strictEqual(getToolForFilename_6502('hello.sdcc'), 'sdcc');
|
||||
assert.strictEqual(getToolForFilename_apple2('hello.sdcc'), 'sdcc');
|
||||
assert.strictEqual(getToolForFilename_z80('skeleton.sdcc'), 'sdcc');
|
||||
});
|
||||
|
||||
|
||||
it("wraps linked images in a load header", function () {
|
||||
const img = Uint8Array.of(1, 2, 3, 4, 5);
|
||||
assert.deepStrictEqual(Array.from(loadHeader('dos33', img, 0x803, 3)), [3, 8, 3, 0, 1, 2, 3]);
|
||||
// load address, link pointer, line 10, SYS 2061, then the code
|
||||
assert.deepStrictEqual(Array.from(loadHeader('prg', img, 0x80d, 2)),
|
||||
[1, 8, 0x0b, 8, 10, 0, 0x9e, 0x32, 0x30, 0x36, 0x31, 0, 0, 0, 1, 2]);
|
||||
assert.throws(() => loadHeader('prg', img, 0x900, 2), /\$80d/);
|
||||
assert.throws(() => loadHeader('bogus', img, 0, 0), /unknown load_header/);
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
TOOL_META, getToolMeta, getToolMetaForFilename, getPreloadFSName, getSkeletonName,
|
||||
getIncludePatterns, getLinkPatterns, matchDependencyPatterns,
|
||||
getSystemIncludePatterns, SYSTEM_INCLUDE_PATTERNS, getSharedFileSystemName, getToolHelpURL,
|
||||
getPlatformToolHelpURL
|
||||
getPlatformToolHelpURL, SDCC_DEFAULT_VERSION
|
||||
} from "../../src/common/toolmeta";
|
||||
// Node-friendly worker entry point (re-exports TOOLS without Worker wiring)
|
||||
import { TOOLS } from "../../src/worker/workerlib";
|
||||
@@ -51,12 +51,16 @@ describe('Tool metadata registry', function () {
|
||||
assert.strictEqual(getSharedFileSystemName('cc65', 'nes'), 'wasi:cc65-fs-nes.zip');
|
||||
assert.strictEqual(getSharedFileSystemName('cc65', 'c64'), 'wasi:cc65-fs-c64.zip');
|
||||
assert.strictEqual(getSharedFileSystemName('cc65', 'nonexistent'), undefined);
|
||||
// SDCC 4.x (WASI zip) by default; 3.x-library platforms keep the 3.6.5 preloadFS
|
||||
assert.strictEqual(getPreloadFSName('sdcc'), undefined);
|
||||
assert.strictEqual(getSharedFileSystemName('sdcc', 'mw8080bw'), 'wasi:sdcc-fs.zip');
|
||||
// the 'default' config leads with whichever SDCC the project defaults to
|
||||
// (getSharedFileSystemName prefers preloadFS); platforms whose libraries
|
||||
// were built by 3.x always keep the 3.6.5 preloadFS.
|
||||
const defaultPreload = SDCC_DEFAULT_VERSION === 3 ? 'sdcc' : undefined;
|
||||
const defaultShared = SDCC_DEFAULT_VERSION === 3 ? 'sdcc' : 'wasi:sdcc-fs.zip';
|
||||
assert.strictEqual(getPreloadFSName('sdcc'), defaultPreload);
|
||||
assert.strictEqual(getSharedFileSystemName('sdcc', 'mw8080bw'), defaultShared);
|
||||
assert.strictEqual(getPreloadFSName('sdcc', 'gb'), 'sdcc');
|
||||
assert.strictEqual(getPreloadFSName('sdcc', 'msx-libcv'), 'sdcc');
|
||||
assert.strictEqual(getSharedFileSystemName('sdcc', 'msx'), 'wasi:sdcc-fs.zip');
|
||||
assert.strictEqual(getSharedFileSystemName('sdcc', 'msx'), defaultShared);
|
||||
assert.strictEqual(getPreloadFSName('dasm'), undefined);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user