diff --git a/extension/src/cheaders.ts b/extension/src/cheaders.ts index 788e6837..3bdf3a33 100644 --- a/extension/src/cheaders.ts +++ b/extension/src/cheaders.ts @@ -76,10 +76,13 @@ export function extractHeaders(workerDir: string, tool: string, platform: string const fsName = getSharedFileSystemName(tool, platform); if (fsName && fsName.startsWith('wasi:')) files.push(...readWASIZipPackage(workerDir, fsName.substring(5))); else if (fsName) files.push(...readPreloadPackage(workerDir, fsName)); + // subdirectories too: GBDK's headers are gb/gb.h and so on const libDir = path.join(workerDir, 'lib', platform); if (fs.existsSync(libDir)) { - for (const f of fs.readdirSync(libDir).filter((f) => /\.h$/i.test(f))) - files.push({ path: `/lib/${f}`, data: fs.readFileSync(path.join(libDir, f)) }); + for (const f of fs.readdirSync(libDir, { recursive: true }) as string[]) { + if (/\.h$/i.test(f)) + files.push({ path: `/lib/${f.split(path.sep).join('/')}`, data: fs.readFileSync(path.join(libDir, f)) }); + } } const written: string[] = []; for (const f of files) { @@ -111,16 +114,17 @@ export function clangConfig(tool: string, platform: string, dirs: { }): ClangConfig | undefined { const params: any = (PLATFORM_PARAMS as any)[platform] || {}; const defines = ['__8BITWORKSHOP__', ...(params.define || [])]; - const includeDirs = [dirs.sourceDir]; - // -D and -I from extra_preproc_args; '.' is the platform's lib directory, - // which extractHeaders() puts in /lib + // the worker stages the platform's lib directory (extra_compile_files) in + // the build directory, next to the source; extractHeaders() puts it in /lib + const libDir = path.join(dirs.headerDir, 'lib'); + const includeDirs = [dirs.sourceDir, libDir]; + // -D and -I from extra_preproc_args; '.' is the build directory const extra: string[] = params.extra_preproc_args || []; for (let i = 0; i < extra.length - 1; i++) { if (extra[i] === '-D') defines.push(extra[++i]); else if (extra[i] === '-I') { const dir = extra[++i]; - includeDirs.push(dir === '.' ? path.join(dirs.headerDir, 'lib') - : dir.replace(/^\/share/, dirs.headerDir)); + if (dir !== '.') includeDirs.push(dir.replace(/^\/share/, dirs.headerDir)); } } // the first of the tool's include dirs, as extractHeaders() wrote it diff --git a/extension/test/cheaders.test.ts b/extension/test/cheaders.test.ts index dfb5476d..eb0498e4 100644 --- a/extension/test/cheaders.test.ts +++ b/extension/test/cheaders.test.ts @@ -44,6 +44,11 @@ describe('clangConfig', function () { assert.ok(coleco.includeDirs.includes('/h/include/coleco')); }); + it('always searches the lib directory, as the worker stages it', function () { + const c = clangConfig('cc65', 'nes', DIRS)!; + assert.ok(c.includeDirs.includes(path.join('/h', 'lib'))); + }); + it('uses gbz80 defines on the Game Boy', function () { const c = clangConfig('sdcc', 'gb', DIRS)!; assert.ok(c.defines.includes('__SDCC_gbz80')); @@ -65,6 +70,10 @@ describe('extractHeaders', function () { assert.ok(!/extern void \w+\[\]/.test(c64)); const lib = extractHeaders(WORKER, 'sdcc', 'msx-libcv', out); assert.ok(lib.some((f) => f.endsWith(path.join('lib', 'cv.h')))); + const nes = extractHeaders(WORKER, 'cc65', 'nes', out); + assert.ok(nes.some((f) => f.endsWith(path.join('lib', 'neslib.h')))); + const gb = extractHeaders(WORKER, 'sdcc', 'gb', out); + assert.ok(gb.some((f) => f.endsWith(path.join('lib', 'gb', 'gb.h')))); } finally { fs.rmSync(out, { recursive: true, force: true }); } diff --git a/presets/gb/banking.c b/presets/gb/banking.c index 7ccf693c..58520c2e 100644 --- a/presets/gb/banking.c +++ b/presets/gb/banking.c @@ -10,11 +10,8 @@ the ROM size in the header. We pick an MBC5 mapper here; the build defaults to MBC5 when a ROM has banks. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" //#link "bank1.c" //#link "bank2.c" -//#resource "gb/global.sgb" //#symbol ld GB_MAPPER=0x19 #include diff --git a/presets/gb/chase.c b/presets/gb/chase.c index 6f1cd3b2..66cec1f3 100644 --- a/presets/gb/chase.c +++ b/presets/gb/chase.c @@ -11,9 +11,6 @@ * Controls: D-pad move, Start = pause, A = start / continue. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include #include "gb/types.h" @@ -128,7 +125,6 @@ byte sfx_vol; byte sfx_busy; /* ch1 claimed by SFX */ byte hw_note[3]; /* last note pushed to hardware (avoid retrigger clicks) */ - /* * Level maps — exact NES Chase layouts (from levelN_nam.h metatiles). * # wall * gem P player 1/2/3 enemies (space) void outside shape diff --git a/presets/gb/climber.c b/presets/gb/climber.c index ee51be5d..30f5ce06 100644 --- a/presets/gb/climber.c +++ b/presets/gb/climber.c @@ -25,10 +25,6 @@ Porting notes: beeps. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include #include "gb/types.h" diff --git a/presets/gb/clock.c b/presets/gb/clock.c index 30591bc0..7f6d6b7a 100644 --- a/presets/gb/clock.c +++ b/presets/gb/clock.c @@ -13,9 +13,6 @@ Reading the clock is the same on real hardware: The screen redraws only when the seconds change. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" //#symbol ld GB_MAPPER=0x10 #include diff --git a/presets/gb/galaxy.c b/presets/gb/galaxy.c index c70efbae..dfb561f8 100644 --- a/presets/gb/galaxy.c +++ b/presets/gb/galaxy.c @@ -33,10 +33,6 @@ Also uses fixed-point (fixed) positions/speeds, vsync, and the interrupt enable/disable helpers. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/gb/bcd.h b/presets/gb/gb/bcd.h deleted file mode 100644 index 76f503cf..00000000 --- a/presets/gb/gb/bcd.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef __BCD_H_INCLUDE -#define __BCD_H_INCLUDE - -#include "types.h" -#include - -/** @file bcd.h - Support for working with BCD (Binary Coded Decimal) - - See the example BCD project for additional details. -*/ - -// macro for creating BCD constants -#define BCD_HEX(v) ((BCD)(v)) - -/** Converts an integer value into BCD format - - A maximum of 8 digits may be used -*/ -#define MAKE_BCD(v) BCD_HEX(0x ## v) - -typedef uint32_t BCD; - -/** Converts integer __i__ into BCD format (Binary Coded Decimal) - @param i Numeric value to convert - @param value Pointer to a BCD variable to store the converted result -*/ -void uint2bcd(uint16_t i, BCD * value) OLDCALL; - -/** Adds two numbers in BCD format: __sour__ += __value__ - @param sour Pointer to a BCD value to add to (and where the result is stored) - @param value Pointer to the BCD value to add to __sour__ -*/ -void bcd_add(BCD * sour, const BCD * value) OLDCALL; - -/** Subtracts two numbers in BCD format: __sour__ -= __value__ - @param sour Pointer to a BCD value to subtract from (and where the result is stored) - @param value Pointer to the BCD value to subtract from __sour__ -*/ -void bcd_sub(BCD * sour, const BCD * value) OLDCALL; - -/** Convert a BCD number into an asciiz (null terminated) string and return the length - @param bcd Pointer to BCD value to convert - @param tile_offset Optional per-character offset value to add (use 0 for none) - @param buffer Buffer to store the result in - - Returns: Length in characters (always 8) - - __buffer__ should be large enough to store the converted string - (9 bytes: 8 characters + 1 for terminator) - - There are a couple different ways to use __tile_offset__. - For example: - \li It can be the Index of the Font Tile '0' in VRAM to - allow the buffer to be used directly with @ref set_bkg_tiles. - \li It can also be set to the ascii value for character '0' - so that the buffer is a normal string that can be passed to @ref printf. -*/ -uint8_t bcd2text(const BCD * bcd, uint8_t tile_offset, uint8_t * buffer) OLDCALL; - -#endif diff --git a/presets/gb/gbc.c b/presets/gb/gbc.c index 169d6794..3dc5ccc4 100644 --- a/presets/gb/gbc.c +++ b/presets/gb/gbc.c @@ -15,10 +15,6 @@ _cpu to see which machine it is running on. On a CGB it: On a monochrome Game Boy it falls back to the four-shade DMG palettes. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/gbdecompress.c b/presets/gb/gbdecompress.c index 6415123a..c77dace9 100644 --- a/presets/gb/gbdecompress.c +++ b/presets/gb/gbdecompress.c @@ -12,10 +12,6 @@ gb_decompress() returns the decompressed size in bytes, and the tile count is that divided by 16. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/hello.c b/presets/gb/hello.c index e05f7e14..85dcec2e 100644 --- a/presets/gb/hello.c +++ b/presets/gb/hello.c @@ -6,10 +6,6 @@ then turn on the LCD. Ported from presets/nes/hello.c, using GBDK (gb/gb.h). */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/interrupts.c b/presets/gb/interrupts.c index cd9110c0..84a97d27 100644 --- a/presets/gb/interrupts.c +++ b/presets/gb/interrupts.c @@ -12,10 +12,6 @@ much faster than the VBlank. Press START to remove both handlers, then press it again to add them back. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/joypad.c b/presets/gb/joypad.c index 51c6a907..0aed2cb1 100644 --- a/presets/gb/joypad.c +++ b/presets/gb/joypad.c @@ -11,10 +11,6 @@ This demo waits for START, then moves a sprite with the d-pad while counting A and B presses with edge detection. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/large_map.c b/presets/gb/large_map.c index b9821620..69be97e7 100644 --- a/presets/gb/large_map.c +++ b/presets/gb/large_map.c @@ -16,10 +16,6 @@ Notes on the port: - The map/tile data lives in bigmap.h (generated with png2asset). */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/metasprites.c b/presets/gb/metasprites.c index 72f4d4c3..ca91dca9 100644 --- a/presets/gb/metasprites.c +++ b/presets/gb/metasprites.c @@ -9,10 +9,6 @@ The Game Boy OAM holds 40 sprites, so 10 actors of 4 sprites each fit on screen (the NES fits 16). */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include #include "gb/types.h" diff --git a/presets/gb/music.c b/presets/gb/music.c index ab6f13ab..7a20afd3 100644 --- a/presets/gb/music.c +++ b/presets/gb/music.c @@ -15,9 +15,6 @@ * exactly like the NES player routes low notes to the triangle. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include #include "gb/types.h" diff --git a/presets/gb/paint.c b/presets/gb/paint.c index ac1ac878..b90a464d 100644 --- a/presets/gb/paint.c +++ b/presets/gb/paint.c @@ -11,10 +11,6 @@ switch_data(), which also saves the replaced tile so the menu can be restored when it closes. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include #include "gb/types.h" diff --git a/presets/gb/pakupaku.c b/presets/gb/pakupaku.c index f788c3a5..1fef17e6 100644 --- a/presets/gb/pakupaku.c +++ b/presets/gb/pakupaku.c @@ -6,9 +6,6 @@ * Collect dots; power pellet makes the ghost edible. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/raster.c b/presets/gb/raster.c index 220fa5bc..2bfd3bc3 100644 --- a/presets/gb/raster.c +++ b/presets/gb/raster.c @@ -13,10 +13,6 @@ next interrupt. The table phase advances every two frames, so the wave travels across the screen. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/scroll.c b/presets/gb/scroll.c index f022123b..bf2f32bc 100644 --- a/presets/gb/scroll.c +++ b/presets/gb/scroll.c @@ -8,10 +8,6 @@ view up and down through the whole map. Ported from presets/nes/scroll.c, using GBDK (gb/gb.h). */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/siegegame.c b/presets/gb/siegegame.c index 3692af0b..a071450b 100644 --- a/presets/gb/siegegame.c +++ b/presets/gb/siegegame.c @@ -23,9 +23,6 @@ Porting notes: Controls: D-pad moves player 1, Start starts the game. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include #include diff --git a/presets/gb/skeleton.sdcc b/presets/gb/skeleton.sdcc index e05f7e14..85dcec2e 100644 --- a/presets/gb/skeleton.sdcc +++ b/presets/gb/skeleton.sdcc @@ -6,10 +6,6 @@ then turn on the LCD. Ported from presets/nes/hello.c, using GBDK (gb/gb.h). */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/sprites.c b/presets/gb/sprites.c index 849a10c6..ff3ee00f 100644 --- a/presets/gb/sprites.c +++ b/presets/gb/sprites.c @@ -9,10 +9,6 @@ is the same tile data the objects use, so we can point the sprites at them too. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include #include "gb/types.h" diff --git a/presets/gb/testdrawing.c b/presets/gb/testdrawing.c index 49ed08e6..8ecca4be 100644 --- a/presets/gb/testdrawing.c +++ b/presets/gb/testdrawing.c @@ -15,9 +15,6 @@ pixel one row up at a time. This is slow, since each call to plot_point() reads and writes a byte of pixel memory. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include #include "gb/types.h" diff --git a/presets/gb/testphys.c b/presets/gb/testphys.c index b37a633a..d3aace63 100644 --- a/presets/gb/testphys.c +++ b/presets/gb/testphys.c @@ -7,9 +7,6 @@ are the sub-pixel fraction, dropped with >> 4 when the sprite is moved. Arrow keys apply thrust; holding A gives a jump burst. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include #include "gb/types.h" diff --git a/presets/gb/tiles.c b/presets/gb/tiles.c index 7fa9e8bd..7cabcfbe 100644 --- a/presets/gb/tiles.c +++ b/presets/gb/tiles.c @@ -1,7 +1,4 @@ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" #include #include #include "gb/types.h" diff --git a/presets/gb/typewriter.c b/presets/gb/typewriter.c index 6e3c9fbe..4fcbd6cc 100644 --- a/presets/gb/typewriter.c +++ b/presets/gb/typewriter.c @@ -16,10 +16,6 @@ The font is the 8x8 text font from gbtext.h. Its tile numbers equal ASCII codes, so the characters can go straight into the map. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/vram.c b/presets/gb/vram.c index 0e1e08a1..c354dcf5 100644 --- a/presets/gb/vram.c +++ b/presets/gb/vram.c @@ -13,10 +13,6 @@ This demo exercises the low-level access helpers: - hide_sprite() hides the objects outside a moving four-sprite window. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include "gb/types.h" #include "gb/hardware.h" diff --git a/presets/gb/window.c b/presets/gb/window.c index ecd2eb87..f893454e 100644 --- a/presets/gb/window.c +++ b/presets/gb/window.c @@ -10,10 +10,6 @@ a row of icons with set_win_submap(), then slides the whole bar in from the left with scroll_win() while the background scrolls behind it. */ -//#link "gb/sfr.sgb" -//#link "gb/crt0.sgb" -//#resource "gb/global.sgb" - #include #include #include "gb/types.h" diff --git a/src/common/workertypes.ts b/src/common/workertypes.ts index 31ecf6dc..7316bb12 100644 --- a/src/common/workertypes.ts +++ b/src/common/workertypes.ts @@ -128,6 +128,9 @@ export interface WorkerMessage { /** read a file from the preloaded filesystem package (path e.g. '/include/nes.h') * requires preload_fs; result comes back as {output:Uint8Array} */ readshared?:string + /** read a header from the platform's library (one of its extra_compile_files, + * e.g. 'neslib.h'); requires platform; result comes back as {output:Uint8Array|null} */ + readlib?:string /** list files under a directory of the preloaded filesystem package * requires preload_fs; result comes back as {output:string[]} */ listshared?:string diff --git a/src/ide/views/editors.ts b/src/ide/views/editors.ts index 9d05d14a..97c8269b 100644 --- a/src/ide/views/editors.ts +++ b/src/ide/views/editors.ts @@ -1018,12 +1018,28 @@ export function getLanguageForFilename(fn: string): Extension | null { return null; } -// Look up an include file inside the toolchain's preload filesystem -// (e.g. /include/nes.h inside the cc65 package), via the worker. -// Results are cached per filesystem+filename. +// Look up an include file the build gets from outside the project, via the +// worker: first the platform's library (src/worker/lib, e.g. neslib.h or +// gb/gb.h), then the toolchain's preload filesystem (e.g. /include/nes.h +// inside the cc65 package). Results are cached per source+filename. const sharedFileCache = new Map>(); -export function lookupSharedFileText(fn: string): Promise { +async function queryWorkerText(msg: WorkerMessage): Promise { + var result = await current_project.queryWorker(msg); + var output = result && (result as any).output; + return output instanceof Uint8Array && output.length > 0 ? new TextDecoder().decode(output) : null; +} + +function lookupLibraryFileText(fn: string): Promise { + const platform = current_project.platform_id; + const key = 'lib:' + platform + ':' + fn; + if (!sharedFileCache.has(key)) { + sharedFileCache.set(key, queryWorkerText({ platform, readlib: fn, updates: [], buildsteps: [] })); + } + return sharedFileCache.get(key); +} + +function lookupToolchainFileText(fn: string): Promise { const tool = current_project.getToolForFilename(current_project.mainPath); const fsName = getSharedFileSystemName(tool, current_project.platform_id); const dirs = getIncludeDirs(tool, current_project.platform_id); @@ -1036,12 +1052,8 @@ export function lookupSharedFileText(fn: string): Promise { // e.g. "headers/vcs.h") var candidates = [...dirs.map(dir => dir + '/' + fn), fn]; for (var path of candidates) { - var msg = { preload_fs: fsName, readshared: path, updates: [], buildsteps: [] } as WorkerMessage; - var result = await current_project.queryWorker(msg); - var output = result && (result as any).output; - if (output instanceof Uint8Array && output.length > 0) { - return new TextDecoder().decode(output); - } + var text = await queryWorkerText({ preload_fs: fsName, readshared: path, updates: [], buildsteps: [] }); + if (text != null) return text; } return null; })()); @@ -1049,8 +1061,13 @@ export function lookupSharedFileText(fn: string): Promise { return sharedFileCache.get(key); } -// Read-only viewer for toolchain include files (headers inside the tool's -// preload filesystem), opened by clicking the badge next to an #include line. +export async function lookupSharedFileText(fn: string): Promise { + return (await lookupLibraryFileText(fn)) ?? lookupToolchainFileText(fn); +} + +// Read-only viewer for include files from outside the project (the platform +// library in src/worker/lib, or the tool's preload filesystem), opened by +// clicking the badge next to an #include line. // Project files are linked to their own editor windows instead. // Each opened header gets its own window, id '#headerview/'. export class HeaderView implements ProjectView { @@ -1125,7 +1142,7 @@ export class HeaderView implements ProjectView { this.setHeaderText(current_project.getFile(path) as string); return; } - // 2) toolchain preload filesystem (via worker) + // 2) platform library, then toolchain preload filesystem (via worker) var text = await lookupSharedFileText(fn); if (this.requestedFn !== fn) return; // a newer request superseded us if (text != null) { @@ -1137,7 +1154,7 @@ export class HeaderView implements ProjectView { this.currentPath = fn; this.setHeaderText('// ' + fn + ' was not found.\n' + '// Project include files are loaded during a build -- try building first.\n' - + '// Toolchain headers are only available when the tool has a bundled filesystem.'); + + '// Library and toolchain headers are only available when the platform or tool bundles them.'); } /** Jump to a line in the header (after content loads). */ diff --git a/src/tools/buildpresets.ts b/src/tools/buildpresets.ts index ae52e330..e72a849d 100644 --- a/src/tools/buildpresets.ts +++ b/src/tools/buildpresets.ts @@ -40,6 +40,7 @@ import { EmuTarget, VideoOutput, captureRejectionListeners, dropAbortHandlers, i import { EmuHalt, PLATFORMS } from '../common/emu'; import { getSkeletonName, getToolMeta } from '../common/toolmeta'; import { getBasePlatform } from '../common/util'; +import { PLATFORM_PARAMS } from '../worker/platforms'; import { c } from './cliformat'; const PRESETS_DIR = 'presets'; @@ -181,6 +182,24 @@ function listSkeletons(dir: string, platform: string, skelTools: { [name: string // (.mame, .wasm, -defender) repeat their parent's list, so the same file can // be named several times; it is built once, under the first platform that // claims it. +/** + * Preset files that shadow a platform library file: the worker stages + * `extra_compile_files` from src/worker/lib/, so a same-named file in + * presets/ would be copied into new projects and win over it. + * Library files go in src/worker/lib only (doc/notes/sysroot.md, Tiers). + */ +export function libraryCollisions(presetsDir: string, params: { [platform: string]: any }): string[] { + const found = new Set(); + for (const id of Object.keys(params).sort()) { + for (const file of params[id].extra_compile_files || []) { + const relpath = getBasePlatform(id) + '/' + file; + if (fs.existsSync(path.join(presetsDir, relpath))) + found.add(`${presetsDir}/${relpath} shadows library file src/worker/lib/${relpath}`); + } + } + return [...found]; +} + export async function listPresets( filter?: string, platform?: string, warn: (s: string) => void = () => { }, @@ -252,6 +271,7 @@ export async function listPresets( for (const id of Object.keys(missing).sort()) { err(`platform ${id}: ${missing[id]} preset(s) not in presets/${getBasePlatform(id)}/`); } + for (const e of libraryCollisions(PRESETS_DIR, PLATFORM_PARAMS)) err(e); found.sort((a, b) => a.preset < b.preset ? -1 : a.preset > b.preset ? 1 : 0); return found; } @@ -662,7 +682,7 @@ async function main() { for (const w of warnings) console.log(dim(`note: ${w}`)); for (const e of errors) console.log(red(`error: ${e}`)); if (errors.length) { - console.error(red(`aborting: ${errors.length} platform(s) list preset files that don't exist`)); + console.error(red(`aborting: ${errors.length} problem(s) with the presets tree`)); } console.log(bold(`${run ? 'building and running' : 'building'} ${presets.length} presets...`)); const results = await buildAllPresets({ diff --git a/src/worker/builder.ts b/src/worker/builder.ts index 9ec8d1e5..143555b8 100644 --- a/src/worker/builder.ts +++ b/src/worker/builder.ts @@ -285,20 +285,27 @@ export function getWorkFileAsString(path: string): string { return store.getFileAsString(path); } +/** Create the directories above `path` (a/b/c.h -> a, a/b). */ +function makeParentDirs(fs, path: string) { + var toks = path.split('/'); + for (var i = 1; i < toks.length; i++) { + var dir = toks.slice(0, i).join('/'); + try { + fs.mkdir(dir); + } catch (e) { + // emscripten's FS throws EEXIST, whose errno differs between builds + if (!(fs.analyzePath && fs.analyzePath(dir).exists)) throw e; + } + } +} + export function populateEntry(fs, path: string, entry: FileEntry, options: BuildOptions) { var data = entry.data; if (options && options.processFn) { data = options.processFn(path, data); } data = fixLineEndings(data); - // create subfolders - var toks = path.split('/'); - if (toks.length > 1) { - for (var i = 0; i < toks.length - 1; i++) - try { - fs.mkdir(toks[i]); - } catch (e) { } - } + makeParentDirs(fs, path); // write file fs.writeFile(path, data, { encoding: entry.encoding }); var time = new Date(entry.ts); @@ -360,29 +367,41 @@ export function populateExtraFiles(step: BuildStep, fs, extrafiles) { if (extrafiles) { for (var i = 0; i < extrafiles.length; i++) { var xfn = extrafiles[i]; + makeParentDirs(fs, xfn); // is this file cached? if (store.workfs[xfn]) { - fs.writeFile(xfn, store.workfs[xfn].data, { encoding: 'binary' }); + // may be the project's own file of that name, as text + fs.writeFile(xfn, store.workfs[xfn].data, { encoding: store.workfs[xfn].encoding }); continue; } - // fetch from network - var xpath = "lib/" + getBasePlatform(step.platform) + "/" + xfn; - var xhr = new XMLHttpRequest(); - xhr.responseType = 'arraybuffer'; - xhr.open("GET", PWORKER + xpath, false); // synchronous request - xhr.send(null); - if (xhr.response && xhr.status == 200) { - var data = new Uint8Array(xhr.response); - fs.writeFile(xfn, data, { encoding: 'binary' }); - putWorkFile(xfn, data); - console.log(":::", xfn, data.length); - } else { - throw Error("Could not load extra file " + xpath); - } + var data = fetchLibraryFile(step.platform, xfn); + if (!data) throw Error("Could not load extra file lib/" + getBasePlatform(step.platform) + "/" + xfn); + fs.writeFile(xfn, data, { encoding: 'binary' }); + putWorkFile(xfn, data); + console.log(":::", xfn, data.length); } } } +/** A file from the platform's library, src/worker/lib//, or null. */ +export function fetchLibraryFile(platform: string, name: string): Uint8Array | null { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + xhr.open("GET", PWORKER + "lib/" + getBasePlatform(platform) + "/" + name, false); // synchronous request + xhr.send(null); + return xhr.response && xhr.status == 200 ? new Uint8Array(xhr.response) : null; +} + +/** + * A header the platform's library provides (listed in extra_compile_files, + * which builds copy in), or null -- for the IDE's read-only header view. + */ +export function readLibraryHeader(platform: string, name: string): Uint8Array | null { + var params = PLATFORM_PARAMS[platform] || PLATFORM_PARAMS[getBasePlatform(platform)]; + var listed: string[] = (params && params.extra_compile_files) || []; + return listed.includes(name) ? fetchLibraryFile(platform, name) : null; +} + // see if any target file compares to the inputs in the given direction function targetCompare(step: BuildStep, targets: string[], isNewer: (inputts: number, targetts: number) => boolean) { if (!step.maxts) throw Error("call populateFiles() first"); @@ -411,9 +430,9 @@ export function anyTargetChanged(step: BuildStep, targets: string[]) { * interrupt vectors, so linking it against crt0.o (which has vectors of its * own) and the bank-switched config its C programs use can only collide. A * platform spells the difference out with asm_-prefixed copies of the link - * params -- asm_cfgfile, asm_libargs, asm_extra_link_files -- which the - * assembler applies when the project's main file is its own source, and which - * nothing else looks at. Runs before fixParamsWithDefines() so that a source + * params -- asm_cfgfile, asm_libargs, asm_extra_link_files, asm_startup_objs + * -- which the assembler (ca65, sdas) applies when the project's main file is + * its own source, and which nothing else looks at. Runs before fixParamsWithDefines() so that a source * file's own directive (//#tooldef ... cfgfile=, or a legacy CFGFILE define) * still has the last word. */ diff --git a/src/worker/lib/gb/Makefile b/src/worker/lib/gb/Makefile new file mode 100644 index 00000000..ea65f214 --- /dev/null +++ b/src/worker/lib/gb/Makefile @@ -0,0 +1,23 @@ +# Rebuild the GBDK runtime objects linked first into every Game Boy C +# program (startup_objs in src/worker/platforms.ts): +# +# crt0.rel - startup code, interrupt vectors, header (crt0.sgb, global.sgb) +# sfr.rel - hardware register symbols (sfr.sgb) +# +# The bundled sdasgb writes the XL3 format gb.lib uses; a system sdasgb +# (4.x) writes XL4, so build through 8bws. A project that links its own +# crt0.sgb replaces crt0.rel, which is how this build gets the sources in. +# Run npm run tsbuild first. + +ROOT = ../../../.. + +all: crt0.rel sfr.rel + +crt0.rel sfr.rel: crt0.sgb sfr.sgb global.sgb + set -e; tmp=$$(mktemp -d "$${TMPDIR:-/tmp}/gbrt.XXXXXX"); \ + cp crt0.sgb sfr.sgb global.sgb $$tmp/; \ + printf '//#link "sfr.sgb"\n//#link "crt0.sgb"\nvoid main(void) {}\n' > $$tmp/gbrt.c; \ + saved=$$(cd $(ROOT) && node gen/tools/8bws.js build --json --platform gb --save $$tmp/gbrt.c -o $$tmp/gbrt.gb \ + | node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(0)).data.saveDir)'); \ + for f in crt0.rel crt0.lst sfr.rel sfr.lst; do cp "$$saved/$$f" .; done; \ + rm -rf $$tmp "$$saved" diff --git a/src/worker/lib/gb/crt0.lst b/src/worker/lib/gb/crt0.lst new file mode 100644 index 00000000..bac208d7 --- /dev/null +++ b/src/worker/lib/gb/crt0.lst @@ -0,0 +1,1054 @@ + 1 .include "global.sgb" + 000001 1 .NEAR_CALLS = 1 ; - tag so that sed can change this + 2 + 3 ;; Changed by astorgb.pl to 1 + 000000 4 __RGBDS__ = 0 + 5 + 008000 6 _VRAM = 0x8000 ; $8000->$9FFF + 008000 7 _VRAM8000 = 0x8000 + 008800 8 _VRAM8800 = 0x8800 + 009000 9 _VRAM9000 = 0x9000 + 009800 10 _SCRN0 = 0x9800 ; $9800->$9BFF + 009C00 11 _SCRN1 = 0x9C00 ; $9C00->$9FFF + 00A000 12 _SRAM = 0xA000 ; $A000->$BFFF + 00C000 13 _RAM = 0xC000 ; $C000->$CFFF / $C000->$DFFF + 00D000 14 _RAMBANK = 0xD000 ; $D000->$DFFF + 00FE00 15 _OAMRAM = 0xFE00 ; $FE00->$FE9F + 00FF00 16 _IO = 0xFF00 ; $FF00->$FF7F,$FFFF + 00FF30 17 _AUD3WAVERAM = 0xFF30 ; $FF30->$FF3F + 00FF80 18 _HRAM = 0xFF80 ; $FF80->$FFFE + 19 + 20 ;; MBC Equates + 21 + 002000 22 .MBC1_ROM_PAGE = 0x2000 ; Address to write to for MBC1 switching + 002000 23 .MBC_ROM_PAGE = 0x2000 ; Default platform MBC rom switching address + 24 + 000000 25 rRAMG = 0x0000 ; $0000->$1fff + 002000 26 rROMB0 = 0x2000 ; $2000->$2fff + 003000 27 rROMB1 = 0x3000 ; $3000->$3fff - If more than 256 ROM banks are present. + 004000 28 rRAMB = 0x4000 ; $4000->$5fff - Bit 3 enables rumble (if present) + 29 + 30 ;; Keypad + 000004 31 .UP = 0x04 + 000008 32 .DOWN = 0x08 + 000002 33 .LEFT = 0x02 + 000001 34 .RIGHT = 0x01 + 000010 35 .A = 0x10 + 000020 36 .B = 0x20 + 000040 37 .SELECT = 0x40 + 000080 38 .START = 0x80 + 39 + 000010 40 .P14 = 0x10 + 000020 41 .P15 = 0x20 + 42 + 43 ;; Screen dimensions + 000013 44 .MAXCURSPOSX = 0x13 ; In tiles + 000011 45 .MAXCURSPOSY = 0x11 + 46 + 0000A0 47 .SCREENWIDTH = 0xA0 + 000090 48 .SCREENHEIGHT = 0x90 + 000007 49 .MINWNDPOSX = 0x07 + 000000 50 .MINWNDPOSY = 0x00 + 0000A6 51 .MAXWNDPOSX = 0xA6 + 00008F 52 .MAXWNDPOSY = 0x8F + 53 + 54 ;; Hardware registers + 55 + 000000 56 .P1 = 0x00 ; Joystick: 1.1.P15.P14.P13.P12.P11.P10 + 00FF00 57 rP1 = 0xFF00 + 58 + 000020 59 P1F_5 = 0b00100000 ; P15 out port, set to 0 to get buttons + 000010 60 P1F_4 = 0b00010000 ; P14 out port, set to 0 to get dpad + 000008 61 P1F_3 = 0b00001000 ; P13 in port + 000004 62 P1F_2 = 0b00000100 ; P12 in port + 000002 63 P1F_1 = 0b00000010 ; P11 in port + 000001 64 P1F_0 = 0b00000001 ; P10 in port + 65 + 000020 66 P1F_GET_DPAD = 0b00100000 + 000010 67 P1F_GET_BTN = 0b00010000 + 000030 68 P1F_GET_NONE = 0b00110000 + 69 + 000001 70 .SB = 0x01 ; Serial IO data buffer + 00FF01 71 rSB = 0xFF01 + 72 + 000002 73 .SC = 0x02 ; Serial IO control register + 00FF02 74 rSC = 0xFF02 + 75 + 000004 76 .DIV = 0x04 ; Divider register + 00FF04 77 rDIV = 0xFF04 + 78 + 000005 79 .TIMA = 0x05 ; Timer counter + 00FF05 80 rTIMA = 0xFF05 + 81 + 000006 82 .TMA = 0x06 ; Timer modulo + 00FF06 83 rTMA = 0xFF06 + 84 + 000007 85 .TAC = 0x07 ; Timer control + 00FF07 86 rTAC = 0xFF07 + 87 + 000004 88 TACF_START = 0b00000100 + 000000 89 TACF_STOP = 0b00000000 + 000000 90 TACF_4KHZ = 0b00000000 + 000003 91 TACF_16KHZ = 0b00000011 + 000002 92 TACF_65KHZ = 0b00000010 + 000001 93 TACF_262KHZ = 0b00000001 + 94 + 00000F 95 .IF = 0x0F ; Interrupt flags: 0.0.0.JST.SIO.TIM.LCD.VBL + 00FF0F 96 rIF = 0xFF0F + 97 + 000010 98 .NR10 = 0x10 ; Sound register + 00FF10 99 rNR10 = 0xFF10 + 00FF10 100 rAUD1SWEEP = 0xFF10 + 101 + 000000 102 AUD1SWEEP_UP = 0b00000000 + 000008 103 AUD1SWEEP_DOWN = 0b00001000 + 104 + 000011 105 .NR11 = 0x11 ; Sound register + 00FF11 106 rNR11 = 0xFF11 + 00FF11 107 rAUD1LEN = 0xFF11 + 108 + 000012 109 .NR12 = 0x12 ; Sound register + 00FF12 110 rNR12 = 0xFF12 + 00FF12 111 rAUD1ENV = 0xFF12 + 112 + 000013 113 .NR13 = 0x13 ; Sound register + 00FF13 114 rNR13 = 0xFF13 + 00FF13 115 rAUD1LOW = 0xFF13 + 116 + 000014 117 .NR14 = 0x14 ; Sound register + 00FF14 118 rNR14 = 0xFF14 + 00FF14 119 rAUD1HIGH = 0xFF14 + 120 + 000016 121 .NR21 = 0x16 ; Sound register + 00FF16 122 rNR21 = 0xFF16 + 00FF16 123 rAUD2LEN = 0xFF16 + 124 + 000017 125 .NR22 = 0x17 ; Sound register + 00FF17 126 rNR22 = 0xFF17 + 00FF17 127 rAUD2ENV = 0xFF17 + 128 + 000018 129 .NR23 = 0x18 ; Sound register + 00FF18 130 rNR23 = 0xFF18 + 00FF18 131 rAUD2LOW = 0xFF18 + 132 + 000019 133 .NR24 = 0x19 ; Sound register + 00FF19 134 rNR24 = 0xFF19 + 00FF19 135 rAUD2HIGH = 0xFF19 + 136 + 00001A 137 .NR30 = 0x1A ; Sound register + 00FF1A 138 rNR30 = 0xFF1A + 00FF1A 139 rAUD3ENA = 0xFF1A + 140 + 00001B 141 .NR31 = 0x1B ; Sound register + 00FF1B 142 rNR31 = 0xFF1B + 00FF1B 143 rAUD3LEN = 0xFF1B + 144 + 00001C 145 .NR32 = 0x1C ; Sound register + 00FF1C 146 rNR32 = 0xFF1C + 00FF1C 147 rAUD3LEVEL = 0xFF1C + 148 + 00001D 149 .NR33 = 0x1D ; Sound register + 00FF1D 150 rNR33 = 0xFF1D + 00FF1D 151 rAUD3LOW = 0xFF1D + 152 + 00001E 153 .NR34 = 0x1E ; Sound register + 00FF1E 154 rNR34 = 0xFF1E + 00FF1E 155 rAUD3HIGH = 0xFF1E + 156 + 000020 157 .NR41 = 0x20 ; Sound register + 00FF20 158 rNR41 = 0xFF20 + 00FF20 159 rAUD4LEN = 0xFF20 + 160 + 000021 161 .NR42 = 0x21 ; Sound register + 00FF21 162 rNR42 = 0xFF21 + 00FF21 163 rAUD4ENV = 0xFF21 + 164 + 000022 165 .NR43 = 0x22 ; Sound register + 00FF22 166 rNR43 = 0xFF22 + 00FF22 167 rAUD4POLY = 0xFF22 + 168 + 000023 169 .NR44 = 0x23 ; Sound register + 00FF23 170 rNR44 = 0xFF23 + 00FF23 171 rAUD4GO = 0xFF23 + 172 + 000024 173 .NR50 = 0x24 ; Sound register + 00FF24 174 rNR50 = 0xFF24 + 00FF24 175 rAUDVOL = 0xFF24 + 176 + 000080 177 AUDVOL_VIN_LEFT = 0b10000000 ; SO2 + 000008 178 AUDVOL_VIN_RIGHT = 0b00001000 ; SO1 + 179 + 000025 180 .NR51 = 0x25 ; Sound register + 00FF25 181 rNR51 = 0xFF25 + 00FF25 182 rAUDTERM = 0xFF25 + 183 + 000080 184 AUDTERM_4_LEFT = 0b10000000 + 000040 185 AUDTERM_3_LEFT = 0b01000000 + 000020 186 AUDTERM_2_LEFT = 0b00100000 + 000010 187 AUDTERM_1_LEFT = 0b00010000 + 000008 188 AUDTERM_4_RIGHT = 0b00001000 + 000004 189 AUDTERM_3_RIGHT = 0b00000100 + 000002 190 AUDTERM_2_RIGHT = 0b00000010 + 000001 191 AUDTERM_1_RIGHT = 0b00000001 + 192 + 000026 193 .NR52 = 0x26 ; Sound register + 00FF26 194 rNR52 = 0xFF26 + 00FF26 195 rAUDENA = 0xFF26 + 196 + 000080 197 AUDENA_ON = 0b10000000 + 000000 198 AUDENA_OFF = 0b00000000 ; sets all audio regs to 0! + 199 + 000040 200 .LCDC = 0x40 ; LCD control + 00FF40 201 rLCDC = 0xFF40 + 202 + 000000 203 LCDCF_OFF = 0b00000000 ; LCD Control Operation + 000080 204 LCDCF_ON = 0b10000000 ; LCD Control Operation + 000000 205 LCDCF_WIN9800 = 0b00000000 ; Window Tile Map Display Select + 000040 206 LCDCF_WIN9C00 = 0b01000000 ; Window Tile Map Display Select + 000000 207 LCDCF_WINOFF = 0b00000000 ; Window Display + 000020 208 LCDCF_WINON = 0b00100000 ; Window Display + 000000 209 LCDCF_BG8800 = 0b00000000 ; BG & Window Tile Data Select + 000010 210 LCDCF_BG8000 = 0b00010000 ; BG & Window Tile Data Select + 000000 211 LCDCF_BG9800 = 0b00000000 ; BG Tile Map Display Select + 000008 212 LCDCF_BG9C00 = 0b00001000 ; BG Tile Map Display Select + 000000 213 LCDCF_OBJ8 = 0b00000000 ; OBJ Construction + 000004 214 LCDCF_OBJ16 = 0b00000100 ; OBJ Construction + 000000 215 LCDCF_OBJOFF = 0b00000000 ; OBJ Display + 000002 216 LCDCF_OBJON = 0b00000010 ; OBJ Display + 000000 217 LCDCF_BGOFF = 0b00000000 ; BG Display + 000001 218 LCDCF_BGON = 0b00000001 ; BG Display + 000007 219 LCDCF_B_ON = 7 + 000006 220 LCDCF_B_WIN9C00 = 6 + 000005 221 LCDCF_B_WINON = 5 + 000004 222 LCDCF_B_BG8000 = 4 + 000003 223 LCDCF_B_BG9C00 = 3 + 000002 224 LCDCF_B_OBJ16 = 2 + 000001 225 LCDCF_B_OBJON = 1 + 000000 226 LCDCF_B_BGON = 0 + 227 + 000041 228 .STAT = 0x41 ; LCD status + 00FF41 229 rSTAT = 0xFF41 + 230 + 000040 231 STATF_LYC = 0b01000000 ; LYC=LY Coincidence (Selectable) + 000020 232 STATF_MODE10 = 0b00100000 ; Mode 10 + 000010 233 STATF_MODE01 = 0b00010000 ; Mode 01 (V-Blank) + 000008 234 STATF_MODE00 = 0b00001000 ; Mode 00 (H-Blank) + 000004 235 STATF_LYCF = 0b00000100 ; Coincidence Flag + 000000 236 STATF_HBL = 0b00000000 ; H-Blank + 000001 237 STATF_VBL = 0b00000001 ; V-Blank + 000002 238 STATF_OAM = 0b00000010 ; OAM-RAM is used by system + 000003 239 STATF_LCD = 0b00000011 ; Both OAM and VRAM used by system + 000002 240 STATF_BUSY = 0b00000010 ; When set, VRAM access is unsafe + 000006 241 STATF_B_LYC = 6 + 000005 242 STATF_B_MODE10 = 5 + 000004 243 STATF_B_MODE01 = 4 + 000003 244 STATF_B_MODE00 = 3 + 000002 245 STATF_B_LYCF = 2 + 000000 246 STATF_B_VBL = 0 + 000001 247 STATF_B_OAM = 1 + 000001 248 STATF_B_BUSY = 1 + 249 + 000042 250 .SCY = 0x42 ; Scroll Y + 00FF42 251 rSCY = 0xFF42 + 252 + 000043 253 .SCX = 0x43 ; Scroll X + 00FF43 254 rSCX = 0xFF43 + 255 + 000044 256 .LY = 0x44 ; LCDC Y-coordinate + 00FF44 257 rLY = 0xFF44 + 258 + 000045 259 .LYC = 0x45 ; LY compare + 00FF45 260 rLYC = 0xFF45 + 261 + 000046 262 .DMA = 0x46 ; DMA transfer + 00FF46 263 rDMA = 0xFF46 + 264 + 000047 265 .BGP = 0x47 ; BG palette data + 00FF47 266 rBGP = 0xFF47 + 267 + 000048 268 .OBP0 = 0x48 ; OBJ palette 0 data + 00FF48 269 rOBP0 = 0xFF48 + 270 + 000049 271 .OBP1 = 0x49 ; OBJ palette 1 data + 00FF49 272 rOBP1 = 0xFF49 + 273 + 00004A 274 .WY = 0x4A ; Window Y coordinate + 00FF4A 275 rWY = 0xFF4A + 276 + 00004B 277 .WX = 0x4B ; Window X coordinate + 00FF4B 278 rWX = 0xFF4B + 279 + 00004D 280 .KEY1 = 0x4D ; CPU speed + 00FF4D 281 rKEY1 = 0xFF4D + 00FF4D 282 rSPD = 0xFF4D + 283 + 000080 284 KEY1F_DBLSPEED = 0b10000000 ; 0=Normal Speed, 1=Double Speed (R) + 000001 285 KEY1F_PREPARE = 0b00000001 ; 0=No, 1=Prepare (R/W) + 286 + 00004F 287 .VBK = 0x4F ; VRAM bank + 00FF4F 288 rVBK = 0xFF4F + 289 + 000051 290 .HDMA1 = 0x51 ; DMA control 1 + 00FF51 291 rHDMA1 = 0xFF51 + 292 + 000052 293 .HDMA2 = 0x52 ; DMA control 2 + 00FF52 294 rHDMA2 = 0xFF52 + 295 + 000053 296 .HDMA3 = 0x53 ; DMA control 3 + 00FF53 297 rHDMA3 = 0xFF53 + 298 + 000054 299 .HDMA4 = 0x54 ; DMA control 4 + 00FF54 300 rHDMA4 = 0xFF54 + 301 + 000055 302 .HDMA5 = 0x55 ; DMA control 5 + 00FF55 303 rHDMA5 = 0xFF55 + 304 + 000000 305 HDMA5F_MODE_GP = 0b00000000 ; General Purpose DMA (W) + 000080 306 HDMA5F_MODE_HBL = 0b10000000 ; HBlank DMA (W) + 307 + 000080 308 HDMA5F_BUSY = 0b10000000 ; 0=Busy (DMA still in progress), 1=Transfer complete (R) + 309 + 000056 310 .RP = 0x56 ; IR port + 00FF56 311 rRP = 0xFF56 + 312 + 0000C0 313 RPF_ENREAD = 0b11000000 + 000002 314 RPF_DATAIN = 0b00000010 ; 0=Receiving IR Signal, 1=Normal + 000001 315 RPF_WRITE_HI = 0b00000001 + 000000 316 RPF_WRITE_LO = 0b00000000 + 317 + 000068 318 .BCPS = 0x68 ; BG color palette specification + 00FF68 319 rBCPS = 0xFF68 + 320 + 000080 321 BCPSF_AUTOINC = 0b10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) + 322 + 000069 323 .BCPD = 0x69 ; BG color palette data + 00FF69 324 rBCPD = 0xFF69 + 325 + 00006A 326 .OCPS = 0x6A ; OBJ color palette specification + 00FF6A 327 rOCPS = 0xFF6A + 328 + 000080 329 OCPSF_AUTOINC = 0b10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) + 330 + 00006B 331 .OCPD = 0x6B ; OBJ color palette data + 00FF6B 332 rOCPD = 0xFF6B + 333 + 000070 334 .SVBK = 0x70 ; WRAM bank + 00FF70 335 rSVBK = 0xFF70 + 00FF70 336 rSMBK = 0xFF70 + 337 + 00FF76 338 rPCM12 = 0xFF76 + 339 + 00FF77 340 rPCM34 = 0xFF77 + 341 + 0000FF 342 .IE = 0xFF ; Interrupt enable + 00FFFF 343 rIE = 0xFFFF + 344 + 000001 345 .VBL_IFLAG = 0x01 + 000002 346 .LCD_IFLAG = 0x02 + 000004 347 .TIM_IFLAG = 0x04 + 000008 348 .SIO_IFLAG = 0x08 + 000010 349 .JOY_IFLAG = 0x10 + 350 + 000010 351 IEF_HILO = 0b00010000 ; Transition from High to Low of Pin number P10-P13 + 000008 352 IEF_SERIAL = 0b00001000 ; Serial I/O transfer end + 000004 353 IEF_TIMER = 0b00000100 ; Timer Overflow + 000002 354 IEF_STAT = 0b00000010 ; STAT + 000001 355 IEF_VBLANK = 0b00000001 ; V-Blank + 356 + 357 ;; Flags common to multiple sound channels + 358 + 000000 359 AUDLEN_DUTY_12_5 = 0b00000000 ; 12.5% + 000040 360 AUDLEN_DUTY_25 = 0b01000000 ; 25% + 000080 361 AUDLEN_DUTY_50 = 0b10000000 ; 50% + 0000C0 362 AUDLEN_DUTY_75 = 0b11000000 ; 75% + 363 + 000008 364 AUDENV_UP = 0b00001000 + 000000 365 AUDENV_DOWN = 0b00000000 + 366 + 000080 367 AUDHIGH_RESTART = 0b10000000 + 000040 368 AUDHIGH_LENGTH_ON = 0b01000000 + 000000 369 AUDHIGH_LENGTH_OFF = 0b00000000 + 370 + 371 ;; OAM related constants + 372 + 000028 373 OAM_COUNT = 40 ; number of OAM entries in OAM RAM + 374 + 000080 375 OAMF_PRI = 0b10000000 ; Priority + 000040 376 OAMF_YFLIP = 0b01000000 ; Y flip + 000020 377 OAMF_XFLIP = 0b00100000 ; X flip + 000000 378 OAMF_PAL0 = 0b00000000 ; Palette number; 0,1 (DMG) + 000010 379 OAMF_PAL1 = 0b00010000 ; Palette number; 0,1 (DMG) + 000000 380 OAMF_BANK0 = 0b00000000 ; Bank number; 0,1 (GBC) + 000008 381 OAMF_BANK1 = 0b00001000 ; Bank number; 0,1 (GBC) + 382 + 000007 383 OAMF_PALMASK = 0b00000111 ; Palette (GBC) + 384 + 000007 385 OAMB_PRI = 7 ; Priority + 000006 386 OAMB_YFLIP = 6 ; Y flip + 000005 387 OAMB_XFLIP = 5 ; X flip + 000004 388 OAMB_PAL1 = 4 ; Palette number; 0,1 (DMG) + 000003 389 OAMB_BANK1 = 3 ; Bank number; 0,1 (GBC) + 390 + 391 ;; CPU detection + 000001 392 .DMG_TYPE = 0x01 ; Original GB or Super GB + 0000FF 393 .MGB_TYPE = 0xFF ; Pocket GB or Super GB 2 + 000011 394 .CGB_TYPE = 0x11 ; Color GB + 395 + 396 ;; GBDK library screen modes + 397 + 000001 398 .G_MODE = 0x01 ; Graphic mode + 000002 399 .T_MODE = 0x02 ; Text mode (bit 2) + 000002 400 .T_MODE_OUT = 0x02 ; Text mode output only + 000003 401 .T_MODE_INOUT = 0x03 ; Text mode with input + 000004 402 .M_NO_SCROLL = 0x04 ; Disables scrolling of the screen in text mode + 000008 403 .M_NO_INTERP = 0x08 ; Disables special character interpretation + 404 + 405 ;; Status codes for IO + 000000 406 .IO_IDLE = 0x00 + 000001 407 .IO_SENDING = 0x01 + 000002 408 .IO_RECEIVING = 0x02 + 000004 409 .IO_ERROR = 0x04 + 410 + 411 ;; Type of IO data + 000066 412 .DT_IDLE = 0x66 + 000055 413 .DT_RECEIVING = 0x55 + 414 + 415 ;; Table of routines for modes + 0001E0 416 .MODE_TABLE = 0x01E0 + 417 + 418 ;; C related + 419 ;; Overheap of a banked call. Used for parameters + 420 ;; = ret + real ret + bank + 421 + 000001 422 .if .NEAR_CALLS + 000002 423 .BANKOV = 2 + 424 + 000000 425 .else + 426 .BANKOV = 6 + 427 + 428 .endif + 429 + 430 .globl __current_bank + 431 .globl __shadow_OAM_base + 432 + 433 ;; Global variables + 434 .globl .mode + 435 + 436 .globl __cpu + 437 .globl __is_GBA + 438 + 439 ;; Global routines + 440 ; .globl .set_mode ;; don't link mode.o by default + 441 + 442 .globl .reset + 443 + 444 .globl .display_off + 445 + 446 .globl .wait_vbl_done + 447 + 448 ;; Interrupt routines + 449 .globl .add_VBL + 450 ; .globl .add_LCD ;; don't link LCD.o by default + 451 ; .globl .add_TIM ;; don't link TIM.o by default + 452 ; .globl .add_SIO ;; don't link serial.o by default + 453 ; .globl .add_JOY ;; don't link JOY.o by default + 454 + 455 ;; Symbols defined at link time + 456 .globl .STACK + 457 .globl _shadow_OAM + 458 .globl .refresh_OAM + 459 + 460 ;; Main user routine + 461 .globl _main + 462 + 463 ;; Macro definitions + 464 + 465 .macro WAIT_STAT ?lbl + 466 lbl: LDH A, (.STAT) + 467 AND #STATF_BUSY ; Check if in LCD modes 0 or 1 + 468 JR NZ, lbl + 469 .endm + 470 + 471 .macro ADD_A_REG16 regH regL + 472 ADD regL + 473 LD regL, A + 474 ADC regH + 475 SUB regL + 476 LD regH, A + 477 .endm + 478 + 479 .macro SIGNED_ADD_A_REG16 regH regL ?lbl + 480 ; If A is negative, we need to subtract 1 from upper byte of 16-bit value + 481 BIT 7, A ; set z if a signed bit is 0 + 482 JR Z, lbl ; if z is set jump to positive + 483 dec regH ; if negative decrement upper byte + 484 lbl: + 485 ADD_A_REG16 regH, regL + 486 .endm + 487 + 488 .macro SIGNED_SUB_A_REG16 regH regL ?lbl + 489 ; negate A then add to 16-bit value + 490 CPL + 491 INC A + 492 SIGNED_ADD_A_REG16 regH, regL + 493 .endm + 494 + 495 .macro MUL_DE_BY_A_RET_HL ?lbl1 ?lbl2 + 496 ; Multiply DE by A, return result in HL; preserves: BC + 497 LD HL, #0 + 498 lbl1: + 499 SRL A + 500 JR NC, lbl2 + 501 ADD HL, DE + 502 lbl2: + 503 SLA E + 504 RL D + 505 OR A + 506 JR NZ, lbl1 + 507 .endm + 508 + 2 + 3 ;; **************************************** + 4 ;; Beginning of module + 5 ;; BANKED: checked + 6 .title "Runtime" + 7 .module Runtime + 8 .area _HEADER (ABS) + 9 + 10 ;; RST vectors + 11 ; .org 0x00 ; Trap, utilized by crash_handler.h + 12 + 13 ; .org 0x08 ; --profile handler utilized by emu_debug.h + 14 + 15 ; .org 0x10 ; empty + 16 + 17 ; .org 0x18 ; empty + 18 + 000020 19 .org 0x20 ; RST 0x20 == call HL + 000020 20 .call_hl:: + 000020 E9 [ 4] 21 JP (HL) + 22 + 000028 23 .org 0x28 ; zero up to 256 bytes in C pointed by HL + 000028 24 .MemsetSmall:: + 000028 22 [ 8] 25 LD (HL+),A + 000029 0D [ 4] 26 DEC C + 00002A 20 FC [12] 27 JR NZ,.MemsetSmall + 00002C C9 [16] 28 ret + 29 + 000030 30 .org 0x30 ; copy up to 256 bytes in C from DE to HL + 000030 31 .MemcpySmall:: + 000030 1A [ 8] 32 LD A, (DE) + 000031 22 [ 8] 33 LD (HL+), A + 000032 13 [ 8] 34 INC DE + 000033 0D [ 4] 35 DEC C + 000034 20 FA [12] 36 JR NZ,.MemcpySmall + 000036 C9 [16] 37 RET + 38 + 39 ; .org 0x38 ; crash handler utilized by crash_handler.h + 40 + 41 ;; Hardware interrupt vectors + 000040 42 .org 0x40 ; VBL + 000040 43 .int_VBL: + 000040 F5 [16] 44 PUSH AF + 000041 E5 [16] 45 PUSH HL + 000042 21 A5 C0 [12] 46 LD HL,#.int_0x40 + 000045 C3 80 00 [16] 47 JP .int + 48 + 49 ; .org 0x48 ; LCD + 50 + 51 ; .org 0x50 ; TIM + 52 + 53 ; .org 0x58 ; SIO + 54 + 55 ; .org 0x60 ; JOY + 56 + 57 ; .org 0x70 + 58 ;; space for drawing.s bit table + 59 + 000080 60 .org 0x80 + 000080 61 .int:: + 000080 C5 [16] 62 PUSH BC + 000081 D5 [16] 63 PUSH DE + 000082 64 1$: + 000082 2A [ 8] 65 LD A,(HL+) + 000083 B6 [ 8] 66 OR (HL) + 000084 28 0B [12] 67 JR Z,.int_tail + 000086 E5 [16] 68 PUSH HL + 000087 3A [ 8] 69 LD A,(HL-) + 000088 6E [ 8] 70 LD L,(HL) + 000089 67 [ 4] 71 LD H,A + 00008A E7 [16] 72 RST 0x20 ; .call_hl + 00008B E1 [12] 73 POP HL + 00008C 23 [ 8] 74 INC HL + 00008D 18 F3 [12] 75 JR 1$ + 00008F 76 _wait_int_handler:: + 00008F E8 04 [16] 77 ADD SP,#4 + 000091 78 .int_tail: + 000091 D1 [12] 79 POP DE + 000092 C1 [12] 80 POP BC + 000093 E1 [12] 81 POP HL + 82 + 83 ;; we return at least at the beginning of mode 2 + 000094 84 WAIT_STAT + 000094 F0 41 [12] 1 10000$: LDH A, (.STAT) + 000096 E6 02 [ 8] 2 AND #STATF_BUSY ; Check if in LCD modes 0 or 1 + 000098 20 FA [12] 3 JR NZ, 10000$ + 85 + 00009A F1 [12] 86 POP AF + 00009B D9 [16] 87 RETI + 88 + 89 ;; VBlank default interrupt routine + 00009C 90 __standard_VBL_handler:: + 00009C 91 .std_vbl: + 00009C 21 A3 C0 [12] 92 LD HL,#.sys_time + 00009F 34 [12] 93 INC (HL) + 0000A0 20 02 [12] 94 JR NZ,2$ + 0000A2 23 [ 8] 95 INC HL + 0000A3 34 [12] 96 INC (HL) + 0000A4 97 2$: + 0000A4 CD 80 FF [24] 98 CALL .refresh_OAM + 99 + 0000A7 3E 01 [ 8] 100 LD A, #1 + 0000A9 E0 91 [12] 101 LDH (.vbl_done),A + 0000AB C9 [16] 102 RET + 103 + 0000AC 104 _refresh_OAM:: + 0000AC 105 WAIT_STAT + 0000AC F0 41 [12] 1 10001$: LDH A, (.STAT) + 0000AE E6 02 [ 8] 2 AND #STATF_BUSY ; Check if in LCD modes 0 or 1 + 0000B0 20 FA [12] 3 JR NZ, 10001$ + 0000B2 3E C0 [ 8] 106 LD A, #>_shadow_OAM + 0000B4 C3 84 FF [16] 107 JP .refresh_OAM + (.refresh_OAM_DMA - .start_refresh_OAM) + 108 + 0000B7 109 .clear_WRAM: + 0000B7 D5 [16] 110 PUSH DE + 0000B8 AF [ 4] 111 XOR A + 0000B9 01 19 00 [12] 112 LD BC, #l__DATA + 0000BC 21 A0 C0 [12] 113 LD HL, #s__DATA + 0000BF CD 01 02 [24] 114 CALL .memset_simple + 115 + 0000C2 3E C0 [ 8] 116 LD A, #>_shadow_OAM + 0000C4 E0 92 [12] 117 LDH (__shadow_OAM_base), A + 0000C6 67 [ 4] 118 LD H, A + 0000C7 AF [ 4] 119 XOR A + 0000C8 6F [ 4] 120 LD L, A + 0000C9 0E A0 [ 8] 121 LD C, #(40 << 2) ; 40 entries 4 bytes each + 0000CB EF [16] 122 RST 0x28 + 0000CC D1 [12] 123 POP DE + 0000CD C9 [16] 124 RET + 125 + 126 ;; GameBoy Header + 127 + 128 ;; DO NOT CHANGE... + 000100 129 .org 0x100 + 000100 130 .header: + 000100 18 55 [12] 131 JR .code_start + 132 + 133 ;; Nintendo logo + 000104 134 .org 0x104 + 000104 CE ED 66 66 135 .byte 0xCE,0xED,0x66,0x66 + 000108 CC 0D 00 0B 136 .byte 0xCC,0x0D,0x00,0x0B + 00010C 03 73 00 83 137 .byte 0x03,0x73,0x00,0x83 + 000110 00 0C 00 0D 138 .byte 0x00,0x0C,0x00,0x0D + 000114 00 08 11 1F 139 .byte 0x00,0x08,0x11,0x1F + 000118 88 89 00 0E 140 .byte 0x88,0x89,0x00,0x0E + 00011C DC CC 6E E6 141 .byte 0xDC,0xCC,0x6E,0xE6 + 000120 DD DD D9 99 142 .byte 0xDD,0xDD,0xD9,0x99 + 000124 BB BB 67 63 143 .byte 0xBB,0xBB,0x67,0x63 + 000128 6E 0E EC CC 144 .byte 0x6E,0x0E,0xEC,0xCC + 00012C DD DC 99 9F 145 .byte 0xDD,0xDC,0x99,0x9F + 000130 BB B9 33 3E 146 .byte 0xBB,0xB9,0x33,0x3E + 147 + 148 ;; Title of the game + 000134 149 .org 0x134 + 000134 54 69 74 6C 65 00 150 .asciz "Title" + 151 + 000143 152 .org 0x143 + 000143 00 153 .byte GB_CGB_FLAGS + 154 + 000144 155 .org 0x144 + 000144 00 00 00 156 .byte 0,0,0 + 157 + 158 ;; Cartridge type is ROM only + 000147 159 .org 0x147 + 000147 00 160 .byte GB_MAPPER + 161 + 162 ;; ROM size is 32kB + 000148 163 .org 0x148 + 000148 00 164 .byte GB_ROM_SIZE + 165 + 166 ;; RAM size is 0kB + 000149 167 .org 0x149 + 000149 00 168 .byte GB_RAM_SIZE + 169 + 170 ;; Maker ID + 00014A 171 .org 0x14A + 00014A 00 00 172 .byte 0x00,0x00 + 173 + 174 ;; Version number + 00014C 175 .org 0x14C + 00014C 01 176 .byte 0x01 + 177 + 178 ;; Complement check + 00014D 179 .org 0x14D + 00014D 00 180 .byte 0x00 + 181 + 182 ;; Checksum + 00014E 183 .org 0x14E + 00014E 00 00 184 .byte 0x00,0x00 + 185 + 186 ;; **************************************** + 000150 187 .org 0x150 + 188 + 189 ;; soft reset: falldown to .code_start + 000150 190 .reset:: + 000150 191 _reset:: + 000150 FA A1 C0 [16] 192 LD A, (__is_GBA) + 000153 47 [ 4] 193 LD B, A + 000154 FA A0 C0 [16] 194 LD A, (__cpu) + 195 + 196 ;; Initialization code + 000157 197 .code_start:: + 000157 F3 [ 4] 198 DI ; Disable interrupts + 000158 57 [ 4] 199 LD D, A ; Store CPU type in D + 000159 58 [ 4] 200 LD E, B ; Store GBA flag in E + 201 ;; Initialize the stack + 00015A 31 00 E0 [12] 202 LD SP, #.STACK + 203 + 00015D CD B7 00 [24] 204 CALL .clear_WRAM + 205 + 206 ; LD (.mode),A ; Clearing (.mode) is performed when clearing RAM + 207 + 208 ;; Store CPU type + 000160 7A [ 4] 209 LD A, D + 000161 EA A0 C0 [16] 210 LD (__cpu), A + 000164 FE 11 [ 8] 211 CP #.CGB_TYPE + 000166 20 07 [12] 212 JR NZ, 1$ + 000168 AF [ 4] 213 XOR A + 000169 CB 3B [ 8] 214 SRL E + 00016B 17 [ 4] 215 RLA + 00016C EA A1 C0 [16] 216 LD (__is_GBA), A + 00016F 217 1$: + 218 ;; Turn the screen off + 00016F CD 65 02 [24] 219 CALL .display_off + 220 + 000172 AF [ 4] 221 XOR A + 222 ;; Initialize the display + 000173 E0 42 [12] 223 LDH (.SCY),A + 000175 E0 43 [12] 224 LDH (.SCX),A + 000177 E0 41 [12] 225 LDH (.STAT),A + 000179 E0 4A [12] 226 LDH (.WY),A + 00017B 3E 07 [ 8] 227 LD A,#0x07 + 00017D E0 4B [12] 228 LDH (.WX),A + 229 + 230 ;; Copy refresh_OAM routine to HRAM + 00017F 11 C6 01 [12] 231 LD DE,#.start_refresh_OAM ; source + 000182 21 80 FF [12] 232 LD HL,#.refresh_OAM ; dest + 000185 0E 0C [ 8] 233 LD C,#(.end_refresh_OAM - .start_refresh_OAM) ; size + 000187 F7 [16] 234 RST 0x30 ; call .MemcpySmall + 235 + 236 ;; Clear the OAM by calling refresh_OAM + 000188 CD 80 FF [24] 237 CALL .refresh_OAM + 238 + 239 ;; Install interrupt routines + 00018B 01 9C 00 [12] 240 LD BC,#.std_vbl + 00018E CD 47 02 [24] 241 CALL .add_VBL + 242 + 243 ;; Standard color palettes + 000191 3E E4 [ 8] 244 LD A,#0b11100100 ; Grey 3 = 11 (Black) + 245 ; Grey 2 = 10 (Dark grey) + 246 ; Grey 1 = 01 (Light grey) + 247 ; Grey 0 = 00 (Transparent) + 000193 E0 47 [12] 248 LDH (.BGP),A + 000195 E0 48 [12] 249 LDH (.OBP0),A + 000197 3E 1B [ 8] 250 LD A,#0b00011011 + 000199 E0 49 [12] 251 LDH (.OBP1),A + 252 + 253 ;; Turn the screen on + 00019B 3E C0 [ 8] 254 LD A,#(LCDCF_ON | LCDCF_WIN9C00 | LCDCF_WINOFF | LCDCF_BG8800 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_BGOFF) + 00019D E0 40 [12] 255 LDH (.LCDC),A + 00019F AF [ 4] 256 XOR A + 0001A0 E0 0F [12] 257 LDH (.IF),A + 0001A2 3E 01 [ 8] 258 LD A,#.VBL_IFLAG ; switch on VBlank interrupt only + 0001A4 E0 FF [12] 259 LDH (.IE),A + 260 + 0001A6 E0 90 [12] 261 LDH (__current_bank),A ; current bank is 1 at startup + 262 + 0001A8 AF [ 4] 263 XOR A + 264 + 0001A9 21 A3 C0 [12] 265 LD HL,#.sys_time + 0001AC 22 [ 8] 266 LD (HL+),A + 0001AD 77 [ 8] 267 LD (HL),A + 268 + 0001AE E0 26 [12] 269 LDH (.NR52),A ; Turn sound off + 270 + 0001B0 CD 93 02 [24] 271 CALL gsinit + 272 + 0001B3 FB [ 4] 273 EI ; Enable interrupts + 274 + 275 ;; Call the main function + 0001B4 CD 00 02 [24] 276 CALL _main + 0001B7 277 _exit:: + 0001B7 278 99$: + 0001B7 76 [ 4] 279 HALT + 0001B8 00 [ 4] 280 NOP + 0001B9 18 FC [12] 281 JR 99$ ; Wait forever + 282 + 0001BB 283 _set_interrupts:: + 0001BB F3 [ 4] 284 DI + 0001BC F8 02 [12] 285 LDA HL,2(SP) ; Skip return address + 0001BE AF [ 4] 286 XOR A + 0001BF E0 0F [12] 287 LDH (.IF),A ; Clear pending interrupts + 0001C1 7E [ 8] 288 LD A,(HL) + 0001C2 FB [ 4] 289 EI ; Enable interrupts + 0001C3 E0 FF [12] 290 LDH (.IE),A ; interrupts are still disabled here + 0001C5 C9 [16] 291 RET + 292 + 293 ;; Copy OAM data to OAM RAM + 0001C6 294 .start_refresh_OAM: + 0001C6 F0 92 [12] 295 LDH A,(__shadow_OAM_base) + 0001C8 B7 [ 4] 296 OR A + 0001C9 C8 [20] 297 RET Z + 0001CA 298 .refresh_OAM_DMA: + 0001CA E0 46 [12] 299 LDH (.DMA),A ; Put A into DMA registers + 0001CC 3E 28 [ 8] 300 LD A,#0x28 ; We need to wait 160 ns + 0001CE 301 1$: + 0001CE 3D [ 4] 302 DEC A + 0001CF 20 FD [12] 303 JR NZ,1$ + 0001D1 C9 [16] 304 RET + 0001D2 305 .end_refresh_OAM: + 306 + 0001E0 307 .org .MODE_TABLE + 308 ;; Jump table for modes + 0001E0 C9 [16] 309 RET + 310 + 311 ;; **************************************** + 312 + 313 ;; Ordering of segments for the linker + 314 ;; Code that really needs to be in bank 0 + 315 .area _HOME + 316 ;; Similar to _HOME + 317 .area _BASE + 318 ;; Code + 319 .area _CODE + 320 ;; #pragma bank 0 workaround + 321 .area _CODE_0 + 322 ;; Constant data + 323 .area _LIT + 324 ; ;; since _CODE_1 area base address is pre-defined in the linker from 0x4000, + 325 ; ;; that moves initializer code and tables out of bank 0 + 326 ; .area _CODE_1 + 327 ;; Constant data, used to init _DATA + 328 .area _INITIALIZER + 329 ;; Code, used to init _DATA + 330 .area _GSINIT + 331 .area _GSFINAL + 332 ;; Uninitialised ram data + 333 .area _DATA + 334 .area _BSS + 335 ;; Initialised in ram data + 336 .area _INITIALIZED + 337 ;; For malloc + 338 .area _HEAP + 339 + 340 .area _DATA + 00C0A0 341 .start_crt_globals: + 342 + 00C0A0 343 __cpu:: + 00C0A0 344 .ds 0x01 ; GB type (GB, PGB, CGB) + 00C0A1 345 __is_GBA:: + 00C0A1 346 .ds 0x01 ; detect GBA + 00C0A2 347 .mode:: + 00C0A2 348 .ds 0x01 ; Current mode + 00C0A3 349 .sys_time:: + 00C0A3 350 _sys_time:: + 00C0A3 351 .ds 0x02 ; System time in VBL units + 00C0A5 352 .int_0x40:: + 00C0A5 353 .blkw 0x0A ; 4 interrupt handlers (built-in + user-defined) + 354 + 00C0B9 355 .end_crt_globals: + 356 + 357 .area _HRAM (ABS) + 358 + 00FF90 359 .org 0xFF90 + 00FF90 360 __current_bank:: ; Current bank + 00FF90 361 .ds 0x01 + 00FF91 362 .vbl_done: + 00FF91 363 .ds 0x01 ; Is VBL interrupt finished? + 00FF92 364 __shadow_OAM_base:: + 00FF92 365 .ds 0x01 + 366 + 367 ;; Runtime library + 368 .area _GSINIT + 000293 369 gsinit:: + 370 ;; initialize static storage variables + 000293 01 00 00 [12] 371 LD BC, #l__INITIALIZER + 000296 21 93 02 [12] 372 LD HL, #s__INITIALIZER + 000299 11 B9 C0 [12] 373 LD DE, #s__INITIALIZED + 00029C CD 0A 02 [24] 374 call .memcpy_simple + 375 + 376 .area _GSFINAL + 00029F C9 [16] 377 ret + 378 + 379 .area _HOME + 380 + 381 ;; fills memory at HL of length BC with A, clobbers DE + 000201 382 .memset_simple:: + 000201 5F [ 4] 383 LD E, A + 000202 78 [ 4] 384 LD A, B + 000203 B1 [ 4] 385 OR C + 000204 C8 [20] 386 RET Z + 000205 73 [ 8] 387 LD (HL), E + 000206 0B [ 8] 388 DEC BC + 000207 54 [ 4] 389 LD D, H + 000208 5D [ 4] 390 LD E, L + 000209 13 [ 8] 391 INC DE + 392 + 393 ;; copies BC bytes from HL into DE + 00020A 394 .memcpy_simple:: + 00020A 78 [ 4] 395 LD A, B + 00020B B1 [ 4] 396 OR C + 00020C C8 [20] 397 RET Z + 398 + 00020D CB 38 [ 8] 399 SRL B + 00020F CB 19 [ 8] 400 RR C + 000211 30 03 [12] 401 JR NC,3$ + 000213 2A [ 8] 402 LD A, (HL+) + 000214 12 [ 8] 403 LD (DE), A + 000215 13 [ 8] 404 INC DE + 000216 405 3$: + 000216 04 [ 4] 406 INC B + 000217 0C [ 4] 407 INC C + 000218 18 06 [12] 408 JR 2$ + 00021A 409 1$: + 00021A 2A [ 8] 410 LD A, (HL+) + 00021B 12 [ 8] 411 LD (DE), A + 00021C 13 [ 8] 412 INC DE + 00021D 2A [ 8] 413 LD A, (HL+) + 00021E 12 [ 8] 414 LD (DE), A + 00021F 13 [ 8] 415 INC DE + 000220 416 2$: + 000220 0D [ 4] 417 DEC C + 000221 20 F7 [12] 418 JR NZ,1$ + 000223 05 [ 4] 419 DEC B + 000224 20 F4 [12] 420 JR NZ,1$ + 000226 421 4$: + 000226 C9 [16] 422 RET + 423 + 424 ;; Remove interrupt routine in BC from the VBL interrupt list + 425 ;; falldown to .remove_int + 000227 426 .remove_VBL:: + 000227 21 A5 C0 [12] 427 LD HL,#.int_0x40 + 428 + 429 ;; Remove interrupt BC from interrupt list HL if it exists + 430 ;; Abort if a 0000 is found (end of list) + 00022A 431 .remove_int:: + 00022A 432 1$: + 00022A 2A [ 8] 433 LD A,(HL+) + 00022B 5F [ 4] 434 LD E,A + 00022C 56 [ 8] 435 LD D,(HL) + 00022D 23 [ 8] 436 INC HL + 00022E B2 [ 4] 437 OR D + 00022F C8 [20] 438 RET Z ; No interrupt found + 439 + 000230 7B [ 4] 440 LD A,E + 000231 B9 [ 4] 441 CP C + 000232 20 F6 [12] 442 JR NZ,1$ + 000234 7A [ 4] 443 LD A,D + 000235 B8 [ 4] 444 CP B + 000236 20 F2 [12] 445 JR NZ,1$ + 446 + 000238 54 [ 4] 447 LD D,H + 000239 5D [ 4] 448 LD E,L + 00023A 1B [ 8] 449 DEC DE + 00023B 1B [ 8] 450 DEC DE + 451 + 452 ;; Now do a memcpy from here until the end of the list + 00023C 453 2$: + 00023C 2A [ 8] 454 LD A,(HL+) + 00023D 12 [ 8] 455 LD (DE),A + 00023E 47 [ 4] 456 LD B,A + 00023F 13 [ 8] 457 INC DE + 000240 2A [ 8] 458 LD A,(HL+) + 000241 12 [ 8] 459 LD (DE),A + 000242 13 [ 8] 460 INC DE + 000243 B0 [ 4] 461 OR B + 000244 C8 [20] 462 RET Z + 000245 18 F5 [12] 463 JR 2$ + 464 + 465 ;; Add interrupt routine in BC to the VBL interrupt list + 466 ;; falldown to .add_int + 000247 467 .add_VBL:: + 000247 21 A5 C0 [12] 468 LD HL,#.int_0x40 + 469 + 470 ;; Add interrupt routine in BC to the interrupt list in HL + 00024A 471 .add_int:: + 00024A 472 1$: + 00024A 2A [ 8] 473 LD A,(HL+) + 00024B B6 [ 8] 474 OR (HL) + 00024C 28 03 [12] 475 JR Z,2$ + 00024E 23 [ 8] 476 INC HL + 00024F 18 F9 [12] 477 JR 1$ + 000251 478 2$: + 000251 78 [ 4] 479 LD A,B + 000252 32 [ 8] 480 LD (HL-),A + 000253 71 [ 8] 481 LD (HL),C + 000254 C9 [16] 482 RET + 483 + 484 ;; Wait for VBL interrupt to be finished + 000255 485 .wait_vbl_done:: + 000255 486 _wait_vbl_done:: + 487 ;; Check if the screen is on + 000255 F0 40 [12] 488 LDH A,(.LCDC) + 000257 E6 80 [ 8] 489 AND #LCDCF_ON + 000259 C8 [20] 490 RET Z ; Return if screen is off + 00025A AF [ 4] 491 XOR A + 00025B E0 91 [12] 492 LDH (.vbl_done),A ; Clear any previous sets of vbl_done + 00025D 493 1$: + 00025D 76 [ 4] 494 HALT ; Wait for any interrupt + 00025E 00 [ 4] 495 NOP ; HALT sometimes skips the next instruction + 00025F F0 91 [12] 496 LDH A,(.vbl_done) ; Was it a VBlank interrupt? + 497 ;; Warning: we may lose a VBlank interrupt, if it occurs now + 000261 B7 [ 4] 498 OR A + 000262 28 F9 [12] 499 JR Z,1$ ; No: back to sleep! + 000264 C9 [16] 500 RET + 501 + 000265 502 .display_off:: + 000265 503 _display_off:: + 504 ;; Check if the screen is on + 000265 F0 40 [12] 505 LDH A,(.LCDC) + 000267 E6 80 [ 8] 506 AND #LCDCF_ON + 000269 C8 [20] 507 RET Z ; Return if screen is off + 00026A 508 1$: ; We wait for the *NEXT* VBL + 00026A F0 44 [12] 509 LDH A,(.LY) + 00026C FE 92 [ 8] 510 CP #0x92 ; Smaller than or equal to 0x91? + 00026E 30 FA [12] 511 JR NC,1$ ; Loop until smaller than or equal to 0x91 + 000270 512 2$: + 000270 F0 44 [12] 513 LDH A,(.LY) + 000272 FE 91 [ 8] 514 CP #0x91 ; Bigger than 0x90? + 000274 38 FA [12] 515 JR C,2$ ; Loop until bigger than 0x90 + 516 + 000276 F0 40 [12] 517 LDH A,(.LCDC) + 000278 E6 7F [ 8] 518 AND #~LCDCF_ON + 00027A E0 40 [12] 519 LDH (.LCDC),A ; Turn off screen + 00027C C9 [16] 520 RET + 521 + 00027D 522 _remove_VBL:: + 00027D C5 [16] 523 PUSH BC + 00027E F8 04 [12] 524 LDA HL,4(SP) ; Skip return address and registers + 000280 2A [ 8] 525 LD A,(HL+) + 000281 4F [ 4] 526 LD C,A + 000282 46 [ 8] 527 LD B,(HL) + 000283 CD 27 02 [24] 528 CALL .remove_VBL + 000286 C1 [12] 529 POP BC + 000287 C9 [16] 530 RET + 531 + 000288 532 _add_VBL:: + 000288 C5 [16] 533 PUSH BC + 000289 F8 04 [12] 534 LDA HL, 4(SP) ; Skip return address and registers + 00028B 2A [ 8] 535 LD A,(HL+) + 00028C 4F [ 4] 536 LD C,A + 00028D 46 [ 8] 537 LD B,(HL) + 00028E CD 47 02 [24] 538 CALL .add_VBL + 000291 C1 [12] 539 POP BC + 000292 C9 [16] 540 RET diff --git a/src/worker/lib/gb/crt0.rel b/src/worker/lib/gb/crt0.rel new file mode 100644 index 00000000..6b786674 --- /dev/null +++ b/src/worker/lib/gb/crt0.rel @@ -0,0 +1,642 @@ +;!FILE crt0.sgb +XL3 +H 22 areas 126 global symbols +M Runtime +S GB_CGB_FLAGS Ref000000 +S l__DATA Ref000000 +S _main Ref000000 +S s__DATA Ref000000 +S .refresh_OAM Ref000000 +S .__.ABS. Def000000 +S _shadow_OAM Ref000000 +S GB_RAM_SIZE Ref000000 +S .STACK Ref000000 +S s__INITIALIZED Ref000000 +S GB_MAPPER Ref000000 +S l__INITIALIZER Ref000000 +S GB_ROM_SIZE Ref000000 +S s__INITIALIZER Ref000000 +A _CODE size 0 flags 0 +A _HEADER size 0 flags 8 +A _HEADER0 size 1 flags 8 +S A$crt0$21 Def000020 +S .call_hl Def000020 +A _HEADER1 size 5 flags 8 +S .MemsetSmall Def000028 +S A$crt0$25 Def000028 +S A$crt0$26 Def000029 +S A$crt0$27 Def00002A +S A$crt0$28 Def00002C +A _HEADER2 size 7 flags 8 +S .MemcpySmall Def000030 +S A$crt0$32 Def000030 +S A$crt0$33 Def000031 +S A$crt0$34 Def000032 +S A$crt0$35 Def000033 +S A$crt0$36 Def000034 +S A$crt0$37 Def000036 +A _HEADER3 size 8 flags 8 +S A$crt0$44 Def000040 +S A$crt0$45 Def000041 +S A$crt0$46 Def000042 +S A$crt0$47 Def000045 +A _HEADER4 size 4E flags 8 +S _refresh_OAM Def0000AC +S __standard_VBL_handler Def00009C +S A$crt0$100 Def0000A7 +S A$crt0$110 Def0000B7 +S A$crt0$101 Def0000A9 +S A$crt0$120 Def0000C8 +S A$crt0$111 Def0000B8 +S A$crt0$102 Def0000AB +S A$crt0$121 Def0000C9 +S A$crt0$112 Def0000B9 +S A$crt0$122 Def0000CB +S A$crt0$113 Def0000BC +S A$crt0$123 Def0000CC +S A$crt0$114 Def0000BF +S A$crt0$105 Def0000AC +S A$crt0$124 Def0000CD +S A$crt0$106 Def0000B2 +S A$crt0$116 Def0000C2 +S A$crt0$107 Def0000B4 +S A$crt0$117 Def0000C4 +S A$crt0$118 Def0000C6 +S A$crt0$119 Def0000C7 +S A$crt0$70 Def000088 +S A$crt0$80 Def000092 +S A$crt0$71 Def000089 +S A$crt0$62 Def000080 +S A$crt0$81 Def000093 +S A$crt0$72 Def00008A +S A$crt0$63 Def000081 +S A$crt0$73 Def00008B +S A$crt0$92 Def00009C +S A$crt0$74 Def00008C +S A$crt0$65 Def000082 +S A$crt0$93 Def00009F +S A$crt0$84 Def000094 +S A$crt0$75 Def00008D +S A$crt0$66 Def000083 +S A$crt0$94 Def0000A0 +S A$crt0$67 Def000084 +S A$crt0$95 Def0000A2 +S A$crt0$86 Def00009A +S A$crt0$77 Def00008F +S A$crt0$68 Def000086 +S A$crt0$96 Def0000A3 +S A$crt0$87 Def00009B +S A$crt0$69 Def000087 +S A$crt0$79 Def000091 +S A$crt0$98 Def0000A4 +S .int Def000080 +S _wait_int_handler Def00008F +A _HEADER5 size 2 flags 8 +S A$crt0$131 Def000100 +A _HEADER6 size 30 flags 8 +A _HEADER7 size 6 flags 8 +A _HEADER8 size 1 flags 8 +A _HEADER9 size 3 flags 8 +A _HEADERa size 1 flags 8 +A _HEADERb size 1 flags 8 +A _HEADERc size 1 flags 8 +A _HEADERd size 2 flags 8 +A _HEADERe size 1 flags 8 +A _HEADERf size 1 flags 8 +A _HEADER10 size 2 flags 8 +A _HEADER11 size 82 flags 8 +S _reset Def000150 +S .reset Def000150 +S A$crt0$200 Def000159 +S A$crt0$300 Def0001CC +S A$crt0$210 Def000161 +S A$crt0$211 Def000164 +S A$crt0$202 Def00015A +S .code_start Def000157 +S A$crt0$302 Def0001CE +S A$crt0$221 Def000172 +S A$crt0$212 Def000166 +S A$crt0$303 Def0001CF +S A$crt0$240 Def00018B +S A$crt0$231 Def00017F +S A$crt0$213 Def000168 +S A$crt0$204 Def00015D +S A$crt0$304 Def0001D1 +S A$crt0$250 Def000197 +S A$crt0$241 Def00018E +S A$crt0$232 Def000182 +S A$crt0$223 Def000173 +S A$crt0$214 Def000169 +S _exit Def0001B7 +S A$crt0$251 Def000199 +S A$crt0$233 Def000185 +S A$crt0$224 Def000175 +S A$crt0$215 Def00016B +S A$crt0$261 Def0001A6 +S A$crt0$234 Def000187 +S A$crt0$225 Def000177 +S A$crt0$216 Def00016C +S A$crt0$280 Def0001B8 +S A$crt0$271 Def0001B0 +S A$crt0$244 Def000191 +S A$crt0$226 Def000179 +S A$crt0$290 Def0001C3 +S A$crt0$281 Def0001B9 +S A$crt0$263 Def0001A8 +S A$crt0$254 Def00019B +S A$crt0$227 Def00017B +S A$crt0$209 Def000160 +S A$crt0$291 Def0001C5 +S A$crt0$273 Def0001B3 +S A$crt0$255 Def00019D +S A$crt0$237 Def000188 +S A$crt0$228 Def00017D +S A$crt0$219 Def00016F +S A$crt0$192 Def000150 +S A$crt0$265 Def0001A9 +S A$crt0$256 Def00019F +S A$crt0$193 Def000153 +S A$crt0$284 Def0001BB +S A$crt0$266 Def0001AC +S A$crt0$257 Def0001A0 +S A$crt0$248 Def000193 +S A$crt0$194 Def000154 +S A$crt0$285 Def0001BC +S A$crt0$276 Def0001B4 +S A$crt0$267 Def0001AD +S A$crt0$258 Def0001A2 +S A$crt0$249 Def000195 +S A$crt0$295 Def0001C6 +S A$crt0$286 Def0001BE +S A$crt0$259 Def0001A4 +S A$crt0$296 Def0001C8 +S A$crt0$287 Def0001BF +S A$crt0$269 Def0001AE +S A$crt0$297 Def0001C9 +S A$crt0$288 Def0001C1 +S A$crt0$279 Def0001B7 +S A$crt0$198 Def000157 +S A$crt0$289 Def0001C2 +S A$crt0$199 Def000158 +S A$crt0$299 Def0001CA +S _set_interrupts Def0001BB +A _HEADER12 size 1 flags 8 +S A$crt0$309 Def0001E0 +A _HOME size 92 flags 0 +S .add_int Def000049 +S _add_VBL Def000087 +S .wait_vbl_done Def000054 +S A$crt0$400 Def00000E +S A$crt0$500 Def000063 +S A$crt0$410 Def000019 +S A$crt0$401 Def000010 +S A$crt0$510 Def00006B +S A$crt0$420 Def000023 +S A$crt0$411 Def00001A +S A$crt0$402 Def000012 +S A$crt0$520 Def00007B +S A$crt0$511 Def00006D +S A$crt0$412 Def00001B +S A$crt0$403 Def000013 +S A$crt0$530 Def000086 +S A$crt0$440 Def00002F +S A$crt0$422 Def000025 +S A$crt0$413 Def00001C +S A$crt0$404 Def000014 +S .add_VBL Def000046 +S A$crt0$540 Def000091 +S A$crt0$513 Def00006F +S A$crt0$450 Def00003A +S A$crt0$441 Def000030 +S A$crt0$414 Def00001D +S A$crt0$523 Def00007C +S A$crt0$514 Def000071 +S A$crt0$505 Def000064 +S A$crt0$460 Def000041 +S A$crt0$442 Def000031 +S A$crt0$433 Def000029 +S A$crt0$415 Def00001E +S A$crt0$406 Def000015 +S A$crt0$533 Def000087 +S A$crt0$524 Def00007D +S A$crt0$515 Def000073 +S A$crt0$506 Def000066 +S A$crt0$461 Def000042 +S A$crt0$443 Def000033 +S A$crt0$434 Def00002A +S A$crt0$407 Def000016 +S A$crt0$534 Def000088 +S A$crt0$525 Def00007F +S A$crt0$507 Def000068 +S A$crt0$480 Def000051 +S A$crt0$462 Def000043 +S A$crt0$444 Def000034 +S A$crt0$435 Def00002B +S A$crt0$417 Def00001F +S A$crt0$408 Def000017 +S A$crt0$390 Def000007 +S A$crt0$535 Def00008A +S A$crt0$526 Def000080 +S A$crt0$517 Def000075 +S A$crt0$490 Def000058 +S A$crt0$481 Def000052 +S A$crt0$463 Def000044 +S A$crt0$454 Def00003B +S A$crt0$445 Def000035 +S A$crt0$436 Def00002C +S A$crt0$427 Def000026 +S A$crt0$418 Def000020 +S A$crt0$391 Def000008 +S A$crt0$536 Def00008B +S A$crt0$527 Def000081 +S A$crt0$518 Def000077 +S A$crt0$509 Def000069 +S A$crt0$491 Def000059 +S A$crt0$482 Def000053 +S A$crt0$473 Def000049 +S A$crt0$455 Def00003C +S A$crt0$437 Def00002D +S A$crt0$419 Def000022 +S A$crt0$383 Def000000 +S A$crt0$537 Def00008C +S A$crt0$528 Def000082 +S A$crt0$519 Def000079 +S A$crt0$492 Def00005A +S A$crt0$474 Def00004A +S A$crt0$456 Def00003D +S A$crt0$447 Def000037 +S A$crt0$438 Def00002E +S A$crt0$384 Def000001 +S A$crt0$538 Def00008D +S A$crt0$529 Def000085 +S A$crt0$475 Def00004B +S A$crt0$457 Def00003E +S A$crt0$448 Def000038 +S A$crt0$385 Def000002 +S .memcpy_simple Def000009 +S .memset_simple Def000000 +S A$crt0$539 Def000090 +S A$crt0$494 Def00005C +S A$crt0$476 Def00004D +S A$crt0$458 Def00003F +S A$crt0$449 Def000039 +S A$crt0$395 Def000009 +S A$crt0$386 Def000003 +S A$crt0$495 Def00005D +S A$crt0$477 Def00004E +S A$crt0$468 Def000046 +S A$crt0$459 Def000040 +S A$crt0$396 Def00000A +S A$crt0$387 Def000004 +S A$crt0$496 Def00005E +S A$crt0$397 Def00000B +S A$crt0$388 Def000005 +S A$crt0$488 Def000054 +S A$crt0$479 Def000050 +S A$crt0$389 Def000006 +S .remove_int Def000029 +S A$crt0$498 Def000060 +S A$crt0$489 Def000056 +S A$crt0$399 Def00000C +S A$crt0$499 Def000061 +S _display_off Def000064 +S _remove_VBL Def00007C +S _wait_vbl_done Def000054 +S .display_off Def000064 +S .remove_VBL Def000026 +A _BASE size 0 flags 0 +A _CODE_0 size 0 flags 0 +A _LIT size 0 flags 0 +A _INITIALIZER size 0 flags 0 +A _GSINIT size C flags 0 +S gsinit Def000000 +S A$crt0$371 Def000000 +S A$crt0$372 Def000003 +S A$crt0$373 Def000006 +S A$crt0$374 Def000009 +A _GSFINAL size 1 flags 0 +S A$crt0$377 Def000000 +A _DATA size 19 flags 0 +S __is_GBA Def000001 +S __cpu Def000000 +S _sys_time Def000003 +S .mode Def000002 +S .sys_time Def000003 +S .int_0x40 Def000005 +A _BSS size 0 flags 0 +A _INITIALIZED size 0 flags 0 +A _HEAP size 0 flags 0 +A _HRAM size 0 flags 8 +A _HRAM13 size 3 flags 8 +S __shadow_OAM_base Def00FF92 +S __current_bank Def00FF90 +T 20 00 00 +R 00 00 02 00 +T 20 00 00 +R 00 00 02 00 +T 20 00 00 E9 +R 00 00 02 00 +T 28 00 00 +R 00 00 03 00 +T 28 00 00 +R 00 00 03 00 +T 28 00 00 22 0D 20 FC C9 +R 00 00 03 00 +T 30 00 00 +R 00 00 04 00 +T 30 00 00 +R 00 00 04 00 +T 30 00 00 1A 22 13 0D 20 FA C9 +R 00 00 04 00 +T 40 00 00 +R 00 00 05 00 +T 40 00 00 +R 00 00 05 00 +T 40 00 00 F5 E5 21 05 00 C3 80 00 +R 00 00 05 00 00 06 1C 00 00 09 06 00 +T 80 00 00 +R 00 00 06 00 +T 80 00 00 +R 00 00 06 00 +T 80 00 00 C5 D5 +R 00 00 06 00 +T 82 00 00 +R 00 00 06 00 +T 82 00 00 2A B6 28 0B E5 3A 6E 67 E7 E1 23 18 F3 +R 00 00 06 00 +T 8F 00 00 +R 00 00 06 00 +T 8F 00 00 E8 04 +R 00 00 06 00 +T 91 00 00 +R 00 00 06 00 +T 91 00 00 D1 C1 E1 F0 41 E6 02 20 FA F1 D9 +R 00 00 06 00 +T 9C 00 00 +R 00 00 06 00 +T 9C 00 00 +R 00 00 06 00 +T 9C 00 00 21 03 00 34 20 02 23 34 +R 00 00 06 00 00 04 1C 00 +T A4 00 00 +R 00 00 06 00 +T A4 00 00 CD 00 00 3E 01 E0 91 FF 00 C9 +R 00 00 06 00 02 04 04 00 09 09 21 00 +T AC 00 00 +R 00 00 06 00 +T AC 00 00 F0 41 E6 02 20 FA 3E 00 00 00 C3 04 00 +R 00 00 06 00 8B 0A 06 00 02 0E 04 00 +T B7 00 00 +R 00 00 06 00 +T B7 00 00 D5 AF 01 00 00 21 00 00 CD 00 00 3E +R 00 00 06 00 02 06 01 00 02 09 03 00 00 0C 15 00 +T C3 00 00 00 00 00 E0 92 FF 00 67 AF 6F 0E A0 EF +R 00 00 06 00 8B 03 06 00 09 07 21 00 +T CC 00 00 D1 C9 +R 00 00 06 00 +T 00 01 00 +R 00 00 07 00 +T 00 01 00 +R 00 00 07 00 +T 00 01 00 18 57 01 00 +R 00 00 07 00 0D 04 13 00 +T 04 01 00 +R 00 00 08 00 +T 04 01 00 CE ED 66 66 CC 0D 00 0B 03 73 00 83 00 +R 00 00 08 00 +T 11 01 00 0C 00 0D 00 08 11 1F 88 89 00 0E DC CC +R 00 00 08 00 +T 1E 01 00 6E E6 DD DD D9 99 BB BB 67 63 6E 0E EC +R 00 00 08 00 +T 2B 01 00 CC DD DC 99 9F BB B9 33 3E +R 00 00 08 00 +T 34 01 00 +R 00 00 09 00 +T 34 01 00 54 69 74 6C 65 00 +R 00 00 09 00 +T 43 01 00 +R 00 00 0A 00 +T 43 01 00 00 00 00 +R 00 00 0A 00 0B 03 00 00 +T 44 01 00 +R 00 00 0B 00 +T 44 01 00 00 00 00 +R 00 00 0B 00 +T 47 01 00 +R 00 00 0C 00 +T 47 01 00 00 00 00 +R 00 00 0C 00 0B 03 0A 00 +T 48 01 00 +R 00 00 0D 00 +T 48 01 00 00 00 00 +R 00 00 0D 00 0B 03 0C 00 +T 49 01 00 +R 00 00 0E 00 +T 49 01 00 00 00 00 +R 00 00 0E 00 0B 03 07 00 +T 4A 01 00 +R 00 00 0F 00 +T 4A 01 00 00 00 +R 00 00 0F 00 +T 4C 01 00 +R 00 00 10 00 +T 4C 01 00 01 +R 00 00 10 00 +T 4D 01 00 +R 00 00 11 00 +T 4D 01 00 00 +R 00 00 11 00 +T 4E 01 00 +R 00 00 12 00 +T 4E 01 00 00 00 +R 00 00 12 00 +T 50 01 00 +R 00 00 13 00 +T 50 01 00 +R 00 00 13 00 +T 50 01 00 +R 00 00 13 00 +T 50 01 00 FA 01 00 47 FA 00 00 +R 00 00 13 00 00 04 1C 00 00 08 1C 00 +T 57 01 00 +R 00 00 13 00 +T 57 01 00 F3 57 58 31 00 00 CD B7 00 7A EA 00 00 +R 00 00 13 00 02 07 08 00 00 0A 06 00 00 0E 1C 00 +T 64 01 00 FE 11 20 07 AF CB 3B 17 EA 01 00 +R 00 00 13 00 00 0C 1C 00 +T 6F 01 00 +R 00 00 13 00 +T 6F 01 00 CD 64 00 AF E0 42 E0 43 E0 41 E0 4A 3E +R 00 00 13 00 00 04 15 00 +T 7C 01 00 07 E0 4B 11 C6 01 21 00 00 0E 0C F7 CD +R 00 00 13 00 00 07 13 00 02 0A 04 00 +T 89 01 00 00 00 01 9C 00 CD 46 00 3E E4 E0 47 E0 +R 00 00 13 00 02 03 04 00 00 06 06 00 00 09 15 00 +T 96 01 00 48 3E 1B E0 49 3E C0 E0 40 AF E0 0F 3E +R 00 00 13 00 +T A3 01 00 01 E0 FF E0 90 FF 00 AF 21 03 00 22 77 +R 00 00 13 00 09 07 21 00 00 0C 1C 00 +T AE 01 00 E0 26 CD 00 00 FB CD 00 00 +R 00 00 13 00 00 06 1A 00 02 0A 02 00 +T B7 01 00 +R 00 00 13 00 +T B7 01 00 +R 00 00 13 00 +T B7 01 00 76 00 18 FC +R 00 00 13 00 +T BB 01 00 +R 00 00 13 00 +T BB 01 00 F3 F8 02 AF E0 0F 7E FB E0 FF C9 +R 00 00 13 00 +T C6 01 00 +R 00 00 13 00 +T C6 01 00 F0 92 FF 00 B7 C8 +R 00 00 13 00 09 04 21 00 +T CA 01 00 +R 00 00 13 00 +T CA 01 00 E0 46 3E 28 +R 00 00 13 00 +T CE 01 00 +R 00 00 13 00 +T CE 01 00 3D 20 FD C9 +R 00 00 13 00 +T D2 01 00 +R 00 00 13 00 +T E0 01 00 +R 00 00 14 00 +T E0 01 00 C9 +R 00 00 14 00 +T 00 00 00 +R 00 00 1C 00 +T 00 00 00 +R 00 00 1C 00 +T 00 00 00 +R 00 00 1C 00 +T 01 00 00 +R 00 00 1C 00 +T 01 00 00 +R 00 00 1C 00 +T 02 00 00 +R 00 00 1C 00 +T 02 00 00 +R 00 00 1C 00 +T 03 00 00 +R 00 00 1C 00 +T 03 00 00 +R 00 00 1C 00 +T 03 00 00 +R 00 00 1C 00 +T 05 00 00 +R 00 00 1C 00 +T 05 00 00 +R 00 00 1C 00 +T 19 00 00 +R 00 00 1C 00 +T 90 FF 00 +R 00 00 21 00 +T 90 FF 00 +R 00 00 21 00 +T 90 FF 00 +R 00 00 21 00 +T 91 FF 00 +R 00 00 21 00 +T 91 FF 00 +R 00 00 21 00 +T 92 FF 00 +R 00 00 21 00 +T 92 FF 00 +R 00 00 21 00 +T 00 00 00 +R 00 00 1A 00 +T 00 00 00 01 00 00 21 00 00 11 00 00 CD +R 00 00 1A 00 02 04 0B 00 02 07 0D 00 02 0A 09 00 +T 0A 00 00 09 00 +R 00 00 1A 00 00 03 15 00 +T 00 00 00 C9 +R 00 00 1B 00 +T 00 00 00 +R 00 00 15 00 +T 00 00 00 5F 78 B1 C8 73 0B 54 5D 13 +R 00 00 15 00 +T 09 00 00 +R 00 00 15 00 +T 09 00 00 78 B1 C8 CB 38 CB 19 30 03 2A 12 13 +R 00 00 15 00 +T 15 00 00 +R 00 00 15 00 +T 15 00 00 04 0C 18 06 +R 00 00 15 00 +T 19 00 00 +R 00 00 15 00 +T 19 00 00 2A 12 13 2A 12 13 +R 00 00 15 00 +T 1F 00 00 +R 00 00 15 00 +T 1F 00 00 0D 20 F7 05 20 F4 +R 00 00 15 00 +T 25 00 00 +R 00 00 15 00 +T 25 00 00 C9 +R 00 00 15 00 +T 26 00 00 +R 00 00 15 00 +T 26 00 00 21 05 00 +R 00 00 15 00 00 04 1C 00 +T 29 00 00 +R 00 00 15 00 +T 29 00 00 +R 00 00 15 00 +T 29 00 00 2A 5F 56 23 B2 C8 7B B9 20 F6 7A B8 20 +R 00 00 15 00 +T 36 00 00 F2 54 5D 1B 1B +R 00 00 15 00 +T 3B 00 00 +R 00 00 15 00 +T 3B 00 00 2A 12 47 13 2A 12 13 B0 C8 18 F5 +R 00 00 15 00 +T 46 00 00 +R 00 00 15 00 +T 46 00 00 21 05 00 +R 00 00 15 00 00 04 1C 00 +T 49 00 00 +R 00 00 15 00 +T 49 00 00 +R 00 00 15 00 +T 49 00 00 2A B6 28 03 23 18 F9 +R 00 00 15 00 +T 50 00 00 +R 00 00 15 00 +T 50 00 00 78 32 71 C9 +R 00 00 15 00 +T 54 00 00 +R 00 00 15 00 +T 54 00 00 +R 00 00 15 00 +T 54 00 00 F0 40 E6 80 C8 AF E0 91 FF 00 +R 00 00 15 00 09 0A 21 00 +T 5C 00 00 +R 00 00 15 00 +T 5C 00 00 76 00 F0 91 FF 00 B7 28 F9 C9 +R 00 00 15 00 09 06 21 00 +T 64 00 00 +R 00 00 15 00 +T 64 00 00 +R 00 00 15 00 +T 64 00 00 F0 40 E6 80 C8 +R 00 00 15 00 +T 69 00 00 +R 00 00 15 00 +T 69 00 00 F0 44 FE 92 30 FA +R 00 00 15 00 +T 6F 00 00 +R 00 00 15 00 +T 6F 00 00 F0 44 FE 91 38 FA F0 40 E6 7F E0 40 C9 +R 00 00 15 00 +T 7C 00 00 +R 00 00 15 00 +T 7C 00 00 C5 F8 04 2A 4F 46 CD 26 00 C1 C9 +R 00 00 15 00 00 0A 15 00 +T 87 00 00 +R 00 00 15 00 +T 87 00 00 C5 F8 04 2A 4F 46 CD 46 00 C1 C9 +R 00 00 15 00 00 0A 15 00 diff --git a/presets/gb/gb/crt0.sgb b/src/worker/lib/gb/crt0.sgb similarity index 100% rename from presets/gb/gb/crt0.sgb rename to src/worker/lib/gb/crt0.sgb diff --git a/presets/gb/gb/bgb_emu.h b/src/worker/lib/gb/gb/bgb_emu.h similarity index 100% rename from presets/gb/gb/bgb_emu.h rename to src/worker/lib/gb/gb/bgb_emu.h diff --git a/presets/gb/gb/cgb.h b/src/worker/lib/gb/gb/cgb.h similarity index 100% rename from presets/gb/gb/cgb.h rename to src/worker/lib/gb/gb/cgb.h diff --git a/presets/gb/gb/crash_handler.h b/src/worker/lib/gb/gb/crash_handler.h similarity index 100% rename from presets/gb/gb/crash_handler.h rename to src/worker/lib/gb/gb/crash_handler.h diff --git a/presets/gb/gb/drawing.h b/src/worker/lib/gb/gb/drawing.h similarity index 100% rename from presets/gb/gb/drawing.h rename to src/worker/lib/gb/gb/drawing.h diff --git a/presets/gb/gb/emu_debug.h b/src/worker/lib/gb/gb/emu_debug.h similarity index 100% rename from presets/gb/gb/emu_debug.h rename to src/worker/lib/gb/gb/emu_debug.h diff --git a/presets/gb/gb/gb.h b/src/worker/lib/gb/gb/gb.h similarity index 100% rename from presets/gb/gb/gb.h rename to src/worker/lib/gb/gb/gb.h diff --git a/presets/gb/gb/gbdecompress.h b/src/worker/lib/gb/gb/gbdecompress.h similarity index 100% rename from presets/gb/gb/gbdecompress.h rename to src/worker/lib/gb/gb/gbdecompress.h diff --git a/presets/gb/gb/hardware.h b/src/worker/lib/gb/gb/hardware.h similarity index 100% rename from presets/gb/gb/hardware.h rename to src/worker/lib/gb/gb/hardware.h diff --git a/presets/gb/gb/isr.h b/src/worker/lib/gb/gb/isr.h similarity index 100% rename from presets/gb/gb/isr.h rename to src/worker/lib/gb/gb/isr.h diff --git a/presets/gb/gb/metasprites.h b/src/worker/lib/gb/gb/metasprites.h similarity index 100% rename from presets/gb/gb/metasprites.h rename to src/worker/lib/gb/gb/metasprites.h diff --git a/presets/gb/gb/sgb.h b/src/worker/lib/gb/gb/sgb.h similarity index 100% rename from presets/gb/gb/sgb.h rename to src/worker/lib/gb/gb/sgb.h diff --git a/presets/gb/gb/types.h b/src/worker/lib/gb/gb/types.h similarity index 100% rename from presets/gb/gb/types.h rename to src/worker/lib/gb/gb/types.h diff --git a/presets/gb/gb/global.sgb b/src/worker/lib/gb/global.sgb similarity index 100% rename from presets/gb/gb/global.sgb rename to src/worker/lib/gb/global.sgb diff --git a/src/worker/lib/gb/sfr.lst b/src/worker/lib/gb/sfr.lst new file mode 100644 index 00000000..1de4c488 --- /dev/null +++ b/src/worker/lib/gb/sfr.lst @@ -0,0 +1,111 @@ + 008000 1 __VRAM = 0x8000 + 008000 2 __VRAM8000 = 0x8000 + 008800 3 __VRAM8800 = 0x8800 + 009000 4 __VRAM9000 = 0x9000 + 009800 5 __SCRN0 = 0x9800 + 009C00 6 __SCRN1 = 0x9C00 + 00A000 7 __SRAM = 0xA000 + 00C000 8 __RAM = 0xC000 + 00D000 9 __RAMBANK = 0xD000 + 00FE00 10 __OAMRAM = 0xFE00 + 00FF00 11 __IO = 0xFF00 + 00FF30 12 __AUD3WAVERAM = 0xFF30 + 00FF80 13 __HRAM = 0xFF80 + 14 + 15 .globl __VRAM, __VRAM8000, __VRAM8800, __VRAM9000, __SCRN0, __SCRN1, __SRAM, __RAM, __RAMBANK, __OAMRAM, __IO, __AUD3WAVERAM, __HRAM + 16 + 000000 17 _rRAMG = 0x0000 + 002000 18 _rROMB0 = 0x2000 + 003000 19 _rROMB1 = 0x3000 + 004000 20 _rRAMB = 0x4000 + 21 + 22 .globl _rRAMG, _rROMB0, _rROMB1, _rRAMB + 23 + 00FF00 24 _P1_REG = 0xFF00 ; Joystick: 1.1.P15.P14.P13.P12.P11.P10 + 00FF01 25 _SB_REG = 0xFF01 ; Serial IO data buffer + 00FF02 26 _SC_REG = 0xFF02 ; Serial IO control register + 00FF04 27 _DIV_REG = 0xFF04 ; Divider register + 00FF05 28 _TIMA_REG = 0xFF05 ; Timer counter + 00FF06 29 _TMA_REG = 0xFF06 ; Timer modulo + 00FF07 30 _TAC_REG = 0xFF07 ; Timer control + 00FF0F 31 _IF_REG = 0xFF0F ; Interrupt flags: 0.0.0.JOY.SIO.TIM.LCD.VBL + 00FF10 32 _NR10_REG = 0xFF10 ; Sound register + 00FF11 33 _NR11_REG = 0xFF11 ; Sound register + 00FF12 34 _NR12_REG = 0xFF12 ; Sound register + 00FF13 35 _NR13_REG = 0xFF13 ; Sound register + 00FF14 36 _NR14_REG = 0xFF14 ; Sound register + 00FF16 37 _NR21_REG = 0xFF16 ; Sound register + 00FF17 38 _NR22_REG = 0xFF17 ; Sound register + 00FF18 39 _NR23_REG = 0xFF18 ; Sound register + 00FF19 40 _NR24_REG = 0xFF19 ; Sound register + 00FF1A 41 _NR30_REG = 0xFF1A ; Sound register + 00FF1B 42 _NR31_REG = 0xFF1B ; Sound register + 00FF1C 43 _NR32_REG = 0xFF1C ; Sound register + 00FF1D 44 _NR33_REG = 0xFF1D ; Sound register + 00FF1E 45 _NR34_REG = 0xFF1E ; Sound register + 00FF20 46 _NR41_REG = 0xFF20 ; Sound register + 00FF21 47 _NR42_REG = 0xFF21 ; Sound register + 00FF22 48 _NR43_REG = 0xFF22 ; Sound register + 00FF23 49 _NR44_REG = 0xFF23 ; Sound register + 00FF24 50 _NR50_REG = 0xFF24 ; Sound register + 00FF25 51 _NR51_REG = 0xFF25 ; Sound register + 00FF26 52 _NR52_REG = 0xFF26 ; Sound register + 00FF30 53 _PCM_SAMPLE = 0xFF30 ; PCM wave pattern + 00FF30 54 _AUD3WAVE = 0xFF30 ; PCM wave pattern + 00FF40 55 _LCDC_REG = 0xFF40 ; LCD control + 00FF41 56 _STAT_REG = 0xFF41 ; LCD status + 00FF42 57 _SCY_REG = 0xFF42 ; Scroll Y + 00FF43 58 _SCX_REG = 0xFF43 ; Scroll X + 00FF44 59 _LY_REG = 0xFF44 ; LCDC Y-coordinate + 00FF45 60 _LYC_REG = 0xFF45 ; LY compare + 00FF46 61 _DMA_REG = 0xFF46 ; DMA transfer + 00FF47 62 _BGP_REG = 0xFF47 ; BG palette data + 00FF48 63 _OBP0_REG = 0xFF48 ; OBJ palette 0 data + 00FF49 64 _OBP1_REG = 0xFF49 ; OBJ palette 1 data + 00FF4A 65 _WY_REG = 0xFF4A ; Window Y coordinate + 00FF4B 66 _WX_REG = 0xFF4B ; Window X coordinate + 00FF4D 67 _KEY1_REG = 0xFF4D ; CPU speed + 00FF4F 68 _VBK_REG = 0xFF4F ; VRAM bank + 00FF51 69 _HDMA1_REG = 0xFF51 ; DMA control 1 + 00FF52 70 _HDMA2_REG = 0xFF52 ; DMA control 2 + 00FF53 71 _HDMA3_REG = 0xFF53 ; DMA control 3 + 00FF54 72 _HDMA4_REG = 0xFF54 ; DMA control 4 + 00FF55 73 _HDMA5_REG = 0xFF55 ; DMA control 5 + 00FF56 74 _RP_REG = 0xFF56 ; IR port + 00FF68 75 _BCPS_REG = 0xFF68 ; BG color palette specification + 00FF69 76 _BCPD_REG = 0xFF69 ; BG color palette data + 00FF6A 77 _OCPS_REG = 0xFF6A ; OBJ color palette specification + 00FF6B 78 _OCPD_REG = 0xFF6B ; OBJ color palette data + 00FF70 79 _SVBK_REG = 0xFF70 ; WRAM bank + 00FF76 80 _PCM12_REG = 0xFF76 ; Sound channel 1&2 PCM amplitude (R) + 00FF77 81 _PCM34_REG = 0xFF77 ; Sound channel 3&4 PCM amplitude (R) + 00FFFF 82 _IE_REG = 0xFFFF ; Interrupt enable + 83 + 84 .globl _P1_REG + 85 .globl _SB_REG, _SC_REG + 86 .globl _DIV_REG + 87 .globl _TIMA_REG, _TMA_REG, _TAC_REG + 88 .globl _IF_REG + 89 .globl _NR10_REG, _NR11_REG, _NR12_REG, _NR13_REG, _NR14_REG + 90 .globl _NR21_REG, _NR22_REG, _NR23_REG, _NR24_REG + 91 .globl _NR30_REG, _NR31_REG, _NR32_REG, _NR33_REG, _NR34_REG + 92 .globl _NR41_REG, _NR42_REG, _NR43_REG, _NR44_REG + 93 .globl _NR50_REG, _NR51_REG, _NR52_REG + 94 .globl _PCM_SAMPLE, _AUD3WAVE + 95 .globl _LCDC_REG + 96 .globl _STAT_REG + 97 .globl _SCY_REG, _SCX_REG + 98 .globl _LY_REG, _LYC_REG + 99 .globl _DMA_REG + 100 .globl _BGP_REG + 101 .globl _OBP0_REG, _OBP1_REG + 102 .globl _WY_REG, _WX_REG + 103 .globl _KEY1_REG + 104 .globl _VBK_REG + 105 .globl _HDMA1_REG, _HDMA2_REG, _HDMA3_REG, _HDMA4_REG, _HDMA5_REG + 106 .globl _RP_REG + 107 .globl _BCPS_REG, _BCPD_REG + 108 .globl _OCPS_REG, _OCPD_REG + 109 .globl _SVBK_REG + 110 .globl _PCM12_REG, _PCM34_REG + 111 .globl _IE_REG diff --git a/src/worker/lib/gb/sfr.rel b/src/worker/lib/gb/sfr.rel new file mode 100644 index 00000000..e0780be6 --- /dev/null +++ b/src/worker/lib/gb/sfr.rel @@ -0,0 +1,80 @@ +XL3 +H 1 areas 4D global symbols +S _LY_REG Def00FF44 +S _OCPD_REG Def00FF6B +S _BCPS_REG Def00FF68 +S _LYC_REG Def00FF45 +S __VRAM8800 Def008800 +S _TIMA_REG Def00FF05 +S _SCX_REG Def00FF43 +S _WX_REG Def00FF4B +S _SCY_REG Def00FF42 +S _WY_REG Def00FF4A +S _OCPS_REG Def00FF6A +S _SVBK_REG Def00FF70 +S __IO Def00FF00 +S _STAT_REG Def00FF41 +S _NR10_REG Def00FF10 +S _P1_REG Def00FF00 +S _NR11_REG Def00FF11 +S __AUD3WAVERAM Def00FF30 +S __RAM Def00C000 +S .__.ABS. Def000000 +S _PCM12_REG Def00FF76 +S _AUD3WAVE Def00FF30 +S _NR30_REG Def00FF1A +S _NR21_REG Def00FF16 +S _NR12_REG Def00FF12 +S _PCM_SAMPLE Def00FF30 +S _NR31_REG Def00FF1B +S _NR22_REG Def00FF17 +S _NR13_REG Def00FF13 +S _NR50_REG Def00FF24 +S _NR41_REG Def00FF20 +S _NR32_REG Def00FF1C +S _NR23_REG Def00FF18 +S _NR14_REG Def00FF14 +S _NR51_REG Def00FF25 +S _NR42_REG Def00FF21 +S _NR33_REG Def00FF1D +S _NR24_REG Def00FF19 +S _PCM34_REG Def00FF77 +S _NR52_REG Def00FF26 +S _NR43_REG Def00FF22 +S _NR34_REG Def00FF1E +S _NR44_REG Def00FF23 +S __SCRN0 Def009800 +S __SCRN1 Def009C00 +S __HRAM Def00FF80 +S _HDMA1_REG Def00FF51 +S _HDMA2_REG Def00FF52 +S _HDMA3_REG Def00FF53 +S _IE_REG Def00FFFF +S _HDMA4_REG Def00FF54 +S _HDMA5_REG Def00FF55 +S _IF_REG Def00FF0F +S _OBP0_REG Def00FF48 +S _OBP1_REG Def00FF49 +S _DMA_REG Def00FF46 +S _SB_REG Def00FF01 +S _rROMB0 Def002000 +S __SRAM Def00A000 +S _LCDC_REG Def00FF40 +S _SC_REG Def00FF02 +S _rROMB1 Def003000 +S _rRAMB Def004000 +S _TAC_REG Def00FF07 +S __VRAM Def008000 +S _BCPD_REG Def00FF69 +S _BGP_REG Def00FF47 +S _KEY1_REG Def00FF4D +S _rRAMG Def000000 +S __RAMBANK Def00D000 +S __OAMRAM Def00FE00 +S __VRAM8000 Def008000 +S __VRAM9000 Def009000 +S _RP_REG Def00FF56 +S _TMA_REG Def00FF06 +S _VBK_REG Def00FF4F +S _DIV_REG Def00FF04 +A _CODE size 0 flags 0 diff --git a/presets/gb/gb/sfr.sgb b/src/worker/lib/gb/sfr.sgb similarity index 100% rename from presets/gb/gb/sfr.sgb rename to src/worker/lib/gb/sfr.sgb diff --git a/presets/nes/neslib.h b/src/worker/lib/nes/neslib.h similarity index 100% rename from presets/nes/neslib.h rename to src/worker/lib/nes/neslib.h diff --git a/presets/vcs/macro.h b/src/worker/lib/vcs/macro.h similarity index 100% rename from presets/vcs/macro.h rename to src/worker/lib/vcs/macro.h diff --git a/presets/vcs/vcs.h b/src/worker/lib/vcs/vcs.h similarity index 100% rename from presets/vcs/vcs.h rename to src/worker/lib/vcs/vcs.h diff --git a/presets/vcs/xmacro.h b/src/worker/lib/vcs/xmacro.h similarity index 100% rename from presets/vcs/xmacro.h rename to src/worker/lib/vcs/xmacro.h diff --git a/src/worker/platforms.ts b/src/worker/platforms.ts index bef73f9d..a8f62ac7 100644 --- a/src/worker/platforms.ts +++ b/src/worker/platforms.ts @@ -37,6 +37,8 @@ export var PLATFORM_PARAMS = { asm_cfgfile: 'atari2600-asm.cfg', asm_libargs: [], asm_extra_link_files: ['atari2600-asm.cfg'], + // DASM's standard headers, which dasm programs include + extra_compile_files: ['vcs.h', 'macro.h', 'xmacro.h'], define: ['__ATARI2600__'], }, 'mw8080bw': { @@ -205,6 +207,7 @@ export var PLATFORM_PARAMS = { '-D', 'NES_MIRRORING=0', // horizontal mirroring ], extra_link_files: ['crt0.o', 'neslib2.lib', 'neslib2.cfg', 'nesbanked.cfg'], + extra_compile_files: ['neslib.h'], // source link symbol -> linker config (was hardcoded in fixParamsWithDefines) symbolConfigs: { NES_MAPPER: { '4': 'nesbanked.cfg' } }, // iNES header and CHR data are not in CPU address space @@ -396,7 +399,15 @@ export var PLATFORM_PARAMS = { data_start: 0xc0a0, data_size: 0x1f60, stack_end: 0xe000, - extra_link_files: ['gbz80.lib', 'gb.lib'], + extra_link_files: ['gbz80.lib', 'gb.lib', 'sfr.rel', 'sfr.lst', 'crt0.rel', 'crt0.lst'], + // GBDK runtime, from crt0.sgb and sfr.sgb (see src/worker/lib/gb/Makefile) + startup_objs: ['sfr.rel', 'crt0.rel'], + // an assembly program (gingerbread.sgb) brings its own header and vectors + asm_startup_objs: [], + // GBDK 4.0.6 headers for gb.lib (#include "gb/gb.h") + extra_compile_files: ['gb/bgb_emu.h', 'gb/cgb.h', 'gb/crash_handler.h', 'gb/drawing.h', + 'gb/emu_debug.h', 'gb/gb.h', 'gb/gbdecompress.h', 'gb/hardware.h', 'gb/isr.h', + 'gb/metasprites.h', 'gb/sgb.h', 'gb/types.h'], extra_link_args: [ '-l', 'gb', '-g', '_shadow_OAM=0xC000', diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index 6282b4fd..d9d92e49 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -359,15 +359,18 @@ export async function compileCC65(step: BuildStep): Promise { var customArgs = params.extra_compiler_args || ['-T', '-g', '-Oirs', '-Cl', '-W', '-pointer-sign,-no-effect']; args = args.concat(customArgs, args); args.push(step.path); - const { wasi, errno, stderr } = await runCC65Tool(step, 'cc65', args, (fs) => populateFiles(step, fs, { - mainFilePath: step.path, - processFn: (path, code) => { - if (typeof code === 'string') { - code = processEmbedDirective(code); + const { wasi, errno, stderr } = await runCC65Tool(step, 'cc65', args, (fs) => { + populateFiles(step, fs, { + mainFilePath: step.path, + processFn: (path, code) => { + if (typeof code === 'string') { + code = processEmbedDirective(code); + } + return code; } - return code; - } - })); + }); + populateExtraFiles(step, fs, params.extra_compile_files); + }); stderr.forEach(makeErrorMatcher(errors, /(.*?):(\d+): (.+)/, 2, 3, step.path, 1)); checkExitCode('cc65', errno, stderr, errors); if (errors.length) return { errors }; diff --git a/src/worker/tools/dasm.ts b/src/worker/tools/dasm.ts index af7d9065..f8e37855 100644 --- a/src/worker/tools/dasm.ts +++ b/src/worker/tools/dasm.ts @@ -1,9 +1,9 @@ import { WASIRunner } from "../../common/wasi/wasishim"; import { CodeListingMap, WorkerError } from "../../common/workertypes"; -import { BuildStep, BuildStepResult, gatherFiles, putWorkFile, anyTargetChanged } from "../builder"; +import { BuildStep, BuildStepResult, gatherFiles, putWorkFile, anyTargetChanged, populateExtraFiles } from "../builder"; import { msvcErrorMatcher, re_crlf, re_msvc } from "../listingutils"; import { loadWASMBinary } from "../wasmutils"; -import { populateWASIFiles } from "../wasiutils"; +import { populateWASIFiles, wasiFSAdapter } from "../wasiutils"; // DASM writes its listing (-l) in a fixed-column layout, with tabs padding out // the columns. Once the tabs are expanded to 8-column stops the fields are: @@ -348,6 +348,7 @@ export function assembleDASM(step: BuildStep): BuildStepResult { const sympath = step.prefix + '.sym'; const wasi = new WASIRunner(); wasi.initSync(wasiModule); + populateExtraFiles(step, wasiFSAdapter(wasi), step.params.extra_compile_files); populateWASIFiles(wasi, step); wasi.setArgs(['dasm', step.path, '-f3', "-l" + lstpath, diff --git a/src/worker/tools/sdcc.ts b/src/worker/tools/sdcc.ts index 92e79d2d..16799901 100644 --- a/src/worker/tools/sdcc.ts +++ b/src/worker/tools/sdcc.ts @@ -1,7 +1,7 @@ import { Worker } from "node:worker_threads"; import { defineArgs, extraArgsFor, linkSymbolArgs } from "../../common/toolmeta"; import { CodeListingMap, WorkerError } from "../../common/workertypes"; -import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, populateExtraFiles, anyTargetChanged, getWorkFileAsString, fixParamsWithDefines } from "../builder"; +import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, populateExtraFiles, anyTargetChanged, getWorkFileAsString, fixParamsWithDefines, applyAsmProjectParams } from "../builder"; import { parseListing, parseSourceLines, msvcErrorMatcher } from "../listingutils"; import { EmscriptenModule, emglobal, execMain, loadNative, moduleInstFn, print_fn, setupFS, setupStdin } from "../wasmutils"; import { preprocessMCPP } from "./mcpp"; @@ -135,6 +135,10 @@ async function assembleSDAS(step: BuildStep, tool: 'sdasz80' | 'sdasgb'): Promis gatherFiles(step, { mainFilePath: "main.asm" }); var objpath = step.prefix + ".rel"; var lstpath = step.prefix + ".lst"; + // the link step reads these params, so settle them even when up to date + if (step.mainfile) { + applyAsmProjectParams(step.params); // an asm project, not a C one + } if (staleFiles(step, [objpath, lstpath])) { const match_asm_fn = errorMatcherSDASZ80(step.path, errors); var AS: EmscriptenModule = emglobal[tool]({ @@ -240,6 +244,7 @@ export function linkSDLDZ80(step: BuildStep) { if (banked.length && rest.length) objargs = rest.slice(0, -1).concat(banked, rest.slice(-1)); } + objargs = withStartupObjects(params.startup_objs, objargs); args.push.apply(args, objargs); //console.log(args); execMain(step, LDZ80, args); @@ -347,6 +352,17 @@ export function fixBankedCalls(asm: string): string { return asm; } +/** + * The platform's startup objects (crt0), from its lib directory, go first: + * they set the area order. A project that links its own (by file name, so + * gb/crt0.rel replaces crt0.rel) keeps it instead. + */ +export function withStartupObjects(startup: string[] | undefined, objargs: string[]): string[] { + if (!startup) return objargs; + var own = new Set(objargs.map((fn) => fn.split('/').pop())); + return startup.filter((fn) => !own.has(fn)).concat(objargs); +} + export function compileSDCC(step: BuildStep): BuildStepResult { gatherFiles(step, { diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index bd3de58a..78a05d48 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -1,7 +1,7 @@ import type { WorkerResult, WorkerMessage, WorkerError, SourceLine } from "../common/workertypes"; import { getSharedFileSystemName } from "../common/toolmeta"; -import { store, builder, errorResult, getWorkFileAsString } from "./builder"; +import { store, builder, errorResult, getWorkFileAsString, readLibraryHeader } from "./builder"; import { emglobal, fsMeta, loadFilesystem, listSharedFiles, readSharedFile, ensureFilesystem, readWasiSharedFile, listWasiSharedFiles, ensureWasiFilesystem } from "./wasmutils"; // shared FS names starting with 'wasi:' refer to a WASI filesystem zip @@ -48,6 +48,10 @@ export async function handleMessage(data: WorkerMessage): Promise : (ensureFilesystem(fs1.name), await readSharedFile(fs1.name, data.readshared)); return { output: contents, qid: data.qid } as WorkerResult; } + // read a header from the platform's library (src/worker/lib) + if (data.readlib) { + return { output: readLibraryHeader(data.platform, data.readlib), qid: data.qid } as WorkerResult; + } // list files in a filesystem package directory (shared code) if (data.listshared != null) { var fs2 = splitWasiFSName(data.preload_fs); diff --git a/test/cli/testworker.js b/test/cli/testworker.js index 9c5e66f4..ceef0d7d 100644 --- a/test/cli/testworker.js +++ b/test/cli/testworker.js @@ -115,6 +115,20 @@ describe('Worker', function() { assert.equal(msg.output, null); assert.equal(msg.qid, 125); }); + it('should read a platform library header', async function() { + var msg = await queryWorker({platform:'nes', readlib:'neslib.h', updates:[], buildsteps:[], qid:140}); + assert.ok(msg.output instanceof Uint8Array); + assert.ok(new TextDecoder().decode(msg.output).includes('ppu_on_all')); + assert.equal(msg.qid, 140); + msg = await queryWorker({platform:'gb.color', readlib:'gb/gb.h', updates:[], buildsteps:[], qid:141}); + assert.ok(new TextDecoder().decode(msg.output).includes('wait_vbl_done')); + }); + it('should return null for a file the platform library does not list', async function() { + var msg = await queryWorker({platform:'nes', readlib:'neslib2.lib', updates:[], buildsteps:[], qid:142}); + assert.equal(msg.output, null); + msg = await queryWorker({platform:'nes', readlib:'nosuchfile.h', updates:[], buildsteps:[], qid:143}); + assert.equal(msg.output, null); + }); it('should list shared files in WASI filesystem zip', async function() { var msg = await queryWorker({preload_fs:'wasi:cc7800-fs.zip', listshared:'/headers', updates:[], buildsteps:[], qid:126}); assert.ok(Array.isArray(msg.output)); diff --git a/test/unit/testbuildpresetrun.ts b/test/unit/testbuildpresetrun.ts index b4232c51..d4569677 100644 --- a/test/unit/testbuildpresetrun.ts +++ b/test/unit/testbuildpresetrun.ts @@ -1,6 +1,10 @@ import assert from "assert"; import { describe, it } from "mocha"; -import { screenStats } from "../../src/tools/buildpresets"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import { libraryCollisions, screenStats } from "../../src/tools/buildpresets"; +import { PLATFORM_PARAMS } from "../../src/worker/platforms"; // buildpresets --run flags the frames that look like nothing drew: a blank // (untouched) screen or one color covering everything. screenStats is the pure @@ -48,3 +52,24 @@ describe('buildpresets screen check', function () { assert.strictEqual(s.height, 6); }); }); + +// Library headers live in src/worker/lib only; a preset copy would be copied +// into new projects and shadow the one the worker stages. +describe('buildpresets library collisions', function () { + it('finds none in the presets tree', function () { + assert.deepStrictEqual(libraryCollisions('presets', PLATFORM_PARAMS), []); + }); + + it('reports a preset that shadows a library file', function () { + const params = { nes: { extra_compile_files: ['neslib.h'] }, 'gb.color': { extra_compile_files: ['gb/gb.h'] } }; + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'presets-')); + try { + fs.mkdirSync(path.join(dir, 'gb/gb'), { recursive: true }); + fs.writeFileSync(path.join(dir, 'gb/gb/gb.h'), ''); + assert.deepStrictEqual(libraryCollisions(dir, params), + [`${dir}/gb/gb/gb.h shadows library file src/worker/lib/gb/gb/gb.h`]); + } finally { + fs.rmSync(dir, { recursive: true }); + } + }); +}); diff --git a/test/unit/testdasm.ts b/test/unit/testdasm.ts index e3f0802f..6fc9c19e 100644 --- a/test/unit/testdasm.ts +++ b/test/unit/testdasm.ts @@ -201,7 +201,7 @@ describe('DASM listing parser', function () { const files = {}; files['fullgame.a'] = fs.readFileSync('./presets/vcs/examples/fullgame.a', 'utf8'); for (const inc of ['vcs.h', 'macro.h', 'xmacro.h']) { - files[inc] = fs.readFileSync('./presets/vcs/' + inc, 'utf8'); + files[inc] = fs.readFileSync('./src/worker/lib/vcs/' + inc, 'utf8'); } const run = runDASM(files, 'fullgame.a'); assert.strictEqual(run.errno, 0, run.stdout); diff --git a/test/unit/testsdcclink.ts b/test/unit/testsdcclink.ts new file mode 100644 index 00000000..e6362f7b --- /dev/null +++ b/test/unit/testsdcclink.ts @@ -0,0 +1,61 @@ +import assert from "assert"; +import { describe, it } from "mocha"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import { compileSourceFile, preload } from "../../src/tools/testlib"; +import { withStartupObjects } from "../../src/worker/tools/sdcc"; + +// The Game Boy links GBDK's crt0 and sfr objects from src/worker/lib/gb +// ahead of the program's, unless the program links its own. +describe('sdcc startup objects', function () { + it('go first', function () { + assert.deepStrictEqual(withStartupObjects(['sfr.rel', 'crt0.rel'], ['main.rel', 'util.rel']), + ['sfr.rel', 'crt0.rel', 'main.rel', 'util.rel']); + }); + + it('give way to the project\'s own, by file name', function () { + assert.deepStrictEqual(withStartupObjects(['sfr.rel', 'crt0.rel'], ['gb/sfr.rel', 'gb/crt0.rel', 'main.rel']), + ['gb/sfr.rel', 'gb/crt0.rel', 'main.rel']); + assert.deepStrictEqual(withStartupObjects(['sfr.rel', 'crt0.rel'], ['crt0.rel', 'main.rel']), + ['sfr.rel', 'crt0.rel', 'main.rel']); + }); + + it('leave other platforms alone', function () { + assert.deepStrictEqual(withStartupObjects(undefined, ['main.rel']), ['main.rel']); + assert.deepStrictEqual(withStartupObjects([], ['main.rel']), ['main.rel']); + }); +}); + +describe('Game Boy C build', function () { + this.timeout(60000); + const MAIN = '#include "gb/types.h"\n#include "gb/hardware.h"\n#include "gb/gb.h"\n' + + 'void main(void) { wait_vbl_done(); }\n'; + let tmpdir: string; + before(async function () { + tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "sdcclink-")); + await preload('sdcc', 'gb'); + }); + after(function () { fs.rmSync(tmpdir, { recursive: true, force: true }); }); + + function build(name: string, code: string) { + const p = path.join(tmpdir, name); + fs.writeFileSync(p, code); + return compileSourceFile('sdcc', 'gb', p); + } + + it('links crt0 and the GBDK headers from the library', async function () { + const result = await build('plain.c', MAIN); + assert.deepStrictEqual(result.errors || [], []); + assert.strictEqual(result.output.length, 0x8000); + }); + + it('links the project\'s own crt0 instead, to the same ROM', async function () { + for (const f of ['crt0.sgb', 'sfr.sgb', 'global.sgb']) + fs.copyFileSync(path.join('src/worker/lib/gb', f), path.join(tmpdir, f)); + const plain = await build('plain2.c', MAIN); + const own = await build('own.c', '//#link "sfr.sgb"\n//#link "crt0.sgb"\n' + MAIN); + assert.deepStrictEqual(own.errors || [], []); + assert.deepStrictEqual(Buffer.from(own.output), Buffer.from(plain.output)); + }); +});