diff --git a/presets/exidy/minimal.c b/presets/exidy/minimal.c new file mode 100644 index 00000000..092aea6b --- /dev/null +++ b/presets/exidy/minimal.c @@ -0,0 +1,71 @@ + +#include +#include + +extern void (*INTVEC)(); +#pragma zpsym("INTVEC") + +char framecount; + +void irq_handler() { + asm("inc _framecount"); + asm("rti"); +} + +void irq_setup() { + INTVEC = irq_handler; +} + +// lower 4 bits are background +// upper 2 bits are sprites +const char PALETTE[3] = { + 0b01000001, // red + 0b11111010, // green + 0b10111100, // blue +}; + +/*{w:8,h:8,brev:1,count:256}*/ +const char FONT[256][8] = { +{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x38,0x44,0x6c,0x44,0x54,0x44,0x38,0x00 }, { 0x38,0x7c,0x54,0x7c,0x44,0x7c,0x38,0x00 }, { 0x00,0x28,0x7c,0x7c,0x7c,0x38,0x10,0x00 }, { 0x00,0x10,0x38,0x7c,0x7c,0x38,0x10,0x00 }, { 0x10,0x38,0x38,0x10,0x7c,0x7c,0x10,0x00 }, { 0x00,0x10,0x38,0x7c,0x7c,0x10,0x38,0x00 }, { 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00 }, { 0xfc,0xfc,0xfc,0xcc,0xcc,0xfc,0xfc,0xfc }, { 0x00,0x00,0x78,0x48,0x48,0x78,0x00,0x00 }, { 0xfc,0xfc,0x84,0xb4,0xb4,0x84,0xfc,0xfc }, { 0x00,0x1c,0x0c,0x34,0x48,0x48,0x30,0x00 }, { 0x38,0x44,0x44,0x38,0x10,0x38,0x10,0x00 }, { 0x10,0x18,0x14,0x10,0x30,0x70,0x60,0x00 }, { 0x0c,0x34,0x2c,0x34,0x2c,0x6c,0x60,0x00 }, { 0x00,0x54,0x38,0x6c,0x38,0x54,0x00,0x00 }, { 0x20,0x30,0x38,0x3c,0x38,0x30,0x20,0x00 }, { 0x08,0x18,0x38,0x78,0x38,0x18,0x08,0x00 }, { 0x10,0x38,0x7c,0x10,0x7c,0x38,0x10,0x00 }, { 0x28,0x28,0x28,0x28,0x28,0x00,0x28,0x00 }, { 0x3c,0x54,0x54,0x34,0x14,0x14,0x14,0x00 }, { 0x38,0x44,0x30,0x28,0x18,0x44,0x38,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x78,0x78,0x00 }, { 0x10,0x38,0x7c,0x10,0x7c,0x38,0x10,0x38 }, { 0x10,0x38,0x7c,0x10,0x10,0x10,0x10,0x00 }, { 0x10,0x10,0x10,0x10,0x7c,0x38,0x10,0x00 }, { 0x00,0x10,0x18,0x7c,0x18,0x10,0x00,0x00 }, { 0x00,0x10,0x30,0x7c,0x30,0x10,0x00,0x00 }, { 0x00,0x00,0x00,0x40,0x40,0x40,0x7c,0x00 }, { 0x00,0x28,0x28,0x7c,0x28,0x28,0x00,0x00 }, { 0x10,0x10,0x38,0x38,0x7c,0x7c,0x00,0x00 }, { 0x7c,0x7c,0x38,0x38,0x10,0x10,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x10,0x38,0x38,0x10,0x10,0x00,0x10,0x00 }, { 0x6c,0x6c,0x48,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x28,0x7c,0x28,0x28,0x7c,0x28,0x00 }, { 0x20,0x38,0x40,0x30,0x08,0x70,0x10,0x00 }, { 0x64,0x64,0x08,0x10,0x20,0x4c,0x4c,0x00 }, { 0x20,0x50,0x50,0x20,0x54,0x48,0x34,0x00 }, { 0x30,0x30,0x20,0x00,0x00,0x00,0x00,0x00 }, { 0x10,0x20,0x20,0x20,0x20,0x20,0x10,0x00 }, { 0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x00 }, { 0x00,0x28,0x38,0x7c,0x38,0x28,0x00,0x00 }, { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x20 }, { 0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00 }, { 0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00 }, { 0x38,0x44,0x4c,0x54,0x64,0x44,0x38,0x00 }, { 0x10,0x30,0x10,0x10,0x10,0x10,0x38,0x00 }, { 0x38,0x44,0x04,0x18,0x20,0x40,0x7c,0x00 }, { 0x38,0x44,0x04,0x38,0x04,0x44,0x38,0x00 }, { 0x08,0x18,0x28,0x48,0x7c,0x08,0x08,0x00 }, { 0x7c,0x40,0x40,0x78,0x04,0x44,0x38,0x00 }, { 0x18,0x20,0x40,0x78,0x44,0x44,0x38,0x00 }, { 0x7c,0x04,0x08,0x10,0x20,0x20,0x20,0x00 }, { 0x38,0x44,0x44,0x38,0x44,0x44,0x38,0x00 }, { 0x38,0x44,0x44,0x3c,0x04,0x08,0x30,0x00 }, { 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x00 }, { 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x20 }, { 0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00 }, { 0x00,0x00,0x7c,0x00,0x00,0x7c,0x00,0x00 }, { 0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x00 }, { 0x38,0x44,0x04,0x18,0x10,0x00,0x10,0x00 }, { 0x38,0x44,0x5c,0x54,0x5c,0x40,0x38,0x00 }, { 0x38,0x44,0x44,0x44,0x7c,0x44,0x44,0x00 }, { 0x78,0x44,0x44,0x78,0x44,0x44,0x78,0x00 }, { 0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00 }, { 0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00 }, { 0x7c,0x40,0x40,0x78,0x40,0x40,0x7c,0x00 }, { 0x7c,0x40,0x40,0x78,0x40,0x40,0x40,0x00 }, { 0x38,0x44,0x40,0x5c,0x44,0x44,0x3c,0x00 }, { 0x44,0x44,0x44,0x7c,0x44,0x44,0x44,0x00 }, { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00 }, { 0x04,0x04,0x04,0x04,0x44,0x44,0x38,0x00 }, { 0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00 }, { 0x40,0x40,0x40,0x40,0x40,0x40,0x7c,0x00 }, { 0x44,0x6c,0x54,0x44,0x44,0x44,0x44,0x00 }, { 0x44,0x64,0x54,0x4c,0x44,0x44,0x44,0x00 }, { 0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00 }, { 0x78,0x44,0x44,0x78,0x40,0x40,0x40,0x00 }, { 0x38,0x44,0x44,0x44,0x54,0x48,0x34,0x00 }, { 0x78,0x44,0x44,0x78,0x48,0x44,0x44,0x00 }, { 0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00 }, { 0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x00 }, { 0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00 }, { 0x44,0x44,0x44,0x44,0x44,0x28,0x10,0x00 }, { 0x44,0x44,0x54,0x54,0x54,0x54,0x28,0x00 }, { 0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00 }, { 0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x00 }, { 0x78,0x08,0x10,0x20,0x40,0x40,0x78,0x00 }, { 0x38,0x20,0x20,0x20,0x20,0x20,0x38,0x00 }, { 0x00,0x40,0x20,0x10,0x08,0x04,0x00,0x00 }, { 0x38,0x08,0x08,0x08,0x08,0x08,0x38,0x00 }, { 0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc }, { 0x30,0x30,0x10,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x38,0x04,0x3c,0x44,0x3c,0x00 }, { 0x40,0x40,0x78,0x44,0x44,0x44,0x78,0x00 }, { 0x00,0x00,0x38,0x44,0x40,0x44,0x38,0x00 }, { 0x04,0x04,0x3c,0x44,0x44,0x44,0x3c,0x00 }, { 0x00,0x00,0x38,0x44,0x78,0x40,0x38,0x00 }, { 0x18,0x20,0x20,0x78,0x20,0x20,0x20,0x00 }, { 0x00,0x00,0x3c,0x44,0x44,0x3c,0x04,0x38 }, { 0x40,0x40,0x70,0x48,0x48,0x48,0x48,0x00 }, { 0x10,0x00,0x10,0x10,0x10,0x10,0x18,0x00 }, { 0x08,0x00,0x18,0x08,0x08,0x08,0x48,0x30 }, { 0x40,0x40,0x48,0x50,0x60,0x50,0x48,0x00 }, { 0x10,0x10,0x10,0x10,0x10,0x10,0x18,0x00 }, { 0x00,0x00,0x68,0x54,0x54,0x44,0x44,0x00 }, { 0x00,0x00,0x70,0x48,0x48,0x48,0x48,0x00 }, { 0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x00 }, { 0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x40 }, { 0x00,0x00,0x3c,0x44,0x44,0x44,0x3c,0x04 }, { 0x00,0x00,0x58,0x24,0x20,0x20,0x70,0x00 }, { 0x00,0x00,0x38,0x40,0x38,0x04,0x38,0x00 }, { 0x00,0x20,0x78,0x20,0x20,0x28,0x10,0x00 }, { 0x00,0x00,0x48,0x48,0x48,0x58,0x28,0x00 }, { 0x00,0x00,0x44,0x44,0x44,0x28,0x10,0x00 }, { 0x00,0x00,0x44,0x44,0x54,0x7c,0x28,0x00 }, { 0x00,0x00,0x48,0x48,0x30,0x48,0x48,0x00 }, { 0x00,0x00,0x48,0x48,0x48,0x38,0x10,0x60 }, { 0x00,0x00,0x78,0x08,0x30,0x40,0x78,0x00 }, { 0x18,0x20,0x20,0x60,0x20,0x20,0x18,0x00 }, { 0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00 }, { 0x30,0x08,0x08,0x0c,0x08,0x08,0x30,0x00 }, { 0x28,0x50,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x10,0x38,0x6c,0x44,0x44,0x7c,0x00,0x00 }, { 0x38,0x44,0x40,0x40,0x44,0x38,0x10,0x30 }, { 0x48,0x00,0x48,0x48,0x48,0x58,0x28,0x00 }, { 0x0c,0x00,0x38,0x44,0x78,0x40,0x38,0x00 }, { 0x38,0x00,0x38,0x04,0x3c,0x44,0x3c,0x00 }, { 0x28,0x00,0x38,0x04,0x3c,0x44,0x3c,0x00 }, { 0x30,0x00,0x38,0x04,0x3c,0x44,0x3c,0x00 }, { 0x38,0x28,0x38,0x04,0x3c,0x44,0x3c,0x00 }, { 0x00,0x38,0x44,0x40,0x44,0x38,0x10,0x30 }, { 0x38,0x00,0x38,0x44,0x78,0x40,0x38,0x00 }, { 0x28,0x00,0x38,0x44,0x78,0x40,0x38,0x00 }, { 0x30,0x00,0x38,0x44,0x78,0x40,0x38,0x00 }, { 0x28,0x00,0x10,0x10,0x10,0x10,0x18,0x00 }, { 0x10,0x28,0x00,0x10,0x10,0x10,0x18,0x00 }, { 0x20,0x00,0x10,0x10,0x10,0x10,0x18,0x00 }, { 0x28,0x00,0x10,0x28,0x44,0x7c,0x44,0x00 }, { 0x38,0x28,0x38,0x6c,0x44,0x7c,0x44,0x00 }, { 0x0c,0x00,0x7c,0x40,0x78,0x40,0x7c,0x00 }, { 0x00,0x00,0x78,0x14,0x7c,0x50,0x3c,0x00 }, { 0x3c,0x50,0x50,0x7c,0x50,0x50,0x5c,0x00 }, { 0x38,0x00,0x30,0x48,0x48,0x48,0x30,0x00 }, { 0x28,0x00,0x30,0x48,0x48,0x48,0x30,0x00 }, { 0x60,0x00,0x30,0x48,0x48,0x48,0x30,0x00 }, { 0x38,0x00,0x48,0x48,0x48,0x58,0x28,0x00 }, { 0x60,0x00,0x48,0x48,0x48,0x58,0x28,0x00 }, { 0x28,0x00,0x48,0x48,0x48,0x38,0x10,0x60 }, { 0x48,0x30,0x48,0x48,0x48,0x48,0x30,0x00 }, { 0x28,0x00,0x48,0x48,0x48,0x48,0x30,0x00 }, { 0x00,0x10,0x38,0x40,0x40,0x38,0x10,0x00 }, { 0x18,0x24,0x20,0x78,0x20,0x24,0x5c,0x00 }, { 0x44,0x28,0x10,0x7c,0x10,0x7c,0x10,0x00 }, { 0x60,0x50,0x50,0x68,0x5c,0x48,0x48,0x00 }, { 0x08,0x14,0x10,0x38,0x10,0x10,0x50,0x20 }, { 0x18,0x00,0x38,0x04,0x3c,0x44,0x3c,0x00 }, { 0x18,0x00,0x10,0x10,0x10,0x10,0x18,0x00 }, { 0x18,0x00,0x30,0x48,0x48,0x48,0x30,0x00 }, { 0x18,0x00,0x48,0x48,0x48,0x58,0x28,0x00 }, { 0x28,0x50,0x00,0x70,0x48,0x48,0x48,0x00 }, { 0x28,0x50,0x00,0x48,0x68,0x58,0x48,0x00 }, { 0x38,0x04,0x3c,0x44,0x3c,0x00,0x3c,0x00 }, { 0x30,0x48,0x48,0x48,0x30,0x00,0x78,0x00 }, { 0x10,0x00,0x10,0x30,0x40,0x44,0x38,0x00 }, { 0x00,0x00,0x7c,0x40,0x40,0x40,0x00,0x00 }, { 0x00,0x00,0xfc,0x04,0x04,0x00,0x00,0x00 }, { 0x40,0x48,0x50,0x38,0x44,0x08,0x1c,0x00 }, { 0x40,0x48,0x50,0x2c,0x54,0x1c,0x04,0x00 }, { 0x10,0x00,0x10,0x10,0x38,0x38,0x10,0x00 }, { 0x00,0x00,0x24,0x48,0x24,0x00,0x00,0x00 }, { 0x00,0x00,0x48,0x24,0x48,0x00,0x00,0x00 }, { 0x54,0x00,0xa8,0x00,0x54,0x00,0xa8,0x00 }, { 0x54,0xa8,0x54,0xa8,0x54,0xa8,0x54,0xa8 }, { 0xa8,0xfc,0x54,0xfc,0xa8,0xfc,0x54,0xfc }, { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 }, { 0x10,0x10,0x10,0xf0,0x10,0x10,0x10,0x10 }, { 0x10,0xf0,0x10,0xf0,0x10,0x10,0x10,0x10 }, { 0x50,0x50,0x50,0xd0,0x50,0x50,0x50,0x50 }, { 0x00,0x00,0x00,0xf0,0x50,0x50,0x50,0x50 }, { 0x00,0xf0,0x10,0xf0,0x10,0x10,0x10,0x10 }, { 0x50,0xd0,0x10,0xd0,0x50,0x50,0x50,0x50 }, { 0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50 }, { 0x00,0xf0,0x10,0xd0,0x50,0x50,0x50,0x50 }, { 0x50,0xd0,0x10,0xf0,0x00,0x00,0x00,0x00 }, { 0x50,0x50,0x50,0xf0,0x00,0x00,0x00,0x00 }, { 0x10,0xf0,0x10,0xf0,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0xf0,0x10,0x10,0x10,0x10 }, { 0x10,0x10,0x10,0x1c,0x00,0x00,0x00,0x00 }, { 0x10,0x10,0x10,0xfc,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0xfc,0x10,0x10,0x10,0x10 }, { 0x10,0x10,0x10,0x1c,0x10,0x10,0x10,0x10 }, { 0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00 }, { 0x10,0x10,0x10,0xfc,0x10,0x10,0x10,0x10 }, { 0x10,0x1c,0x10,0x1c,0x10,0x10,0x10,0x10 }, { 0x50,0x50,0x50,0x5c,0x50,0x50,0x50,0x50 }, { 0x50,0x5c,0x40,0x7c,0x00,0x00,0x00,0x00 }, { 0x00,0x7c,0x40,0x5c,0x50,0x50,0x50,0x50 }, { 0x50,0xdc,0x00,0xfc,0x00,0x00,0x00,0x00 }, { 0x00,0xfc,0x00,0xdc,0x50,0x50,0x50,0x50 }, { 0x50,0x5c,0x40,0x5c,0x50,0x50,0x50,0x50 }, { 0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00 }, { 0x50,0xdc,0x00,0xdc,0x50,0x50,0x50,0x50 }, { 0x10,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00 }, { 0x50,0x50,0x50,0xfc,0x00,0x00,0x00,0x00 }, { 0x00,0xfc,0x00,0xfc,0x10,0x10,0x10,0x10 }, { 0x00,0x00,0x00,0xfc,0x50,0x50,0x50,0x50 }, { 0x50,0x50,0x50,0x7c,0x00,0x00,0x00,0x00 }, { 0x10,0x1c,0x10,0x1c,0x00,0x00,0x00,0x00 }, { 0x00,0x1c,0x10,0x1c,0x10,0x10,0x10,0x10 }, { 0x00,0x00,0x00,0x7c,0x50,0x50,0x50,0x50 }, { 0x50,0x50,0x50,0xdc,0x50,0x50,0x50,0x50 }, { 0x10,0xfc,0x00,0xfc,0x10,0x10,0x10,0x10 }, { 0x10,0x10,0x10,0xf0,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x1c,0x10,0x10,0x10,0x10 }, { 0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc }, { 0x00,0x00,0x00,0x00,0xfc,0xfc,0xfc,0xfc }, { 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0 }, { 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c }, { 0xfc,0xfc,0xfc,0xfc,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x34,0x48,0x48,0x34,0x00,0x00 }, { 0x00,0x70,0x48,0x70,0x48,0x48,0x70,0x40 }, { 0x78,0x48,0x40,0x40,0x40,0x40,0x40,0x00 }, { 0x00,0x7c,0x28,0x28,0x28,0x28,0x28,0x00 }, { 0x78,0x48,0x20,0x10,0x20,0x48,0x78,0x00 }, { 0x00,0x00,0x3c,0x48,0x48,0x30,0x00,0x00 }, { 0x00,0x00,0x48,0x48,0x48,0x70,0x40,0x40 }, { 0x00,0x00,0x28,0x50,0x10,0x10,0x10,0x00 }, { 0x38,0x10,0x38,0x44,0x38,0x10,0x38,0x00 }, { 0x30,0x48,0x48,0x78,0x48,0x48,0x30,0x00 }, { 0x00,0x38,0x44,0x44,0x28,0x28,0x6c,0x00 }, { 0x30,0x40,0x20,0x10,0x38,0x48,0x30,0x00 }, { 0x00,0x00,0x28,0x54,0x54,0x28,0x00,0x00 }, { 0x00,0x10,0x38,0x54,0x54,0x38,0x10,0x00 }, { 0x00,0x38,0x40,0x78,0x40,0x38,0x00,0x00 }, { 0x00,0x30,0x48,0x48,0x48,0x48,0x00,0x00 }, { 0x00,0x78,0x00,0x78,0x00,0x78,0x00,0x00 }, { 0x00,0x10,0x38,0x10,0x00,0x38,0x00,0x00 }, { 0x40,0x30,0x08,0x30,0x40,0x00,0x78,0x00 }, { 0x08,0x30,0x40,0x30,0x08,0x00,0x78,0x00 }, { 0x00,0x08,0x14,0x10,0x10,0x10,0x10,0x10 }, { 0x10,0x10,0x10,0x10,0x10,0x50,0x20,0x00 }, { 0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00 }, { 0x00,0x28,0x50,0x00,0x28,0x50,0x00,0x00 }, { 0x30,0x48,0x48,0x30,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00 }, { 0x00,0x1c,0x10,0x10,0x50,0x50,0x20,0x00 }, { 0x50,0x28,0x28,0x28,0x00,0x00,0x00,0x00 }, { 0x60,0x10,0x20,0x70,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x78,0x78,0x78,0x78,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, +}; + +#pragma rodata-name (push, "SPRITES") +const char SPRITES[2][16*2] = { + /*{w:16,h:16,brev:1,remap:[4,0,1,2,3,5,6,7,8,9],count:2}*/ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x3F, + 0x35, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x40, 0xFC, + 0x54, 0xAC, 0x04, 0x04, 0x04, 0x04, 0x04, 0xFC, + },{ + 0x00, 0x19, 0x1F, 0x0A, 0x05, 0x07, 0x0E, 0x1C, + 0x3A, 0x2A, 0x3C, 0x2E, 0x1E, 0x18, 0x0E, 0x07, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0x38, + 0xFC, 0xF4, 0x7C, 0xB4, 0xB8, 0x68, 0xF0, 0xE0, + } +}; +#pragma rodata-name (pop) + +void main() { + int i; + + // copy tile bitmaps + memcpy((char*)0x6800, FONT, sizeof(FONT)); + // set up screen + for (i=0; i<32*32; i++) { + POKE(0x4000+i, i); + } + memcpy((char*)0x5210, PALETTE, 3); + + // setup interrupt handler + irq_setup(); + + while (1) { + // move sprites + POKE(0x5000, 16); // sprite 0 x + POKE(0x5040, 16); // sprite 0 y + POKE(0x5080, framecount); // sprite 0 x + POKE(0x50C0, 16); // sprite 1 y + POKE(0x5100, 0x01); + } +} diff --git a/src/machine/exidy.ts b/src/machine/exidy.ts new file mode 100644 index 00000000..ecce64f5 --- /dev/null +++ b/src/machine/exidy.ts @@ -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 +*/ diff --git a/src/platform/_index.ts b/src/platform/_index.ts index ccea0030..29a805ca 100644 --- a/src/platform/_index.ts +++ b/src/platform/_index.ts @@ -12,6 +12,7 @@ export function importPlatform(name: string) : Promise { 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"); diff --git a/src/platform/exidy.ts b/src/platform/exidy.ts new file mode 100644 index 00000000..40dd8a46 --- /dev/null +++ b/src/platform/exidy.ts @@ -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 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; + diff --git a/src/worker/lib/exidy/crt0.o b/src/worker/lib/exidy/crt0.o new file mode 100644 index 00000000..e98448f4 Binary files /dev/null and b/src/worker/lib/exidy/crt0.o differ diff --git a/src/worker/lib/exidy/crt0.s b/src/worker/lib/exidy/crt0.s new file mode 100644 index 00000000..4164ef7f --- /dev/null +++ b/src/worker/lib/exidy/crt0.s @@ -0,0 +1,96 @@ +; Startup code for cc65 and Shiru's NES library +; based on code by Groepaz/Hitmen , Ullrich von Bassewitz +; 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 + diff --git a/src/worker/lib/exidy/exidy.cfg b/src/worker/lib/exidy/exidy.cfg new file mode 100644 index 00000000..4c64a083 --- /dev/null +++ b/src/worker/lib/exidy/exidy.cfg @@ -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__; +} diff --git a/src/worker/platforms.ts b/src/worker/platforms.ts index 0c53adc4..52e2f51a 100644 --- a/src/worker/platforms.ts +++ b/src/worker/platforms.ts @@ -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']; diff --git a/src/worker/wasmutils.ts b/src/worker/wasmutils.ts index 1d76598b..ef9bbf6b 100644 --- a/src/worker/wasmutils.ts +++ b/src/worker/wasmutils.ts @@ -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, { diff --git a/src/worker/workertools.ts b/src/worker/workertools.ts index 4040e470..a105cdf5 100644 --- a/src/worker/workertools.ts +++ b/src/worker/workertools.ts @@ -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',