1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2026-04-19 08:27:40 +00:00

Deploying to gh-pages from @ sehugg/8bitworkshop@8023d56b88 🚀

This commit is contained in:
sehugg
2023-12-25 15:26:17 +00:00
parent fd03d98566
commit 75d8ea2a22
62 changed files with 1488 additions and 214 deletions
+153 -23
View File
@@ -71,6 +71,7 @@ const _BallyAstrocade = function(arcade:boolean) {
const swidth = arcade ? 320 : 160;
const sheight = arcade ? 204 : 102;
const swbytes = swidth >> 2;
const samask = arcade ? 0x3fff : 0xfff;
const INITIAL_WATCHDOG = 256;
const PIXEL_ON = 0xffeeeeee;
const PIXEL_OFF = 0xff000000;
@@ -90,19 +91,19 @@ const _BallyAstrocade = function(arcade:boolean) {
var palinds = new Uint8Array(8);
var refreshlines = 0;
var dirtylines = new Uint8Array(arcade ? 262 : 131);
var vidactive = false;
var rotdata = new Uint8Array(4);
var rotcount = 0;
var intst = 0;
var waitstates = 0;
var patboard = new Uint8Array(0x08);
var patdest = 0;
function ramwrite(a: number, v: number) {
// set RAM
ram[a] = v;
waitstates++;
// mark scanline as dirty
dirtylines[((a & 0xfff) / swbytes) | 0] = 1;
dirtylines[((a & samask) / swbytes) | 0] = 1;
// this was old behavior where we updated instantly
// but it had problems if we had mid-screen palette changes
//ramupdate(a, v);
@@ -201,7 +202,105 @@ const _BallyAstrocade = function(arcade:boolean) {
function refreshall() {
refreshlines = sheight;
}
// bally astrocade pattern board
// https://github.com/mamedev/mame/blob/7ff10685c56a6e123336c684e5e96fdb9e8b3674/src/mame/midway/astrocde_v.cpp#L726
function xfer_patboard() {
let m_pattern_source = patboard[0] | (patboard[1] << 8);
let m_pattern_mode = patboard[2] & 0x3f;
let m_pattern_skip = patboard[3];
let m_pattern_dest = (patdest & 0xff) | (patboard[4] << 8);
let m_pattern_width = patboard[5];
let m_pattern_height = patboard[6] + 1;
let curwidth: number;
let u13ff: number = 0;
let cycles: number = 0;
u13ff = 0;
if ((m_pattern_mode & 0x02) === 0) {
u13ff = 1;
}
function incrementSource(): void {
if (u13ff && (m_pattern_mode & 0x04) !== 0 && (curwidth !== 0 || (m_pattern_mode & 0x08) === 0)) {
m_pattern_source++;
}
if ((m_pattern_mode & 0x02) !== 0) {
u13ff ^= 1;
}
}
function incrementDest(): void {
if (curwidth !== 0) {
if ((m_pattern_mode & 0x20) !== 0) {
m_pattern_dest++;
} else {
m_pattern_dest--;
}
}
}
// Loop over height
while (m_pattern_height >= 0) {
let carry: number;
curwidth = m_pattern_width;
// Loop over width
while (curwidth >= 0) {
let busaddr: number;
let busdata: number;
// Read Phase
busaddr = (m_pattern_mode & 0x01) === 0 ? m_pattern_source : m_pattern_dest;
if (curwidth === 0 && (m_pattern_mode & 0x08) !== 0) {
busdata = 0;
} else {
busdata = membus.read(m_pattern_source);
}
if ((m_pattern_mode & 0x01) === 0) {
incrementSource();
} else {
incrementDest();
}
// Write Phase
busaddr = (m_pattern_mode & 0x01) !== 0 ? m_pattern_source : m_pattern_dest;
ramwrite(busaddr, busdata);
if ((m_pattern_mode & 0x01) === 0) {
incrementDest();
} else {
incrementSource();
}
cycles += 4;
curwidth--;
}
// At the end of each row, adjust m_pattern_dest
carry = ((m_pattern_dest & 0xff) + m_pattern_skip) & 0x100;
m_pattern_dest = (m_pattern_dest & 0xff00) | ((m_pattern_dest + m_pattern_skip) & 0xff);
if ((m_pattern_mode & 0x10) === 0) {
m_pattern_dest += carry;
} else {
m_pattern_dest -= carry ^ 0x100;
}
m_pattern_height--;
}
// Adjust m_maincpu.icount
// m_maincpu.adjust_icount(-cycles);
// Replace the above line with the actual adjustment of icount.
}
this.drawScanline = (sl:number) => {
// interrupt
if (sl == inlin && (inmod & 0x8)) {
@@ -226,9 +325,9 @@ const _BallyAstrocade = function(arcade:boolean) {
ram = r;
inputs = inp;
psg = psgg;
//bios = padBytes(ASTROCADE_MINIMAL_BIOS, 0x2000);
bios = padBytes(new lzgmini().decode(stringToByteArray(atob(ASTROLIBRE_BIOS_LZG))), 0x2000);
if (!arcade) {
//bios = padBytes(ASTROCADE_MINIMAL_BIOS, 0x2000);
bios = padBytes(new lzgmini().decode(stringToByteArray(atob(ASTROLIBRE_BIOS_LZG))), 0x2000);
// game console
membus = {
read: newAddressDecoder([
@@ -246,13 +345,14 @@ const _BallyAstrocade = function(arcade:boolean) {
membus = {
read: newAddressDecoder([
[0x4000, 0x7fff, 0x3fff, function(a) { return ram[a]; }], // screen RAM
[0xd000, 0xdfff, 0xfff, function(a) { return ram[a + 0x4000]; }], // static RAM
[0x0000, 0xafff, 0xffff, function(a) { return rom ? rom[a] : 0; }], // ROM (0-3fff,8000-afff)
[0xd000, 0xdfff, 0x0fff, function(a) { return ram[a + 0x4000]; }], // static RAM
[0x0000, 0x3fff, 0x3fff, function(a) { return rom ? rom[a] : 0; }], // ROM
[0x8000, 0xbfff, 0x3fff, function(a) { return rom ? rom[a + 0x4000] : 0; }], // ROM
]),
write: newAddressDecoder([
[0x4000, 0x7fff, 0x3fff, ramwrite],
[0xd000, 0xdfff, 0xfff, function(a, v) { ramwrite(a + 0x4000, v); }], // static RAM
[0x0000, 0x3fff, 0x3fff, magicwrite],
[0xd000, 0xdfff, 0x0fff, function(a, v) { ramwrite(a + 0x4000, v); }], // static RAM
]),
}
}
@@ -273,6 +373,19 @@ const _BallyAstrocade = function(arcade:boolean) {
return rtn;
},
write: function(addr, val) {
if (addr == 0xa55b) {
// TODO: protected_ram_enable_w
return;
}
addr &= 0xff;
// pattern board
if (addr > 0x78 && addr < 0x80) {
patboard[addr & 7] = val;
if (addr == 0x72) { patdest = 0; }
if (addr == 0x74) { patdest = (patdest + patboard[3]) & 0xff; }
if (addr == 0x7e) { xfer_patboard(); }
return;
}
addr &= 0x1f;
val &= 0xff;
switch (addr) {
@@ -332,8 +445,15 @@ const _BallyAstrocade = function(arcade:boolean) {
case 0x19: // XPAND
xpand = val;
break;
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
//psg2.setACRegister(addr - 0x1a, val);
break;
default:
console.log('IO write', hex(addr, 4), hex(val, 2));
//console.log('IO write', hex(addr, 4), hex(val, 2));
break;
}
}
@@ -372,6 +492,8 @@ const _BallyAstrocade = function(arcade:boolean) {
rotdata.set(state.rotdata);
intst = state.intst;
inputs.set(state.inputs);
patboard.set(state.patboard);
patdest = state.patdest;
refreshall();
}
@@ -393,7 +515,9 @@ const _BallyAstrocade = function(arcade:boolean) {
verbl: verbl,
rotcount: rotcount,
rotdata: rotdata.slice(0),
intst: intst
intst: intst,
patboard: patboard.slice(0),
patdest: patdest,
};
}
this.reset = () => {
@@ -450,6 +574,7 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
ram : Uint8Array;
cpu : Z80;
m; // _BallyAstrocade
arcade : boolean;
psg: AstrocadeAudio;
audioadapter;
@@ -458,6 +583,7 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
constructor(arcade:boolean) {
super();
this.arcade = arcade;
this.cpu = new Z80();
this.psg = new AstrocadeAudio(new MasterAudio());
this.audioadapter = new TssChannelAdapter(this.psg.psg, audioOversample, this.sampleRate);
@@ -471,6 +597,12 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
//this.cpuCyclesPerVisible = this.cpuCyclesPerLine - this.cpuCyclesPerHBlank;
this.m = new _BallyAstrocade(arcade);
this.m.init(this, this.cpu, this.ram, this.inputs, this.psg);
if (arcade) {
this.inputs[0x10] = 0xff; // switches (active low)
this.inputs[0x11] = 0xff; // switches (active low)
this.inputs[0x12] = 0x00;
this.inputs[0x13] = 0x08; // dip switches
}
}
read(a:number) : number {
@@ -556,8 +688,16 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
case 'Astro': return this.m.toLongString(state);
}
}
getRasterCanvasPosition() { return { x: this.getRasterX(), y: this.getRasterY() }; }
getRasterCanvasPosition() {
return { x: this.getRasterX(), y: this.getRasterY() };
}
getVideoParams() {
if (this.arcade) {
return { width: 320, height: 204, rotate: 180 };
} else {
return { width: 160, height: 102 };
}
}
}
/////
@@ -591,16 +731,6 @@ class AstrocadeAudio extends AY38910_Audio {
}
}
export const _BallyArcade = function() {
this.__proto__ = new (_BallyAstrocade as any)(true);
// TODO: inputs[0x13] = 0xfe; // dip switch on arcade
// TODO: arcade controls, bit blit
var _in = this.saveControlsState();
_in.in[0x10] = 0xff; // switches
_in.in[0x13] = 0xfe; // dip switches
this.loadControlsState(_in);
}
/////
//http://glankonian.com/~lance/astrocade_palette.html
+331
View File
@@ -0,0 +1,331 @@
import { MOS6502 } from "../common/cpu/MOS6502";
import { BasicScanlineMachine, CPU } from "../common/devices";
import { KeyFlags, Keys, makeKeycodeMap, newAddressDecoder, newKeyboardHandler } from "../common/emu";
// https://github.com/mamedev/mame/blob/e9ac85ca86a873c67d6bc8d7cf2e37bc7696b379/src/mame/exidy/exidy.cpp#L327
// http://www.arcaderestoration.com/games/9089/Targ.aspx
// http://www.arcaderestoration.com/memorymap/9089/Targ.aspx
// http://www.arcaderestoration.com/memorymap/3933/Hard+Hat.aspx
// https://github.com/mamedev/mame/blob/74c4a0c3774e3aeb4895eb13f3c47773d34ce270/src/mame/shared/exidysound.cpp#L13
const EXIDY_KEYCODE_MAP = makeKeycodeMap([
[Keys.START, 1, -0x1],
//[Keys.START, 1, -0x2],
[Keys.RIGHT, 1, -0x4],
[Keys.LEFT, 1, -0x8],
[Keys.A, 1, -0x10],
[Keys.UP, 1, -0x20],
[Keys.DOWN, 1, -0x40],
[Keys.SELECT, 1, -0x80],
]);
/*
ROM layout:
0x0000 - 0x5fff: program ROM
0x6000 - 0x67ff: sprite ROM
0x6800 - 0x7fff: audio ROM
*/
export class ExidyUGBv2 extends BasicScanlineMachine {
cpuFrequency = 705562;
sampleRate = 894886;
numVisibleScanlines = 256;
numTotalScanlines = 262;
cpuCyclesPerLine = 0x150 >> 3;
canvasWidth = 256;
defaultROMSize = 0x8000 + 0x800 + 0x2800; // PRG + CHR + SOUND
cpu = new MOS6502();
ram = new Uint8Array(0x7000);
color_latch = [0x54, 0xee, 0x6b]; // RGB
palette = [
0xff000000, 0xff0000ff, 0xffff0000, 0xffff00ff,
0xff00ff00, 0xff00ffff, 0xffffff00, 0xffffffff,
];
sprite_gfx: Uint8Array;
inputs = new Uint8Array(4);
keyMap = EXIDY_KEYCODE_MAP;
handler = newKeyboardHandler(this.inputs, this.keyMap); /*, (o,k,c,f) => {
// coin inserted?
if (o.index == 1 && o.mask == 128 && (f & KeyFlags.KeyDown)) {
this.inputs[3] |= 0x40;
//this.cpu.IRQ();
//this.ram[0xa2] += 8; // TODO
}
});
*/
scrnbase = 0x4000;
charbase = 0x6800;
bus = {
read: newAddressDecoder([
[0x0000, 0x03ff, 0, (a) => { return this.ram[a]; }],
[0x1000, 0x3fff, 0, (a) => { return this.rom[a - 0x1000]; }],
[0x4000, 0x4fff, 0, (a) => { return this.ram[a]; }],
[0x5100, 0x51ff, 0x03, (a) => { return a == 3 ? this.int_latch() : this.inputs[a] }],
[0x6000, 0x6fff, 0, (a) => { return this.ram[a]; }],
[0x8000, 0xffff, 0, (a) => { return this.rom[a - 0x8000]; }],
]),
write: newAddressDecoder([
[0x0000, 0x03ff, 0, (a, v) => { this.ram[a] = v; }],
[0x4000, 0x4fff, 0, (a, v) => { this.ram[a] = v; }],
[0x5000, 0x5101, 0, (a, v) => { this.ram[a] = v; }], // TODO: sprite latch
[0x5210, 0x5212, 3, (a, v) => { this.setColorLatch(a, v); }],
[0x6000, 0x6fff, 0, (a, v) => { this.ram[a] = v; }],
]),
}
constructor() {
super();
this.connectCPUMemoryBus(this);
this.updatePalette();
this.inputs[0] = 0b11101010; // dip switch
this.inputs[1] = 0b11111111; // active low
}
loadROM(rom: Uint8Array) {
super.loadROM(rom);
if (rom.length < 0x8000) {
if (rom.length == 11616) { // targ
this.rom.set(rom.slice(0x2800, 0x3000), 0x8000); // copy sprites
this.rom.set(rom.slice(0x2700, 0x2800), 0x7f00); // copy ff00-ffff
this.rom.set(rom.slice(0x0000, 0x2800), 0x0800); // ROM starts @ 0x1800
this.scrnbase = 0x4000;
this.charbase = 0x4800;
} else if (rom.length == 14336) { // spectar
this.rom.set(rom.slice(0x3400, 0x3800), 0x8000); // copy sprites
this.rom.set(rom.slice(0x2f00, 0x3000), 0x7f00); // copy ff00-ffff
this.scrnbase = 0x4000;
this.charbase = 0x4800;
} else {
console.log("Warning: ROM is too small", rom.length);
}
}
let sprite_ofs = 0x8000;
this.sprite_gfx = this.rom.subarray(sprite_ofs, sprite_ofs + 32 * 32);
}
read(a: number): number {
return this.bus.read(a);
}
readConst(a: number): number {
if (a == 0x5103) return this.inputs[3];
return this.bus.read(a);
}
write(a: number, v: number): void {
this.bus.write(a, v);
}
int_latch() {
let intsrc = this.inputs[3];
intsrc |= (this.inputs[1] & 0x80) ? 0 : 0x40; // coin 1
this.inputs[3] = 0x80; // clear int latch
return intsrc; // TODO
}
updatePalette() {
/* motion object 1 */
this.set_1_color(0, 0);
this.set_1_color(1, 7);
/* motion object 2 */
this.set_1_color(2, 0);
this.set_1_color(3, 6);
/* characters */
this.set_1_color(4, 4);
this.set_1_color(5, 3);
this.set_1_color(6, 2);
this.set_1_color(7, 1);
}
set_1_color(index: number, dipsw: number) {
let r = (this.color_latch[0] & (1 << dipsw)) ? 1 : 0;
let g = (this.color_latch[1] & (1 << dipsw)) ? 2 : 0;
let b = (this.color_latch[2] & (1 << dipsw)) ? 4 : 0;
this.palette[index] = RGB8[r | g | b];
}
setColorLatch(a: number, v: number): void {
this.color_latch[a & 3] = v;
this.updatePalette();
}
drawSprite(xpos: number, ypos: number, ofs: number, palind: number) {
var sx = 236 - xpos - 4;
var sy = 244 - ypos - 4;
/*
m_gfxdecode->gfx(0)->transpen(bitmap,cliprect,
((*m_spriteno >> 4) & 0x0f) + 32 + 16 * sprite_set_2, 1,
0, 0, sx, sy, 0);*/
sy += 15;
sy -= this.scanline;
if (sy >= 0 && sy < 16) {
sy = 15 - sy;
//console.log("draw sprite", sx, sy, ofs);
let yofs = this.scanline * this.canvasWidth;
let pix = this.sprite_gfx[ofs + sy];
for (let x = 0; x < 8; x++) {
if (pix & (0x80 >> x)) {
this.pixels[yofs + sx + x] = this.palette[palind];
}
}
pix = this.sprite_gfx[ofs + sy + 16];
for (let x = 0; x < 8; x++) {
if (pix & (0x80 >> x)) {
this.pixels[yofs + sx + x + 8] = this.palette[palind];
}
}
}
}
drawSprite1() {
let xpos = this.ram[0x5000];
let ypos = this.ram[0x5040];
let set = (this.ram[0x5101] & 0x20) ? 1 : 0;
let sprite = (this.ram[0x5100] & 0x0f) + 16 * set;
this.drawSprite(xpos, ypos, sprite * 32, 1);
}
drawSprite2() {
let xpos = this.ram[0x5080];
let ypos = this.ram[0x50c0];
let set = (this.ram[0x5101] & 0x40) ? 1 : 0;
let sprite = (this.ram[0x5100] >> 4) + 16 * set;
this.drawSprite(xpos, ypos, sprite * 32, 3);
}
startScanline(): void {
}
drawScanline(): void {
const y = this.scanline;
const row = y >> 3;
const yofs = y * this.canvasWidth;
for (let x = 0; x < 256; x++) {
const col = x >> 3;
let code = this.ram[this.scrnbase + row * 32 + col];
let color1 = 4 + ((code >> 6) & 0x03);
let pix = this.ram[this.charbase + code * 8 + (y & 7)];
let palind = (pix & (0x80 >> (x & 7))) ? color1 : 0;
this.pixels[yofs + x] = this.palette[palind];
}
this.drawSprite2();
this.drawSprite1();
}
postFrame() {
this.inputs[3] &= 0x7f; // TODO?
this.cpu.IRQ();
}
getVideoParams() {
return { width: 256, height: 256, aspect: 6/5 };
}
}
const RGB8 = [
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff,
];
/*
0000-00FF R/W Zero Page RAM
0100-01FF R/W Stack RAM
0200-03FF R/W Scratchpad RAM
0800-3FFF R Program ROM (Targ, Spectar only)
1A00 R PX3 (Player 2 inputs) (Fax only)
bit 4 D
bit 5 C
bit 6 B
bit 7 A
1C00 R PX2 (Player 1 inputs) (Fax only)
bit 0 2 player start
bit 1 1 player start
bit 4 D
bit 5 C
bit 6 B
bit 7 A
2000-3FFF R Banked question ROM (Fax only)
4000-43FF R/W Screen RAM
4800-4FFF R/W Character Generator RAM (except Pepper II and Fax)
5000 W Motion Object 1 Horizontal Position Latch (sprite 1 X)
5040 W Motion Object 1 Vertical Position Latch (sprite 1 Y)
5080 W Motion Object 2 Horizontal Position Latch (sprite 2 X)
50C0 W Motion Object 2 Vertical Position Latch (sprite 2 Y)
5100 R Option Dipswitch Port
bit 0 coin 2 (NOT inverted) (must activate together with $5103 bit 5)
bit 1-2 bonus
bit 3-4 coins per play
bit 5-6 lives
bit 7 US/UK coins
5100 W Motion Objects Image Latch
Sprite number bits 0-3 Sprite #1 4-7 Sprite #2
5101 R Control Inputs Port
bit 0 start 1
bit 1 start 2
bit 2 right
bit 3 left
bit 5 up
bit 6 down
bit 7 coin 1 (must activate together with $5103 bit 6)
5101 W Output Control Latch (not used in PEPPER II upright)
bit 7 Enable sprite #1
bit 6 Enable sprite #2
5103 R Interrupt Condition Latch
bit 0 LNG0 - supposedly a language DIP switch
bit 1 LNG1 - supposedly a language DIP switch
bit 2 different for each game, but generally a collision bit
bit 3 TABLE - supposedly a cocktail table DIP switch
bit 4 different for each game, but generally a collision bit
bit 5 coin 2 (must activate together with $5100 bit 0)
bit 6 coin 1 (must activate together with $5101 bit 7)
bit 7 L256 - VBlank?
5213 R IN2 (Mouse Trap)
bit 3 blue button
bit 2 free play
bit 1 red button
bit 0 yellow button
52XX R/W Audio/Color Board Communications
6000-6FFF R/W Character Generator RAM (Pepper II, Fax only)
8000-FFF9 R Program memory space
FFFA-FFFF R Interrupt and Reset Vectors
Exidy Sound Board:
0000-07FF R/W RAM (mirrored every 0x7f)
0800-0FFF R/W 6532 Timer
1000-17FF R/W 6520 PIA
1800-1FFF R/W 8253 Timer
2000-27FF bit 0 Channel 1 Filter 1 enable
bit 1 Channel 1 Filter 2 enable
bit 2 Channel 2 Filter 1 enable
bit 3 Channel 2 Filter 2 enable
bit 4 Channel 3 Filter 1 enable
bit 5 Channel 3 Filter 2 enable
2800-2FFF 6840 Timer
3000 Bit 0..1 Noise select
3001 Bit 0..2 Channel 1 Amplitude
3002 Bit 0..2 Channel 2 Amplitude
3003 Bit 0..2 Channel 3 Amplitude
5800-7FFF ROM
Targ:
5200 Sound board control
bit 0 Music
bit 1 Shoot
bit 2 unused
bit 3 Swarn
bit 4 Sspec
bit 5 crash
bit 6 long
bit 7 game
5201 Sound board control
bit 0 note
bit 1 upper
MouseTrap:
5101 W MouseTrap P1/P2 LED States
bit 2 Player 1 LED state
bit 4 Player 2 LED state
MouseTrap Digital Sound:
0000-3FFF ROM
IO:
A7 = 0: R Communication from sound processor
A6 = 0: R CVSD Clock State
A5 = 0: W Busy to sound processor
A4 = 0: W Data to CVSD
*/
+1
View File
@@ -12,6 +12,7 @@ export function importPlatform(name: string) : Promise<any> {
case "coleco": return import("../platform/coleco");
case "cpc": return import("../platform/cpc");
case "devel": return import("../platform/devel");
case "exidy": return import("../platform/exidy");
case "galaxian": return import("../platform/galaxian");
case "kim1": return import("../platform/kim1");
case "markdown": return import("../platform/markdown");
+7
View File
@@ -26,6 +26,10 @@ const ASTROCADE_BIOS_PRESETS = [
{ id: 'bios.c', name: 'BIOS' },
];
const ASTROCADE_ARCADE_PRESETS = [
{ id: 'hello.c', name: 'Hello Graphics' },
];
class BallyAstrocadePlatform extends BaseZ80MachinePlatform<BallyAstrocade> implements Platform {
newMachine() { return new BallyAstrocade(false); }
@@ -52,10 +56,13 @@ class BallyAstrocadeBIOSPlatform extends BallyAstrocadePlatform implements Platf
class BallyArcadePlatform extends BallyAstrocadePlatform implements Platform {
newMachine() { return new BallyAstrocade(true); }
getPresets() { return ASTROCADE_ARCADE_PRESETS; }
getMemoryMap = function() { return { main:[
{name:'ROM',start:0x0,size:0x4000,type:'rom'},
{name:'Magic RAM',start:0x0,size:0x4000,type:'ram'},
{name:'Screen RAM',start:0x4000,size:0x4000,type:'ram'},
{name:'ROM',start:0x8000,size:0x4000,type:'rom'},
] } };
}
+32
View File
@@ -0,0 +1,32 @@
import { Base6502MachinePlatform, Platform } from "../common/baseplatform";
import { PLATFORMS } from "../common/emu";
import { ExidyUGBv2 } from "../machine/exidy";
var EXIDY_PRESETS = [
{ id: 'minimal.c', name: 'Minimal Example', category: "C" },
];
class ExidyUGBPlatform extends Base6502MachinePlatform<ExidyUGBv2> implements Platform {
newMachine() { return new ExidyUGBv2(); }
getPresets() { return EXIDY_PRESETS; }
getDefaultExtension() { return ".dasm"; };
readAddress(a) { return this.machine.readConst(a); }
getMemoryMap() {
return { main: [
{ name: 'RAM', start: 0x00, size: 0x400, type: 'ram' },
{ name: 'Sprite I/O', start: 0x5000, size: 0x100, type: 'io' },
{ name: 'I/O', start: 0x5100, size: 0x3, type: 'io' },
{ name: 'PIA 6821', start: 0x5200, size: 0xf, type: 'io' },
{ name: 'Color Latches', start: 0x5210, size: 0x3, type: 'io' },
{ name: 'Screen RAM', start: 0x4000, size: 0x400, type: 'ram' },
{ name: 'Character RAM', start: 0x6800, size: 0x800, type: 'ram' },
{ name: 'Audio ROM', start: 0x5800, size: 0x2800, type: 'rom' },
{ name: 'Program ROM', start: 0x8000, size: 0x8000, type: 'rom' },
]
} }
}
PLATFORMS["exidy"] = ExidyUGBPlatform;
+18 -1
View File
@@ -1,4 +1,4 @@
import { getBasePlatform } from "../common/util";
import { convertDataToUint8Array, getBasePlatform } from "../common/util";
import { WorkerBuildStep, WorkerError, WorkerErrorResult, WorkerMessage, WorkerResult, WorkingStore } from "../common/workertypes";
import { PLATFORM_PARAMS } from "./platforms";
import { TOOLS } from "./workertools";
@@ -414,3 +414,20 @@ export function fixParamsWithDefines(path: string, params) {
}
}
}
export function processEmbedDirective(code: string) {
let re3 = /^\s*#embed\s+"(.+?)"/gm;
// find #embed "filename.bin" and replace with C array data
return code.replace(re3, (m, m1) => {
let filename = m1;
let filedata = store.getFileData(filename);
let bytes = convertDataToUint8Array(filedata);
if (!bytes) throw new Error('#embed: file not found: "' + filename + '"');
let out = '';
for (let i = 0; i < bytes.length; i++) {
out += bytes[i].toString() + ',';
}
return out.substring(0, out.length-1);
});
}
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
; Startup code for cc65 and Shiru's NES library
; based on code by Groepaz/Hitmen <groepaz@gmx.net>, Ullrich von Bassewitz <uz@cc65.org>
; edited by Steven Hugg for Exidy
.export _exit,__STARTUP__:absolute=1
.export _HandyRTI
.exportzp _INTVEC
.export NMI,IRQ,START
.import initlib,push0,popa,popax,_main,zerobss,copydata
.importzp sp
; Linker generated symbols
.import __RAM0_START__ ,__RAM0_SIZE__
.import __ROM_START__ ,__ROM_SIZE__
.import __STARTUP_LOAD__,__STARTUP_RUN__,__STARTUP_SIZE__
.import __CODE_LOAD__ ,__CODE_RUN__ ,__CODE_SIZE__
.import __RODATA_LOAD__ ,__RODATA_RUN__ ,__RODATA_SIZE__
.segment "ZEROPAGE"
_INTVEC: .res 2
.segment "STARTUP"
START:
_exit:
sei ;Disable interrupts
cld ;Clear decimal mode
ldx #$ff ;Setup stack pointer
txs
@irrwait:
lda $5103
dex
bne @irrwait
lda #$00 ;Clear Ram
sta $5100 ;Set sprites to #0
@2:
sta $0,x
sta $100,x
sta $200,x
sta $300,x
sta $4000,x
sta $4100,x
sta $4200,x
sta $4300,x
sta $6800,x
sta $6900,x
sta $6a00,x
sta $6b00,x
sta $6c00,x
sta $6d00,x
sta $6e00,x
sta $6f00,x
inx
bne @2
; copy data segment
jsr copydata
; initialize cc65 stack
lda #<(__RAM0_START__+__RAM0_SIZE__)
sta sp
lda #>(__RAM0_START__+__RAM0_SIZE__)
sta sp+1
; init CC65 library
jsr initlib
; set interrupt vector in ZP
lda #<_HandyRTI
sta _INTVEC
lda #>_HandyRTI
sta _INTVEC+1
cli ;Enable interrupts
; start main()
jmp _main ;no parameters
; interrupt handler
NMI:
IRQ:
jmp (_INTVEC)
_HandyRTI:
rti
; CPU vectors
.segment "VECTORS"
.word NMI ;$fffa vblank nmi
.word START ;$fffc reset
.word IRQ ;$fffe irq / brk
+50
View File
@@ -0,0 +1,50 @@
SYMBOLS {
__STACKSIZE__: type = weak, value = $0100;
}
MEMORY {
# Zero Page
ZP: file = "", start = $0000, size = $0100, type = rw, define = yes;
# RAM
RAM0: file = "", start = $200, size = $200, define = yes;
# ROM Bank
PRG: file = %O, start = $8000, size = $8000 - 6, fill = yes, define = yes;
# CPU Vectors
VECTORS: file = %O, start = $FFFA, size = $0006, fill = yes;
# Sprite Bitmaps
SPRITES: file = %O, start = $0, size = $800, fill = yes, define = yes;
# Audio ROM
AUDIO: file = %O, start = $5800, size = $2800, fill = yes, define = yes;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
STARTUP: load = PRG, type = ro, define = yes;
RODATA: load = PRG, type = ro, define = yes;
ONCE: load = PRG, type = ro, optional = yes;
CODE: load = PRG, type = ro, define = yes;
DATA: load = PRG, run = RAM0, type = rw, define = yes;
VECTORS: load = VECTORS, type = ro;
SPRITES: load = SPRITES, type = ro, optional = yes;
BSS: load = RAM0, type = bss, define = yes;
RAM: load = RAM0, type = rw, optional = yes;
AUDIO: load = AUDIO, type = ro, optional = yes;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__,
segment = ONCE;
CONDES: type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__,
segment = RODATA;
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;
}
+7
View File
@@ -327,6 +327,13 @@ export var PLATFORM_PARAMS = {
cfgfile: 'pce.cfg',
libargs: ['pce.lib', '-D', '__CARTSIZE__=0x8000'],
},
'exidy': {
define: ['__EXIDY__'],
cfgfile: 'exidy.cfg',
libargs: ['crt0.o', 'none.lib'],
extra_link_files: ['crt0.o', 'exidy.cfg'],
//extra_compile_files: ['exidy.h'],
},
};
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];
+3 -19
View File
@@ -1,7 +1,7 @@
import { convertDataToUint8Array, getFilenamePrefix, getRootBasePlatform, safeident } from "../../common/util";
import { getRootBasePlatform } from "../../common/util";
import { CodeListingMap, WorkerError } from "../../common/workertypes";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, fixParamsWithDefines, putWorkFile, populateExtraFiles, store, populateEntry, anyTargetChanged } from "../builder";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, fixParamsWithDefines, putWorkFile, populateExtraFiles, store, populateEntry, anyTargetChanged, processEmbedDirective } from "../builder";
import { re_crlf, makeErrorMatcher } from "../listingutils";
import { loadNative, moduleInstFn, print_fn, setupFS, execMain, emglobal, EmscriptenModule } from "../wasmutils";
@@ -270,22 +270,6 @@ export function linkLD65(step: BuildStep): BuildStepResult {
}
}
function processIncbin(code: string) {
let re3 = /^\s*#embed\s+"(.+?)"/gm;
// find #embed "filename.bin" and replace with C array data
return code.replace(re3, (m, m1) => {
let filename = m1;
let filedata = store.getFileData(filename);
let bytes = convertDataToUint8Array(filedata);
if (!bytes) throw new Error('#embed: file not found: "' + filename + '"');
let out = '';
for (let i = 0; i < bytes.length; i++) {
out += bytes[i].toString() + ',';
}
return out;
});
}
export function compileCC65(step: BuildStep): BuildStepResult {
loadNative("cc65");
var params = step.params;
@@ -321,7 +305,7 @@ export function compileCC65(step: BuildStep): BuildStepResult {
mainFilePath: step.path,
processFn: (path, code) => {
if (typeof code === 'string') {
code = processIncbin(code);
code = processEmbedDirective(code);
}
return code;
}
+10 -2
View File
@@ -1,5 +1,5 @@
import { getBasePlatform } from "../../common/util";
import { BuildStep, populateFiles, populateExtraFiles, errorResult } from "../builder";
import { BuildStep, populateFiles, populateExtraFiles, errorResult, processEmbedDirective } from "../builder";
import { makeErrorMatcher, extractErrors } from "../listingutils";
import { PLATFORM_PARAMS } from "../platforms";
import { load, print_fn, setupFS, execMain, emglobal, EmscriptenModule } from "../wasmutils";
@@ -24,7 +24,15 @@ export function preprocessMCPP(step: BuildStep, filesys: string) {
});
var FS = MCPP.FS;
if (filesys) setupFS(FS, filesys);
populateFiles(step, FS);
populateFiles(step, FS, {
mainFilePath: step.path,
processFn: (path, code) => {
if (typeof code === 'string') {
code = processEmbedDirective(code);
}
return code;
}
});
populateExtraFiles(step, FS, params.extra_compile_files);
// TODO: make configurable by other compilers
var args = [
+3
View File
@@ -146,6 +146,9 @@ export function linkSDLDZ80(step: BuildStep) {
args.push.apply(args, step.args);
//console.log(args);
execMain(step, LDZ80, args);
if (errors.length) {
return { errors: errors };
}
var hexout = FS.readFile("main.ihx", { encoding: 'utf8' });
var noiout = FS.readFile("main.noi", { encoding: 'utf8' });
putWorkFile("main.ihx", hexout);
+1
View File
@@ -149,6 +149,7 @@ export function setupFS(FS, name: string) {
if (name === '65-atari7800') name = '65-none'; // TODO
if (name === '65-devel') name = '65-none'; // TODO
if (name === '65-vcs') name = '65-atari2600'; // TODO
if (name === '65-exidy') name = '65-none'; // TODO
if (!fsMeta[name]) throw Error("No filesystem for '" + name + "'");
FS.mkdir('/share');
FS.mount(WORKERFS, {
+2
View File
@@ -75,6 +75,8 @@ export const TOOL_PRELOADFS = {
'ca65-vcs': '65-atari2600',
'cc65-pce': '65-pce',
'ca65-pce': '65-pce',
'cc65-exidy': '65-none',
'ca65-exidy': '65-none',
'sdasz80': 'sdcc',
'sdcc': 'sdcc',
'sccz80': 'sccz80',