1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2026-04-20 00:17:04 +00:00

gb: wasmboy test

oscar64: emsdk build
This commit is contained in:
Steven Hugg
2025-06-23 17:40:16 -05:00
parent 52c9f9bfaf
commit 6ad35adcb6
13 changed files with 4548 additions and 77 deletions
+46 -1
View File
@@ -22,7 +22,8 @@
"localforage": "^1.9.0",
"mousetrap": "^1.6.5",
"octokat": "^0.10.0",
"split.js": "^1.6.2"
"split.js": "^1.6.2",
"wasmboy": "^0.7.1"
},
"devDependencies": {
"@types/bootbox": "^5.1.3",
@@ -986,6 +987,11 @@
"node": ">= 4.5.0"
}
},
"node_modules/audiobuffer-to-wav": {
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/torch2424/audiobuffer-to-wav.git#8878a20c5cc7e457b113dabfb1781ad4178f9c62",
"license": "MIT"
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -3186,6 +3192,12 @@
"node": ">=0.10.0"
}
},
"node_modules/idb": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/idb/-/idb-2.1.3.tgz",
"integrity": "sha512-1He6QAuavrD38HCiJasi4lEEK87Y22ldFuM+ZHkp433n4Fd5jXjWKutClYFp8w4mgx3zgrjnWxL8dpjMzcQ+WQ==",
"license": "ISC"
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -5795,6 +5807,12 @@
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"optional": true
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
@@ -6112,6 +6130,15 @@
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"optional": true
},
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"license": "MIT",
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -6364,6 +6391,12 @@
"node": ">=4"
}
},
"node_modules/responsive-gamepad": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/responsive-gamepad/-/responsive-gamepad-1.1.0.tgz",
"integrity": "sha512-njsJuKvany9eYjywXm8iorTeXeAAPqwMNaRWOo8jlh0iQboXgGPf6Z6bLGntELrfU+vR94jTPJYRW0Zzb2gaRA==",
"license": "Apache-2.0"
},
"node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -7317,6 +7350,18 @@
"node": ">=18"
}
},
"node_modules/wasmboy": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/wasmboy/-/wasmboy-0.7.1.tgz",
"integrity": "sha512-qgA3bIFAqioYs8kYXtsanIvedgZlZQf382zs3gNlZHIItsAnRzV70/Vp6cJxbK4FyaiG58ah8/g7OW3orrs9Lg==",
"license": "GPL-3.0-or-later",
"dependencies": {
"audiobuffer-to-wav": "git+https://github.com/torch2424/audiobuffer-to-wav.git#es-module-rollup",
"idb": "^2.1.3",
"raf": "^3.4.0",
"responsive-gamepad": "1.1.0"
}
},
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+2 -1
View File
@@ -23,7 +23,8 @@
"localforage": "^1.9.0",
"mousetrap": "^1.6.5",
"octokat": "^0.10.0",
"split.js": "^1.6.2"
"split.js": "^1.6.2",
"wasmboy": "^0.7.1"
},
"devDependencies": {
"@types/bootbox": "^5.1.3",
+116
View File
@@ -0,0 +1,116 @@
; Game Boy Background Example in Z80 Assembly (sdasgb syntax)
; Hardware register definitions
.equ LCDC, 0xFF40 ; LCD Control register
.equ BGP, 0xFF47 ; Background palette register
.equ LY, 0xFF44 ; LCD Y coordinate register
; VRAM addresses
.equ VRAM_TILES, 0x9000 ; Tile data area
.equ VRAM_MAP, 0x9800 ; Background tile map
; LCDC flags
.equ LCDC_ON, 0x80 ; LCD enable
.equ LCDC_BG_ON, 0x01 ; Background enable
.area _CODE
.globl _main
_main:
; Disable interrupts
di
; Turn off LCD
ld a, #0x00
ldh (LCDC), a
; Load tile data into VRAM
; Copy tile data to VRAM tile 1 (tile 0 is blank by default)
ld hl, #tile_data
ld de, #(VRAM_TILES + 0x10) ; Tile 1 starts at offset 0x10
ld bc, #16 ; 16 bytes per tile
call memcpy
; Set up background map
; Fill background map with tile index 1
ld hl, #VRAM_MAP
ld a, #0x01 ; Tile index 1
ld bc, #(20*18) ; 20x18 tiles = 360 tiles
fill_bg_loop:
ld (hl), a
inc hl
dec bc
ld a, b
or c
jr nz, fill_bg_loop
; Set background palette
ld a, #0x12 ; 11 10 01 00 - darkest to lightest
ldh (BGP), a
; Turn on LCD with background enabled
ld a, #(LCDC_ON | LCDC_BG_ON)
ldh (LCDC), a
; Main loop
main_loop:
call wait_vblank
jr main_loop
; Wait for vertical blank
wait_vblank:
ldh a, (LY)
cp #144 ; VBlank starts at line 144
jr nz, wait_vblank
ret
; Simple memory copy routine
; hl = source, de = destination, bc = count
memcpy:
ld a, (hl)
ld (de), a
inc hl
inc de
dec bc
ld a, b
or c
jr nz, memcpy
ret
; Tile data
tile_data:
.db 1,2,4,8,0x10,0x20,0x40,0x80
.db 0x80,0x40,0x20,0x10,8,4,2,1
; ROM header (required for Game Boy)
.area _HEADER (ABS)
.org 0x0100
nop
jp _main
.org 0x0104
.db 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B
.db 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D
.db 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E
.db 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99
.db 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC
.db 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E
.org 0x0134
.ascii "BGTEST" ; Title (11 bytes max)
.org 0x013F
.db 0x00 ; New licensee code
.org 0x0147
.db 0x00 ; Cartridge type (ROM only)
.db 0x00 ; ROM size (32KB)
.db 0x00 ; RAM size (none)
.db 0x01 ; Destination (non-Japanese)
.db 0x33 ; Old licensee code
.db 0x00 ; ROM version
.org 0x014D
.db 0x00 ; Header checksum (will be calculated)
.db 0x00, 0x00 ; Global checksum (will be calculated)
+1
View File
@@ -618,6 +618,7 @@ export function getToolForFilename_z80(fn:string) : string {
if (fn.endsWith(".c")) return "sdcc";
if (fn.endsWith(".h")) return "sdcc";
if (fn.endsWith(".s")) return "sdasz80";
if (fn.endsWith(".sgb")) return "sdasgb";
if (fn.endsWith(".ns")) return "naken";
if (fn.endsWith(".scc")) return "sccz80";
if (fn.endsWith(".z")) return "zmac";
+1
View File
@@ -115,6 +115,7 @@ const TOOL_TO_SOURCE_STYLE = {
'nesasm': '6502',
'z80asm': 'z80',
'sdasz80': 'z80',
'sdasgb': 'z80',
'sdcc': 'text/x-csrc',
'verilator': 'verilog',
'jsasm': 'z80',
+2
View File
@@ -14,6 +14,8 @@ export function importPlatform(name: string) : Promise<any> {
case "devel": return import("../platform/devel");
case "exidy": return import("../platform/exidy");
case "galaxian": return import("../platform/galaxian");
case "gb": return import("../platform/gb");
case "gameboy": return import("../platform/gb");
case "kim1": return import("../platform/kim1");
case "markdown": return import("../platform/markdown");
case "msx": return import("../platform/msx");
+114
View File
@@ -0,0 +1,114 @@
import { WasmBoy } from 'wasmboy';
import { getToolForFilename_z80, Platform, Preset } from "../common/baseplatform";
import { PLATFORMS, RasterVideo } from "../common/emu";
const GB_PRESETS: Preset[] = [
{ id: 'hello.sgb', name: 'Hello (ASM)' },
];
class GameBoyPlatform implements Platform {
mainElement;
video;
audioFrequency = 22050;
frameIndex = 0;
machine = { cpuCyclesPerLine: 114 }; // TODO: adjust for GameBoy
constructor(mainElement) {
//super();
this.mainElement = mainElement;
}
getPresets() { return GB_PRESETS; }
async start() {
this.video = new RasterVideo(this.mainElement, 160, 144, { overscan: false });
this.video.create();
// Initialize WasmBoy
const config = {
headless: false,
useFrameSkip: false,
audioBatchProcessing: false,
timersBatchProcessing: false,
audioAccumulateSamples: false,
graphicsBatchProcessing: false,
graphicsDisableScanlineRendering: false,
tileRendering: true,
tileCaching: true,
};
await WasmBoy.config(config, this.video.canvas, this.audioFrequency);
}
pollControls() {
// WasmBoy handles controller polling internally
// No need to implement this method
}
advance(novideo: boolean): number {
// WasmBoy handles frame timing internally
return 70224; // Game Boy CPU cycles per frame
}
async loadROM(title, data) {
var romArray = new Uint8Array(data);
await WasmBoy.loadROM(romArray);
this.frameIndex = 0;
}
reset() {
WasmBoy.reset();
}
isRunning() {
return WasmBoy.isPlaying();
}
pause() {
WasmBoy.pause();
}
resume() {
WasmBoy.play();
}
getOriginPC() {
return 0x100; // GameBoy boot vector
}
getDefaultExtension() {
return ".c";
}
getROMExtension() {
return ".gb";
}
getToolForFilename = (fn: string): string => {
return getToolForFilename_z80(fn);
}
getMemoryMap = function () {
return {
main: [
{ name: 'ROM Bank 0', start: 0x0000, size: 0x4000, type: 'rom' },
{ name: 'ROM Bank 1+', start: 0x4000, size: 0x4000, type: 'rom' },
{ name: 'Video RAM', start: 0x8000, size: 0x2000, type: 'ram' },
{ name: 'External RAM', start: 0xA000, size: 0x2000, type: 'ram' },
{ name: 'Work RAM', start: 0xC000, size: 0x2000, type: 'ram' },
{ name: 'OAM', start: 0xFE00, size: 0xA0, type: 'ram' },
{ name: 'I/O Registers', start: 0xFF00, size: 0x80, type: 'io' },
{ name: 'High RAM', start: 0xFF80, size: 0x7F, type: 'ram' },
]
};
};
showHelp() {
return "https://8bitworkshop.com/docs/platforms/gameboy/";
}
}
PLATFORMS['gb'] = GameBoyPlatform;
PLATFORMS['gameboy'] = GameBoyPlatform;
+9 -1
View File
@@ -362,7 +362,15 @@ export var PLATFORM_PARAMS = {
extra_link_files: ['crt0.c', 'libc.a'],
extra_link_args: ['crt0.c', '-lc'],
},
};
'gb': {
arch: 'gbz80',
code_start: 0x0,
rom_size: 0x8000,
data_start: 0xc000,
data_size: 0x2000,
stack_end: 0xe000,
},
};
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];
PLATFORM_PARAMS['sms-gg-libcv'] = PLATFORM_PARAMS['sms-sms-libcv'];
+27 -44
View File
@@ -1,53 +1,36 @@
import { WASIFilesystem, WASIMemoryFilesystem, WASIRunner } from "../../common/wasi/wasishim";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, store, putWorkFile } from "../builder";
import { makeErrorMatcher, msvcErrorMatcher } from "../listingutils";
import { loadWASIFilesystemZip } from "../wasiutils";
import { loadWASMBinary } from "../wasmutils";
import { WorkerError, WorkerResult } from "../../common/workertypes";
import { BuildStep, gatherFiles, populateExtraFiles, populateFiles, putWorkFile, staleFiles } from "../builder";
import { makeErrorMatcher } from "../listingutils";
import { emglobal, execMain, loadNative, moduleInstFn, print_fn, setupFS } from "../wasmutils";
let oscar64_fs: WASIFilesystem | null = null;
let wasiModule: WebAssembly.Module | null = null;
export async function compileOscar64(step: BuildStep): Promise<BuildStepResult> {
const errors = [];
const rootDir = "/root/";
export async function compileOscar64(step: BuildStep): Promise<WorkerResult> {
loadNative("oscar64");
var params = step.params;
gatherFiles(step, { mainFilePath: "main.c" });
const destpath = (step.path || "main.c").replace(/\.[^.]+$/, ".prg");
console.log('destpath', destpath);
var errors: WorkerError[] = [];
if (staleFiles(step, [destpath])) {
if (!oscar64_fs) {
oscar64_fs = await loadWASIFilesystemZip("oscar64-fs.zip", "/root/");
}
if (!wasiModule) {
wasiModule = new WebAssembly.Module(loadWASMBinary("oscar64"));
}
const wasi = new WASIRunner();
wasi.initSync(wasiModule);
wasi.fs.setParent(oscar64_fs);
for (let file of step.files) {
wasi.fs.putFile(rootDir + file, store.getFileData(file));
}
//wasi.addPreopenDirectory("include");
wasi.addPreopenDirectory("/root");
wasi.setArgs(["oscar64", "-v", "-g", "-i=/root", step.path]);
try {
wasi.run();
} catch (e) {
errors.push(e);
}
// TODO
let stdout = wasi.fds[1].getBytesAsString();
let stderr = wasi.fds[2].getBytesAsString();
console.log('stdout', stdout);
console.log('stderr', stderr);
// (58, 17) : error 3001: Could not open source file. 'stdlib.c'
const matcher = makeErrorMatcher(errors, /\((\d+),\s+(\d+)\)\s+: error (\d+): (.+)/, 1, 4, step.path);
for (let line of stderr.split('\n')) {
matcher(line);
}
if (errors.length) {
return { errors };
}
const output = wasi.fs.getFile(rootDir + destpath).getBytes();
var oscar64: EmscriptenModule = await emglobal.Oscar64({
instantiateWasm: moduleInstFn('oscar64'),
noInitialRun: true,
print: print_fn,
printErr: matcher,
});
var FS = (oscar64 as any).FS;
//setupFS(FS, 'oscar64');
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_compile_files);
var args = ["-v", "-g", "-i=/root", step.path];
execMain(step, oscar64, args);
if (errors.length)
return { errors: errors };
var output = FS.readFile(destpath, { encoding: 'binary' });
putWorkFile(destpath, output);
return {
output,
+81 -30
View File
@@ -1,4 +1,5 @@
import { CodeListingMap } from "../../common/workertypes";
import { Worker } from "node:worker_threads";
import { CodeListingMap, WorkerError } from "../../common/workertypes";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, populateExtraFiles, anyTargetChanged, getWorkFileAsString } from "../builder";
import { parseListing, parseSourceLines, msvcErrorMatcher } from "../listingutils";
import { EmscriptenModule, emglobal, execMain, loadNative, moduleInstFn, print_fn, setupFS, setupStdin } from "../wasmutils";
@@ -43,38 +44,43 @@ function parseIHX(ihx, rom_start, rom_size, errors) {
return output;
}
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
// ?ASxxxx-Error-<q> in line 1627 of cosmic.asm
// <q> missing or improper operators, terminators, or delimiters
var match_asm_re1 = / in line (\d+) of (\S+)/; // TODO
var match_asm_re2 = / <\w> (.+)/; // TODO
var errline = 0;
var errpath = path;
var match_asm_fn = (s: string) => {
var m = match_asm_re1.exec(s);
if (m) {
errline = parseInt(m[1]);
errpath = m[2];
} else {
m = match_asm_re2.exec(s);
if (m) {
errors.push({
line: errline,
path: errpath,
msg: m[1]
});
}
}
}
return match_asm_fn;
}
export function assembleSDASZ80(step: BuildStep): BuildStepResult {
loadNative("sdasz80");
loadNative('sdasz80');
var objout, lstout, symout;
var errors = [];
gatherFiles(step, { mainFilePath: "main.asm" });
var objpath = step.prefix + ".rel";
var lstpath = step.prefix + ".lst";
if (staleFiles(step, [objpath, lstpath])) {
//?ASxxxx-Error-<o> in line 1 of main.asm null
// <o> .org in REL area or directive / mnemonic error
// ?ASxxxx-Error-<q> in line 1627 of cosmic.asm
// <q> missing or improper operators, terminators, or delimiters
var match_asm_re1 = / in line (\d+) of (\S+)/; // TODO
var match_asm_re2 = / <\w> (.+)/; // TODO
var errline = 0;
var errpath = step.path;
var match_asm_fn = (s: string) => {
var m = match_asm_re1.exec(s);
if (m) {
errline = parseInt(m[1]);
errpath = m[2];
} else {
m = match_asm_re2.exec(s);
if (m) {
errors.push({
line: errline,
path: errpath,
msg: m[1]
});
}
}
}
const match_asm_fn = errorMatcherSDASZ80(step.path, errors);
var ASZ80: EmscriptenModule = emglobal.sdasz80({
instantiateWasm: moduleInstFn('sdasz80'),
noInitialRun: true,
@@ -101,6 +107,41 @@ export function assembleSDASZ80(step: BuildStep): BuildStepResult {
//symout = FS.readFile("main.sym", {encoding:'utf8'});
}
export async function assembleSDASGB(step: BuildStep): Promise<BuildStepResult> {
loadNative('sdasgb');
var objout, lstout, symout;
var errors = [];
gatherFiles(step, { mainFilePath: "main.asm" });
var objpath = step.prefix + ".rel";
var lstpath = step.prefix + ".lst";
if (staleFiles(step, [objpath, lstpath])) {
const match_asm_fn = errorMatcherSDASZ80(step.path, errors);
var ASZ80: EmscriptenModule = await emglobal.sdasgb({
instantiateWasm: moduleInstFn('sdasgb'),
noInitialRun: true,
//logReadFiles:true,
print: match_asm_fn,
printErr: match_asm_fn,
});
var FS = ASZ80.FS;
populateFiles(step, FS);
execMain(step, ASZ80, ['-plosgffwy', step.path]);
if (errors.length) {
return { errors: errors };
}
objout = FS.readFile(objpath, { encoding: 'utf8' });
lstout = FS.readFile(lstpath, { encoding: 'utf8' });
putWorkFile(objpath, objout);
putWorkFile(lstpath, lstout);
}
return {
linktool: "sdldz80",
files: [objpath, lstpath],
args: [objpath]
};
//symout = FS.readFile("main.sym", {encoding:'utf8'});
}
export function linkSDLDZ80(step: BuildStep) {
loadNative("sdldz80");
var errors = [];
@@ -206,6 +247,14 @@ export function linkSDLDZ80(step: BuildStep) {
}
}
}
// gameboy: compute checksum
if (step.params.arch === 'gbz80') {
var checksum = 0;
for (var address = 0x0134; address <= 0x014C; address++) {
checksum = checksum - binout[address] - 1;
}
binout[0x14D] = checksum & 0xff;
}
return {
output: binout,
listings: listings,
@@ -221,10 +270,11 @@ export function compileSDCC(step: BuildStep): BuildStepResult {
gatherFiles(step, {
mainFilePath: "main.c" // not used
});
var params = step.params;
var isGBZ80 = step.params.arch === 'gbz80';
var outpath = step.prefix + ".asm";
if (staleFiles(step, [outpath])) {
var errors = [];
var params = step.params;
loadNative('sdcc');
var SDCC: EmscriptenModule = emglobal.sdcc({
instantiateWasm: moduleInstFn('sdcc'),
@@ -246,7 +296,8 @@ export function compileSDCC(step: BuildStep): BuildStepResult {
// pipe file to stdin
setupStdin(FS, code);
setupFS(FS, 'sdcc');
var args = ['--vc', '--std-sdcc99', '-mz80', //'-Wall',
const machineFlags = isGBZ80 ? '-mgbz80' : '-mz80';
var args = ['--vc', '--std-sdcc99', machineFlags, //'-Wall',
'--c1mode',
//'--debug',
//'-S', 'main.c',
@@ -266,7 +317,7 @@ export function compileSDCC(step: BuildStep): BuildStepResult {
//'--noloopreverse',
'-o', outpath];
// if "#pragma opt_code" found do not disable optimziations
if (!/^\s*#pragma\s+opt_code/m.exec(code)) {
if (!isGBZ80 && !/^\s*#pragma\s+opt_code/m.exec(code)) {
args.push.apply(args, [
'--oldralloc',
'--no-peep',
@@ -287,7 +338,7 @@ export function compileSDCC(step: BuildStep): BuildStepResult {
putWorkFile(outpath, asmout);
}
return {
nexttool: "sdasz80",
nexttool: isGBZ80 ? 'sdasgb' : 'sdasz80',
path: outpath,
args: [outpath],
files: [outpath],
File diff suppressed because it is too large Load Diff
Binary file not shown.
+3
View File
@@ -15,6 +15,7 @@ import * as acme from './tools/acme'
import * as cc7800 from './tools/cc7800'
import * as bataribasic from './tools/bataribasic'
import * as oscar64 from './tools/oscar64'
import { PLATFORM_PARAMS } from "./platforms";
export const TOOLS = {
@@ -26,6 +27,7 @@ export const TOOLS = {
//'z80asm': assembleZ80ASM,
//'sccz80': compileSCCZ80,
'sdasz80': sdcc.assembleSDASZ80,
'sdasgb': sdcc.assembleSDASGB,
'sdldz80': sdcc.linkSDLDZ80,
'sdcc': sdcc.compileSDCC,
'xasm6809': m6809.assembleXASM6809,
@@ -82,6 +84,7 @@ export const TOOL_PRELOADFS = {
'cc65-exidy': '65-none',
'ca65-exidy': '65-none',
'sdasz80': 'sdcc',
'sdasgb': 'sdcc',
'sdcc': 'sdcc',
'sccz80': 'sccz80',
'bataribasic': '2600basic',