//
-// Thunder: A Rolling Thunder Emulator w/6809 debugger
+// Thunder: A Rolling Thunder Emulator
//
// by James Hammons
// (C) 2004, 2014 Underground Software
#define THUNDER_VERSION "1.1.0"
+#include <SDL2/SDL.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
-//#include <curses.h> // For getch()
#include <time.h>
-#include <SDL2/SDL.h>
-#include "v6809.h"
-#include "screen.h"
#include "gui.h"
#include "log.h"
+#include "screen.h"
+#include "sound.h"
+#include "v63701.h"
+#include "v6809.h"
#include "video.h"
using namespace std;
uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
uint8_t chr_rom[0x60000]; // Character ROM pointer
+uint8_t mcuMem[0x10000]; // 64K for MCU
V6809REGS cpu1, cpu2;
+V63701REGS mcu;
bool trace1 = false; // ditto...
bool looking_at_rom = true; // true = R1, false = R2
uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
uint8_t * psg_adrs[16];
-uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-uint8_t * voc_adrs[32];
+//uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+//uint8_t * voc_adrs[32];
uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
uint8_t * fm_adrs[14];
if (addr < 0x8000)
{
+ // Memory shared with MCU (CPU #1 only! CPU #2 does not)
+ if ((addr >= 0x4000) && (addr <= 0x43FF))
+ return mcuMem[addr - 0x3000];
+
if (addr > 0x5FFF)
b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
else
banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
if (addr > 0x4284 && addr < 0x42A5 && b)
SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
+#if 0
if (addr == 0x4380)
{
SpawnSound(FMSOUND, b); // Do FM sound on channel 4
if (b == 12)
game_over_switch = 240; // Set game over delay...
}
- if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
+#endif
+// if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
+
+ // Memory shared with MCU (CPU #1 only! CPU #2 does not)
+ if ((addr >= 0x4000) && (addr <= 0x43FF))
+ mcuMem[addr - 0x3000] = b;
+ else
gram1[addr] = b;
+
if (addr == 0x8800)
charbase = false; // Char banksw1
if (addr == 0x8C00)
if (addr > 0x1FFF && addr < 0x6000)
b = gram1[addr - 0x2000];
if (addr > 0x5FFF)
- b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
+ b = grom3[banksw2 + (addr - 0x6000)];
}
else
b = grom2[addr];
printf("WriteMem: CPU #2 writing $%02X to $%04X...\n", b, addr);
#endif
+#if 0
if (addr == 0x6000)
SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
if (addr == 0x6400)
SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
if (addr > 0x0284 && addr < 0x02A5 && b)
SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
+#endif
if (addr == 0xD803)
banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
+#if 0
if (addr == 0x0380)
{
SpawnSound(FMSOUND, b); // Do FM sound on chan 4
if (b == 12)
game_over_switch = 240; // Set game over delay...
}
- if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
+#endif
+// if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
{
if (addr < 0x2000)
gram1[addr + 0x4000] = b;
if (addr > 0x1FFF && addr < 0x6000)
gram1[addr - 0x2000] = b;
- if (addr > 0x5FFF)
- gram1[addr] = b;
+// if (addr > 0x5FFF)
+// gram1[addr] = b;
}
if (addr == 0x8800)
{
}
+uint8_t MCUReadMemory(uint16_t address)
+{
+#if 1
+ if (address < 0x20)
+ {
+// printf("V63701 read $%02X from $%02X...\n", memory[address], address);
+ return InternalRegisterRead(address);
+ }
+#endif
+
+#if 0
+ // Translate local reads @ $1000-$13FF to $4000-$43FF in shared RAM
+ if ((address >= 0x1000) && (address <= 0x13FF))
+ return gram1[0x3000 + address];
+ else
+#endif
+ if ((address >= 0x2000) && (address <= 0x2001))
+ {
+ return 0; //for now
+// return YMReadReg(0);
+ }
+// else if (address == 0x2020)
+// return input_port_0_r(0);
+// else if (address == 0x2021)
+// return input_port_1_r(0);
+ // This is DSW1 & 2. All switch settings are active low.
+ else if (address == 0x2030)
+ return 0xFF;
+ else if (address == 0x2031)
+ return 0xFF;
+
+ return mcuMem[address];
+}
+
+
+void MCUWriteMemory(uint16_t address, uint8_t data)
+{
+ static uint8_t ymRegister;
+
+#if 1
+ if (address < 0x20)
+ {
+// printf("V63701 wrote $%02X to $%02X...\n", data, address);
+ InternalRegisterWrite(address, data);
+ return;
+ }
+#endif
+
+#if 0
+ // Translate local reads @ $1000-$13FF to $4000-$43FF in shared RAM
+ if ((address >= 0x1000) && (address <= 0x13FF))
+ {
+ gram1[0x3000 + address] = data;
+ return;
+ }
+#endif
+
+ if (((address >= 0x4000) && (address <= 0xBFFF))
+ || (address >= 0xF000))
+ return;
+ else if (address == 0x2000)
+ {
+ ymRegister = data;
+ return;
+ }
+ else if (address == 0x2001)
+ {
+//printf("Writing $%02X to YM2151 register $%02X...\n", data, ymRegister);
+// YMWriteReg(0, ymRegister, data);
+ return;
+ }
+
+ // RAM is from $0 - $3FFF, $C000 - $EFFF
+ mcuMem[address] = data;
+}
+
+
//
// Generic Load file into image space
// (No error checking performed! Responsibility of caller!)
for(int i=0; i<256; i++)
{
char c1, c2;
- uint8_t r, g, b;
ff1.get(c1);
ff2.get(c2);
- r = (uint8_t)c1 & 0x0F;
- g = (uint8_t)c1 >> 4;
- b = (uint8_t)c2;
+ uint8_t r = (uint8_t)c1 & 0x0F;
+ uint8_t g = (uint8_t)c1 >> 4;
+ uint8_t b = (uint8_t)c2;
palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
}
}
// PROM5 has the following in it (tile address decoder):
- // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
- // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
+ // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
+ // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
return ff1;
}
file.get(ch); len |= (int)(uint8_t)ch << 16;
file.get(ch); len |= (int)(uint8_t)ch << 24;
- file.ignore(len + 4); // Skip intermediate data
+ // Skip intermediate data
+ file.ignore(len + 4);
}
- file.get(ch); len = (int)(uint8_t)ch; // & finally get length of data
+ // & finally get length of data
+ file.get(ch); len = (int)(uint8_t)ch;
file.get(ch); len |= (int)(uint8_t)ch << 8;
file.get(ch); len |= (int)(uint8_t)ch << 16;
file.get(ch); len |= (int)(uint8_t)ch << 24;
if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
{ cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
- if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
- { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
+// if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
+// { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
{ cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
return -1;
}
+ // Load MCU program + data
+ if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000)) // Load MCU ROM
+ { cout << "Could not open file '" << MCUROM << "'!" << endl; return -1; }
+
+ if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000)) // Load 4th ROM
+ { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
+
// Load samples if they're there...
LoadPSGs();
LoadFMs();
cpu2.WrMem = WrMemB;
cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
+ memset(&mcu, 0, sizeof(V63701REGS));
+ mcu.RdMem = MCUReadMemory;
+ mcu.WrMem = MCUWriteMemory;
+ mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
+
uint32_t my_clock = 0;
running = true; // Set running status...
trace1 = false;
SetRefreshRate(refresh2); // Tell GUI our refresh rate
-#if 1
+#if 0
// This is data that is supposed to come from the MCU... So that's why it hangs
gram1[0x4182] = 0xA6; // Temp kludge
gram1[0x4184] = 0xA6;
oldTicks = SDL_GetTicks();
WriteLog("About to set up audio...\n");
-#if 1
+#if 0
// This crap SHOULD be in sound.cpp (not yet created)...
SDL_AudioSpec desired, obtained;
desired.freq = 22050;
}
SDL_PauseAudio(0); // Get that audio going!
+#else
+ InitSound();
#endif
memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
{
HandleGUIDebounce(); // Debounce GUI keys
+#if 0
if (game_over_switch)
{
game_over_switch--; // Countdown...
if (game_over_switch == 0)
gram1[0x4380] = 0; // Kill music!
}
+#endif
+
+// Dipswitches are presented to the main CPUs as 0 or 1 at locations
+// $423D - $425B by the MCU
//testing... (works)
//gram1[0x423D] = 1;
//gram1[0x423D] = self_test; // Reset DSW1-1
- gram1[0x4268] = 0; // Reset Video test
- gram1[0x427A] = 0; gram1[0x427C] = 0;
+// gram1[0x4268] = 0; // Reset Video test
+// gram1[0x427A] = 0; gram1[0x427C] = 0;
//gram1[0x427B] = 0; gram1[0x427D] = 0;
- gram1[0x427E] = 0;// gram1[0x427F] = 0;
- gram1[0x4280] = 0;// gram1[0x4281] = 0;
+// gram1[0x427E] = 0;// gram1[0x427F] = 0;
+// gram1[0x4280] = 0;// gram1[0x4281] = 0;
//gram1[0x4276] = 0;
- gram1[0x426A] = 0;
+// gram1[0x426A] = 0;
//gram1[0x4278] = 0;
- gram1[0x426C] = 0;
- gram1[0x4262] = 0; gram1[0x4260] = 0;
+// gram1[0x426C] = 0;
+// gram1[0x4262] = 0; gram1[0x4260] = 0;
//gram1[0x4247] = 0;
// SDL key handling...
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
running = false;
+ // Do PCX snapshot (F4)
+ else if (event.key.keysym.sym == SDLK_F4)
+ {
+// SpawnSound(USERSOUND, SCAMERA);
+ SavePCXSnapshot();
+// debounce = 10;
+ }
else if (event.key.keysym.sym == SDLK_F10)
gram1[0x41A5]++; // Coin? (F10)
else if (event.key.keysym.sym == SDLK_c)
// There's better ways, such as keeping track of when slave writes to master, etc...
Execute6809(&cpu1, 40);
Execute6809(&cpu2, 40);
+
+ // MCU runs at 1,536,000 Hz
+ // 1536000 / 60 / 640 == 40
+ Execute63701(&mcu, 40);
}
} // END: enable_cpu