2 // Thunder: A Rolling Thunder Emulator w/6809 debugger
5 // (c) 2004, 2009 Underground Software
7 // JLH = James L. Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 07/23/2009 Added changelog ;-)
14 #define THUNDER_VERSION "0.9.9"
23 //#include <conio.h> // For getch()
24 #include <curses.h> // For getch()
26 #include "SDL.h" // Get yer SDL out...!
33 using namespace std; // Yes!
36 #define ROM1 "RT3-1B.ROM"
37 #define ROM2 "RT3-2B.ROM"
38 #define ROM3 "RT3-3.ROM"
39 #define ROM4 "RT3-4.ROM"
40 #define ROM5 "RT1-5.ROM"
41 #define ROM6 "RT1-6.ROM"
42 #define ROM7 "RT1-7.ROM"
43 #define ROM8 "RT1-8.ROM"
44 #define ROM9 "RT1-9.ROM"
45 #define ROM10 "RT1-10.ROM"
46 #define ROM11 "RT1-11.ROM"
47 #define ROM12 "RT1-12.ROM"
48 #define ROM13 "RT1-13.ROM"
49 #define ROM14 "RT1-14.ROM"
50 #define ROM15 "RT1-15.ROM"
51 #define ROM16 "RT1-16.ROM"
52 #define ROM17 "RT1-17.ROM"
53 #define ROM18 "RT1-18.ROM"
54 #define ROM19 "RT3-19.ROM"
55 #define ROM20 "RT3-20.ROM"
56 #define ROM21 "RT1-21.ROM"
57 #define ROM22 "RT2-22.ROM"
58 #define PROM1 "RT1-1.BIN"
59 #define PROM2 "RT1-2.BIN"
60 #define PROM3 "RT1-3.BIN"
61 #define PROM4 "RT1-4.BIN"
62 #define PROM5 "RT1-5.BIN"
64 #define ROM1 "rt3-1b.9c"
65 #define ROM2 "rt3-2b.12c"
66 #define ROM3 "rt3-3.12d"
67 #define ROM4 "rt1-4.6b"
68 #define ROM5 "rt1-5.4r"
69 #define ROM6 "rt1-6.4s"
70 #define ROM7 "rt1-7.7r"
71 #define ROM8 "rt1-8.7s"
72 #define ROM9 "rt1-9.12h"
73 #define ROM10 "rt1-10.12k"
74 #define ROM11 "rt1-11.12l"
75 #define ROM12 "rt1-12.12m"
76 #define ROM13 "rt1-13.12p"
77 #define ROM14 "rt1-14.12r"
78 #define ROM15 "rt1-15.12t"
79 #define ROM16 "rt1-16.12u"
80 #define ROM17 "rt1-17.f1"
81 #define ROM18 "rt1-18.h1"
82 #define ROM19 "rt1-19.k1"
83 #define ROM20 "rt1-20.m1"
84 #define ROM21 "rt1-21.f3"
85 #define ROM22 "rt1-22.h3"
86 #define PROM1 "mb7124e.3r"
87 #define PROM2 "mb7116e.3s"
88 #define PROM3 "mb7138h.4v"
89 #define PROM4 "mb7138h.6v"
90 #define PROM5 "mb7112e.6u"
91 #define MCUROM "rt1-mcu.bin"
98 uint8 * gram, * grom; // Allocate RAM & ROM pointers
99 uint8 gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
100 uint8 grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
101 uint8 chr_rom[0x60000]; // Character ROM pointer
103 V6809REGS cpu1, cpu2;
105 bool trace1 = false; // ditto...
106 bool looking_at_rom = true; // true = R1, false = R2
107 uint32 banksw1, banksw2; // Bank switch addresses
108 uint16 game_over_switch; // Game over delay
109 uint16 dpc; // Debug pc reg...
110 bool show_scr = true; // Whether or not to show background
111 bool enable_cpu = true; // Whether or not to enable CPUs
112 bool irqGoA = true; // IRQ switch for CPU #1
113 bool irqGoB = true; // IRQ switch for CPU #2
115 uint16 refresh_ = 0; // Crappy global screen stuff...
116 bool refresh2 = true;
118 uint32 psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
119 uint8 * psg_adrs[16];
120 uint32 voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
122 uint8 * voc_adrs[32];
123 uint32 fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
126 fstream tr; // Tracelog hook
127 uint16 pcx; // Where we at?
129 static uint8 * keys; // SDL raw keyboard matrix
131 static char op_mat1[256] = {
132 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
133 0, 0, 5, 5, 0, 0, 4, 4, 0, 5, 8, 0, 8, 5, 6, 6,
134 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
135 7, 7, 7, 7, 6, 6, 6, 6, 0, 5, 5, 5, 8, 5, 5, 5,
136 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
137 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
138 7, 0, 0, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7,
139 2, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,
140 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 3, 9, 0,
141 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
142 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
143 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
144 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 0, 9, 0,
145 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
146 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
147 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0,
158 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
159 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7,
160 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2,
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7,
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
166 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
175 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
176 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
177 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
182 static char mnemonics[256][6] = {
183 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
184 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
185 "PAGE1","PAGE2","NOP ","SYNC ","??? ","??? ","LBRA ","LBSR ",
186 "??? ","DAA ","ORCC ","??? ","ANDCC","SEX ","EXG ","TFR ",
187 "BRA ","BRN ","BHI ","BLS ","BHS ","BLO ","BNE ","BEQ ",
188 "BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
189 "LEAX ","LEAY ","LEAS ","LEAU ","PSHS ","PULS ","PSHU ","PULU ",
190 "??? ","RTS ","ABX ","RTI ","CWAI ","MUL ","RESET","SWI ",
191 "NEGA ","??? ","??? ","COMA ","LSRA ","??? ","RORA ","ASRA ",
192 "LSLA ","ROLA ","DECA ","??? ","INCA ","TSTA ","??? ","CLRA ",
193 "NEGB ","??? ","??? ","COMB ","LSRB ","??? ","RORB ","ASRB ",
194 "LSLB ","ROLB ","DECB ","??? ","INCB ","TSTB ","??? ","CLRB ",
195 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
196 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
197 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
198 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
199 "SUBA ","CMPA ","SCBA ","SUBD ","ANDA ","BITA ","LDA ","??? ",
200 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","BSR ","LDX ","??? ",
201 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
202 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
203 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
204 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
205 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
206 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
207 "SUBB ","CMPB ","SCBB ","ADDD ","ANDB ","BITB ","LDB ","??? ",
208 "EORB ","ADCB ","ORB ","ADDB ","LDD ","??? ","LDU ","??? ",
209 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
210 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
211 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
212 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
213 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
214 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU " },
215 mnemonics2[256][6] = {
216 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
217 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
218 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
219 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
220 "??? ","LBRN ","LBHI ","LBLS ","LBHS ","LBLO ","LBNE ","LBEQ ",
221 "LBVC ","LBVS ","LBPL ","LBMI ","LBGE ","LBLT ","LBGT ","LBLE ",
222 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
223 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI2 ",
224 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
225 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
226 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
227 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
228 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
229 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
230 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
231 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
232 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
233 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","??? ",
234 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
235 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
236 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
237 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
238 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
239 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
240 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
241 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","??? ",
242 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
243 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
244 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
245 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
246 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
247 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS " },
248 mnemonics3[256][6] = {
249 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
250 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
251 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
252 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
253 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
254 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
255 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
256 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI3 ",
257 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
258 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
259 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
260 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
261 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
262 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
263 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
264 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
265 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
266 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
267 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
268 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
269 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
270 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
271 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
272 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
273 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
274 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
275 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
276 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
277 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
278 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
279 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
280 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? " },
282 "D", "X", "Y", "U", "S", "PC", "??", "??",
283 "A", "B", "CC", "DP", "??", "??", "??", "??" },
284 iregs[4][2] = {"X", "Y", "U", "S" };
287 // Read a byte from memory (without touching PC. Not a Fetch!)
289 uint8 RdMem(uint16 addr)
293 // $4000-4300 is RAM shared with the microcontroller...
298 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
309 // Write a byte to memory
311 void WrMem(uint16 addr, uint8 b)
313 extern bool charbase; // Needed for screen. Extern it in it??
314 //extern uint16 sr, ur, xr, yr; // Needed for tracelog
316 /* if ((addr>0x40FF) && (addr<0x4390))
318 tr << hex << addr << ":" << (int)b;
319 //for(int i=0; i<32; i++)
321 // if (gram1[0x4400+i]<0x10) tr << "0";
322 // tr << hex << (uint16)gram1[0x4400+i] << " ";
329 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
334 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
336 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
338 banksw1 = (uint32)b << 13; // Set char data bankswitch base address
339 if (addr > 0x4284 && addr < 0x42A5 && b)
340 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
343 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
345 game_over_switch = 240; // Set game over delay...
347 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
350 charbase = false; // Char banksw1
352 charbase = true; // Char banksw2
353 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
355 if (refresh_++ == 1) // 30 Hz...
357 BlitChar(screen, chr_rom, gram1);
358 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
360 //Seems they're more regular than this...
361 // irqGoA = true; // Will this work??? no...
362 // cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok???
363 // IRQ Ack (may also be frame go...
364 // cpu1.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
365 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
370 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
372 uint8 RdMemB(uint16 addr)
374 // extern uint16 cpu2.s, cpu2.u, cpu2.x, cpu2.y; // Needed for tracelog
380 b = gram1[addr + 0x4000];
381 if (addr > 0x1FFF && addr < 0x6000)
382 b = gram1[addr - 0x2000];
384 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
389 /* if ((addr>0x3FFF) && (addr<0x4400)) tr << "R-" << hex << pcx << ": "
391 << (int)looking_at_rom
393 << "] XR:" << xr << " YR:" << yr
394 << " SR:" << sr << " UR:" << ur
400 // Write a byte to memory (2nd processor)
402 void WrMemB(uint16 addr, uint8 b)
404 extern bool charbase;
405 //extern uint16 sr, ur, xr, yr; // Needed for tracelog
407 /* if ((addr>0x00FF) && (addr<0x0390))
409 tr << hex << addr << ":" << (int)b;
410 //for(int i=0; i<32; i++)
412 // if (gram1[0x4400+i]<0x10) tr << "0";
413 // tr << hex << (uint16)gram1[0x4400+i] << " ";
420 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
424 // Bah. Dunno if this is accurate or not!
425 // if (addr == 0x8800)
426 // irqGoB = true; // Will it work??? no...
427 // cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok???
429 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
431 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
432 if (addr > 0x0284 && addr < 0x02A5 && b)
433 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
435 banksw2 = (uint32)(b & 0x03) << 13; // Set sprite data bank switch
438 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
440 game_over_switch = 240; // Set game over delay...
442 if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
445 gram1[addr + 0x4000] = b;
446 if (addr > 0x1FFF && addr < 0x6000)
447 gram1[addr - 0x2000] = b;
453 // IRQ Ack (may also be frame go...)
454 // cpu2.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
455 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
460 // Display bytes in mem in hex
462 void DisplayBytes(uint16 src, unsigned long dst)
467 WriteLog("%04X: ", src);
468 cnt = 0; // Init counter...
469 if (src > dst) dst += 0x10000; // That should fix the FFFF bug...
470 for(i=src; i<dst; i++)
472 WriteLog("%02X ", (uint8)(looking_at_rom ? RdMem(i) : RdMemB(i)));
473 cnt++; // Bump counter...
475 for(i=cnt; i<5; i++) // Pad the leftover spaces...
482 uint8 Fetch(void) { return RdMem(dpc); }
483 uint16 FetchW(void) { return (uint16)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
484 uint8 FetchB(void) { return RdMemB(dpc); }
485 uint16 FetchWB(void) { return (uint16)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
488 // Decode a 6809 instruction at 'addr'
492 uint8 (* DFetch)(); // Decode Fetch() pointer...
493 uint16 (* DFetchW)(); // Decode FetchW() pointer...
494 DFetch = (looking_at_rom ? Fetch : FetchB);
495 DFetchW = (looking_at_rom ? FetchW : FetchWB);
497 /* extern*/ uint16 pcr, pcrB; // Pull in 'pcr' from '6809.cpp'
498 uint16 pc_save = pcr, pcB_save = pcrB;
499 pcr = dpc; pcrB = dpc;
500 uint8 opcode = DFetch(); // Get the opcode ('fetch' cycle)
501 uint8 opcode2, operand;
503 uint8 admode = op_mat1[opcode]; // addressing mode
504 char outbuf[80], mnem[6], tmp[30];
506 strcpy(mnem, mnemonics[opcode]); // Copy page 1 opcode
507 if (opcode == 0x10) // Extended opcode?
509 opcode2 = DFetch(); // Then get next byte
510 admode = op_mat2[opcode2]; // And use it as index into 'op_mat2'
511 strcpy(mnem, mnemonics2[opcode2]); // Overwrite mnemonic
513 if (opcode == 0x11) // Same as above...
516 admode = op_mat3[opcode2];
517 strcpy(mnem, mnemonics3[opcode2]); // Overwrite mnemonic
519 switch(admode) // Decode it...
522 { sprintf(outbuf, "???"); break; }
524 { operand = DFetch(); // Get ZP address
525 sprintf(outbuf, "%s $%02X", mnem, operand);
528 { loperand = DFetchW(); // Get ABS address
529 sprintf(outbuf, "%s $%04X", mnem, loperand);
532 { operand = DFetch(); // Get offset
533 uint16 tmpc = (looking_at_rom ? pcr : pcrB);
534 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16)(int8)operand);
536 case 4: // Long Relative
537 { loperand = DFetchW(); // Get long offset
538 uint16 tmpc = (looking_at_rom ? pcr : pcrB);
539 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16)loperand);
542 { sprintf(outbuf, "%s ", mnem);
544 case 6: // Txfr/exchg/push/pull
545 { operand = DFetch(); // Get txfr/exg/push/pull byte
546 if ((opcode == 0x1E) || (opcode == 0x1F)) // Is it TXF/EXG?
548 sprintf(tmp, "%s,%s", tregs[operand>>4], tregs[operand&0x0F]);
553 if (operand&0x01) strcat(tmp, "CC ");
554 if (operand&0x02) strcat(tmp, "A ");
555 if (operand&0x04) strcat(tmp, "B ");
556 if (operand&0x08) strcat(tmp, "DP ");
557 if (operand&0x10) strcat(tmp, "X ");
558 if (operand&0x20) strcat(tmp, "Y ");
559 if (operand&0x40) (((opcode==0x34)||(opcode==0x35))
560 ? strcat(tmp, "U ") : strcat(tmp, "S "));
561 if (operand&0x80) strcat(tmp, "PC");
563 sprintf(outbuf, "%s %s", mnem, tmp);
565 case 7: // Indexed (the tough one!)
566 { operand = DFetch(); // Get IDX byte
567 uint8 reg = ((operand & 0x60) >> 5), idxind = ((operand & 0x10) >> 4),
568 lo_nyb = (operand & 0x0F), boff;
572 if (!(operand & 0x80)) // Hi bit set? Then decode 4 bit offset
574 sprintf(tmp, "(%d),%s", (idxind ? -(16-lo_nyb) : lo_nyb),
577 else // Add the ($nnnn,R) code dude...
583 case 1: sprintf(tmp, "(,%s++)", iregs[reg]); break;
584 case 3: sprintf(tmp, "(,--%s)", iregs[reg]); break;
585 case 4: sprintf(tmp, "(,%s)", iregs[reg]); break;
586 case 5: sprintf(tmp, "(B,%s)", iregs[reg]); break;
587 case 6: sprintf(tmp, "(A,%s)", iregs[reg]); break;
589 { boff = DFetch(); sprintf(tmp, "($%02X,%s)", boff,
590 iregs[reg]); break; }
592 { woff = DFetchW(); sprintf(tmp, "($%04X,%s)", woff,
593 iregs[reg]); break; }
594 case 11: sprintf(tmp, "(D,%s)", iregs[reg]); break;
596 { boff = DFetch(); sprintf(tmp, "($%02X,PC)", boff); break; }
598 { woff = DFetchW(); sprintf(tmp, "($%04X,PC)", woff); break; }
600 { woff = DFetchW(); sprintf(tmp, "[$%04X]", woff); break; }
601 default: strcpy(tmp, "??");
608 case 0: sprintf(tmp, ",%s+", iregs[reg]); break;
609 case 1: sprintf(tmp, ",%s++", iregs[reg]); break;
610 case 2: sprintf(tmp, ",-%s", iregs[reg]); break;
611 case 3: sprintf(tmp, ",--%s", iregs[reg]); break;
612 case 4: sprintf(tmp, ",%s", iregs[reg]); break;
613 case 5: sprintf(tmp, "(B),%s", iregs[reg]); break;
614 case 6: sprintf(tmp, "(A),%s", iregs[reg]); break;
616 { boff = DFetch(); sprintf(tmp, "($%02X),%s", boff,
617 iregs[reg]); break; }
619 { woff = DFetchW(); sprintf(tmp, "($%04X),%s", woff,
620 iregs[reg]); break; }
621 case 11: sprintf(tmp, "(D),%s", iregs[reg]); break;
623 { boff = DFetch(); sprintf(tmp, "($%02X),PC", boff); break; }
625 { woff = DFetchW(); sprintf(tmp, "($%04X),PC", woff); break; }
626 default: strcpy(tmp, "??");
630 sprintf(outbuf, "%s %s", mnem, tmp);
633 { operand = DFetch(); // Get IMM byte
634 sprintf(outbuf, "%s #$%02X", mnem, operand);
636 case 9: // Long Immediate
637 { loperand = DFetchW(); // Get IMM word
638 sprintf(outbuf, "%s #$%04X", mnem, loperand);
641 DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB)); // Show bytes
642 WriteLog(outbuf); WriteLog("\n"); // display opcode & addressing, etc
643 dpc = (looking_at_rom ? pcr : pcrB); // Advance debug PC
644 pcr = pc_save; pcrB = pcB_save; // Restore PCs
648 // Convert hex to dec
650 uint16 htod(char *str)
653 int len = strlen(str);
655 for(int i=0; i<len; i++)
657 if (str[i]>='0' && str[i]<='9')
659 value = (value<<4) | (unsigned)(str[i]-'0');
661 if (str[i]>='a' && str[i]<='f')
663 value = (value<<4) | (unsigned)(str[i]-'a')+10;
665 if (str[i]>='A' && str[i]<='F')
667 value = (value<<4) | (unsigned)(str[i]-'A')+10;
674 // Load 32K file into ROM image space
676 bool Load32KImg(char * filename, uint16 address)
681 ff.open(filename, ios::binary | ios::in); // Open 'da file...
684 for(long i=0; i<32768; i++) // Read it in...
687 grom[address+i] = ch;
689 ff.close(); // Close 'da file...
695 // Generic Load file into image space
696 // (No error checking performed! Responsibility of caller!)
698 bool LoadImg(const char * filename, uint8 * mem, uint32 address, uint32 length)
704 strcpy(path, "./ROMs/");
705 strcat(path, filename);
706 // ff.open(filename, ios::binary | ios::in); // Open 'da file...
707 ff.open(path, ios::binary | ios::in); // Open 'da file...
710 for(uint32 i=0; i<length; i++) // Read it in...
715 ff.close(); // Close 'da file...
723 bool ReadColorPROMs(void)
728 extern uint8 palette[768]; // Screen physical palette
729 extern uint8 ccolor[256][8]; // Character color PROM values
730 extern uint8 scolor[128][16]; // Sprite color PROM values
732 ff1.open("./ROMs/"PROM3, ios::binary|ios::in);
735 for(int i=0; i<256; i++) // Load char pallete with PROM values
737 for(int j=0; j<8; j++)
740 ccolor[i][j] = (uint8)ch;
745 ff1.open("./ROMs/"PROM4, ios::binary|ios::in);
748 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
750 for(int j=0; j<16; j++)
753 scolor[i][j] = (uint8)ch;
759 ff1.open("./ROMs/"PROM1, ios::binary|ios::in);
760 ff2.open("./ROMs/"PROM2, ios::binary|ios::in);
761 if (ff1) // If open was successful...
763 for(int i=0; i<768; i+=3)
766 palette[i] = (uint8)(ch&0x0F);
767 palette[i+1] = (uint8)(ch>>4);
769 palette[i+2] = (uint8)ch;
772 // Do palette stretching here... I.e. add 0 to hinyb 0, 1 to hinyb 1, etc.
774 for(int i=0; i<768; i++)
775 palette[i] = ((palette[i]<<4)&0xF0) | (palette[i]&0x0F);
786 bool UnpackFonts(void)
792 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
793 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
794 if ((!f1) || (!f2)) return false; // Return if not found...
795 for(long i=0; i<0x40000; i+=64)
797 for(int j=0; j<64; j+=8)
799 f1.get(b1); f1.get(b2); f2.get(b3);
800 b3 ^= 0xFF; // Invert top data...
801 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
802 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
803 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
804 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
805 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
806 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
807 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
808 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
814 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
815 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
816 for(long i=0x40000; i<0x60000; i+=64)
818 for(int j=0; j<64; j+=8)
820 f1.get(b1); f1.get(b2); f2.get(b3);
821 b3 ^= 0xFF; // Invert top data
822 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
823 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
824 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
825 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
826 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
827 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
828 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
829 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
834 return true; // Made it!
838 // Get length of sample from WAV format
840 uint32 GetWAVLength(fstream &file)
845 file.ignore(16); // Skip header BS
847 for(int i=0; i<2; i++)
849 file.get(ch); len = (int)(uint8)ch;
850 file.get(ch); len |= (int)(uint8)ch << 8;
851 file.get(ch); len |= (int)(uint8)ch << 16;
852 file.get(ch); len |= (int)(uint8)ch << 24;
854 file.ignore(len + 4); // Skip intermediate data
857 file.get(ch); len = (int)(uint8)ch; // & finally get length of data
858 file.get(ch); len |= (int)(uint8)ch << 8;
859 file.get(ch); len |= (int)(uint8)ch << 16;
860 file.get(ch); len |= (int)(uint8)ch << 24;
866 // Load PSG samples from disk
874 for(int i=0; i<16; i++)
878 psg_adrs[i] = NULL; // Zero out pointer
879 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
881 fp.open(file, ios::binary | ios::in); // Attempt to open it...
885 len = GetWAVLength(fp); // Get WAV data length...
887 psg_adrs[i] = new uint8[len]; // Attempt to allocate space...
889 if (psg_adrs[i] != NULL)
891 for(int j=0; j<(signed)len; j++)
894 psg_adrs[i][j] = ch; // & load it in...
898 // cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
899 printf("Found sample file: %s\t[Length: %u]\n", file, len);
908 // Load FM samples from disk
916 for(int i=0; i<14; i++)
920 fm_adrs[i] = NULL; // Zero out pointer
921 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
923 fp.open(file, ios::binary | ios::in); // Attempt to open it...
927 len = GetWAVLength(fp); // Get WAV length...
929 fm_adrs[i] = new uint8[len]; // Attempt to allocate space...
931 if (fm_adrs[i] != NULL)
933 for(int j=0; j<(signed)len; j++)
936 fm_adrs[i][j] = ch; // & load it in...
940 // cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
941 printf("Found sample file: %s\t[Length: %u]\n", file, len);
952 int main(int argc, char * argv[])
954 InitLog("thunder.log");
956 extern bool disasm; // From 'V6809.CPP'
957 extern bool charbase; // From 'SCREEN.CPP'
961 fstream ff; // Declare fstream without file hooks...
962 bool brk = false, brk2 = false; // Breakpoint set flag
963 uint16 brkpnt, brkpnt2; // Where the breakpoint is...
964 bool running; // CPU running state flag...
965 bool self_test = false; // Self-test switch
966 bool scr_type = false; // false=chars, true=pixels
967 uint16 debounce = 0; // Key de-bounce counter
968 uint16 fire_debounce = 0; // Fire button debounce counter
969 // bool refresh2 = true; // Default to 60 Hz...
970 uint8 x; // General placeholder...
971 bool active = true; // Program running flag
973 SDL_Event event; // SDL "event"
974 extern uint8 palette[768]; // Screen physical palette
975 uint32 ticks, oldTicks;
977 cout << endl << "THUNDER v"THUNDER_VERSION" ";
978 cout << "by James Hammons" << endl;
979 cout << "Serial #20090723 / Prerelease" << endl;
980 cout << "(C) 2003, 2009 Underground Software" << endl << endl;
982 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
985 cout << "Initializing SDL..." << endl;
987 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
989 cout << "Couldn't initialize SDL: " << SDL_GetError() << endl;
993 SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
995 keys = SDL_GetKeyState(NULL); // Get the SDL keyboard matrix
997 gram = gram1; grom = grom1; // Needed only for debugger
999 for(long i=0; i<0x10000; i++)
1001 gram[i] = 0; grom[i] = 0; // Zero out memory
1002 gram2[i] = 0; grom2[i] = 0;
1004 game_over_switch = 0; // Init game over delay
1005 // cpu1.a = 0; cpu1.b = 0; cpu1.cc = 0; cpu1.dp = 0; cpu1.x = 0; cpu1.y = 0; cpu1.s = 0; ur = 0; cpu1.pc = 0;
1007 cout << "Loading ROMs..." << endl;
1008 // LoadCMOS(); // Load CMOS at $CC00-$CFFF
1009 if (!ReadColorPROMs()) // Load virtual PROMs
1010 { cout << "Could not open PROM files!" << endl; return -1; }
1012 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
1013 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
1015 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
1016 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
1018 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
1019 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
1021 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
1022 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
1024 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
1025 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
1027 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
1028 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
1030 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
1031 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
1033 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
1034 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
1036 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
1037 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
1039 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
1040 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
1042 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
1043 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
1045 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
1046 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
1048 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
1049 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
1051 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
1052 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
1054 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
1055 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
1057 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
1058 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
1060 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
1061 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
1063 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
1064 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
1066 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
1068 cout << "Could not open font files!" << endl;
1072 // Load samples if they're there...
1076 // Quick 'n' Dirty voice dump (sound 0x0E)
1077 /* uint32 adc = (voice_rom[26]<<8) | voice_rom[27];
1078 bool doneWitIt = false;
1082 if (voice_rom[adc] < 0x10) tr << "0";
1083 tr << hex << (int)voice_rom[adc] << " ";
1084 if (crh++ > 24) { crh = 0; tr << endl; }
1085 if ((voice_rom[adc] == 0xFF) && (voice_rom[adc-1] != 0x00))
1090 // Set up V6809 execution contexts
1092 memset(&cpu1, 0, sizeof(V6809REGS));
1095 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1097 memset(&cpu2, 0, sizeof(V6809REGS));
1098 cpu2.RdMem = RdMemB;
1099 cpu2.WrMem = WrMemB;
1100 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1102 bool firstTime = true; // kludge...
1104 WriteLog("About to go to the main loop...\n");
1110 firstTime = false; // crappy kludge...
1117 if (lbuff[0] == 'd')
1124 printf("%04X: ", dpc);
1125 uint16 pc_save = cpu1.pc, pcB_save = cpu2.pc;
1126 cpu1.pc = dpc; cpu2.pc = dpc;
1127 for(int i=0; i<16; i++)
1128 printf("%02X ", (looking_at_rom ? Fetch() : FetchB()));
1130 cpu1.pc = dpc; cpu2.pc = dpc;
1131 for(int i=0; i<16; i++)
1133 uint8 a = (looking_at_rom ? Fetch() : FetchB());
1134 if (a<10) cout << (char)(a+48);
1135 if ((a>9) && (a<37)) cout << (char)(a+55);
1136 if (a>36) cout << ".";
1139 dpc = (looking_at_rom ? cpu1.pc : cpu2.pc);
1140 cpu1.pc = pc_save; cpu2.pc = pcB_save;
1142 else if (lbuff[0] == 'e')
1149 printf("%04X: ", dpc);
1150 for(int i=0; i<16; i++) printf("%02X ", (uint8)gram[dpc++]);
1153 else if (lbuff[0] == 'l')
1160 for(int i=0; i<23; i++)
1163 else if (lbuff[0] == 't')
1174 Execute6809(&cpu1, 1);
1176 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1177 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1178 cout << " iclock=" << cpu1.clock << endl;
1184 Execute6809(&cpu2, 1);
1186 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1187 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1188 cout << " iclock=" << cpu2.clock << endl;
1191 else if ((lbuff[0] == 'r') || (lbuff[0] == 'c')) // Run/continue...
1193 WriteLog("Executing 'run' command...\n");
1194 uint32 my_clock = 0;
1195 running = true; // Set running status...
1197 SetRefreshRate(refresh2); // Tell GUI our refresh rate
1198 //for(uint16 i=0; i<0x8000; i++) gram2[i] = grom3[i]; //Temp
1200 if (lbuff[0] == 'r') // If run, then reset CPUs
1202 WriteLog("Executing secondary 'run' command...\n");
1204 // This is data that is supposed to come from the MCU... So that's why it hangs
1205 gram1[0x4182] = 0xA6; // Temp kludge
1206 gram1[0x4184] = 0xA6;
1207 gram1[0x4183] = 0x00; // More of the same
1208 gram1[0x4185] = 0x00;
1210 banksw1 = 0; // Will this work?
1212 // iclock = 0; // Reset instr clock #1...
1213 InitGUI(); // Reset # of coins
1216 cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
1219 lbuff[0] = 32; cpu1.pc = htod(lbuff);
1221 else cpu1.cc = 0xFF; // Set CC register
1223 cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
1224 cpu2.cc = 0xFF; // Set CC register
1225 while(iclock < 8000) // was 17000, 20000, 5000
1227 Execute6809(&cpu1, 1); Execute6809(&cpu2, 1);
1231 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
1232 // Will *this* help video sync? NO
1233 while (cpu1.clock < 8000) // was 17000, 20000, 5000
1235 Execute6809(&cpu1, 1);
1236 Execute6809(&cpu2, 1);
1241 WriteLog("About to set up screen...\n");
1242 // if (!SetVESA2()) running = false; // Set up screen
1243 // Set up screen (windowed)
1244 screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE); //video_bpp, videoflags);
1247 cout << "Failed to initialize screen!" << endl;
1251 SDL_Color colors[256];
1252 for(int i=0; i<256; i++)
1254 colors[i].r = palette[i*3+0];
1255 colors[i].g = palette[i*3+1];
1256 colors[i].b = palette[i*3+2];
1258 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1261 // This confirms that we're getting video to the screen...
1262 SDL_LockSurface(screen);
1265 uint8 * pixels = (uint8 *)(screen->pixels);
1267 for(uint32 y=0; y<480; y++)
1268 for(uint32 x=0; x<640; x++)
1269 pixels[(y * 640) + x] = pixel++;
1271 SDL_UnlockSurface(screen);
1272 SDL_UpdateRect(screen, 0, 0, 0, 0);
1275 for(int i=0; i<256; i++)
1276 keys[i] = 0; // Clear keyboard buffer...
1278 oldTicks = SDL_GetTicks();
1280 WriteLog("About to set up audio...\n");
1281 // This crap SHOULD be in sound.cpp (not yet created)...
1282 SDL_AudioSpec desired, obtained;
1283 desired.freq = 22050;
1284 desired.format = AUDIO_U8;
1285 desired.channels = 1;
1286 desired.samples = 600;
1287 desired.callback = SoundFunc;
1288 desired.userdata = NULL;
1289 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1290 if (SDL_OpenAudio(&desired, &obtained) < 0)
1292 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1295 SDL_PauseAudio(0); // Get that audio going!
1297 WriteLog("About to enter main loop...\n");
1300 HandleGUIDebounce(); // Debounce GUI keys
1301 if (game_over_switch)
1303 game_over_switch--; // Countdown...
1304 if (game_over_switch == 0)
1305 gram1[0x4380] = 0; // Kill music!
1307 //testing... (works)
1308 //gram1[0x423D] = 1;
1309 //gram1[0x423D] = self_test; // Reset DSW1-1
1310 gram1[0x4268] = 0; // Reset Video test
1311 gram1[0x427A] = 0; gram1[0x427C] = 0;
1312 gram1[0x427B] = 0; gram1[0x427D] = 0;
1313 gram1[0x427E] = 0; gram1[0x427F] = 0;
1314 gram1[0x4280] = 0; gram1[0x4281] = 0;
1315 gram1[0x4276] = 0; gram1[0x426A] = 0;
1316 gram1[0x4278] = 0; gram1[0x426C] = 0;
1317 gram1[0x4262] = 0; gram1[0x4260] = 0;
1318 //gram1[0x4247] = 0;
1320 // SDL key handling...
1322 SDL_PumpEvents(); // Force key events into the buffer.
1324 if (keys[SDLK_ESCAPE])
1325 running = false; // ESC to exit...
1328 debounce--; // Debounce toggle keys...
1333 self_test = !self_test; // Self-test (F1-toggle)
1334 debounce = 10; // Key debounce value...
1338 gram1[0x4268] = 1; // Video test (F2)
1339 debounce = 10; // Key debounce value...
1343 scr_type = !scr_type; // Toggle screen (F12)
1344 debounce = 10; // Key debounce value...
1348 show_scr = !show_scr; // Toggle bkgrnd (F3)
1353 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1358 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1359 SetRefreshRate(refresh2); // Inform GUI of refresh
1364 debounce = 10; // Key debounce value...
1366 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1368 SpawnSound(USERSOUND, SCAMERA);
1372 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1381 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1382 if (keys[SDLK_RIGHT]) // Right arrow
1385 SelectRight(); // If GUI active...
1388 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1389 gram1[0x427F] = 1; // Stick right
1392 if (keys[SDLK_LEFT])
1395 SelectLeft(); // If GUI active...
1398 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1399 gram1[0x4281] = 1; // Left arrow
1405 SelectUp(); // If GUI active...
1408 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1409 gram1[0x427B] = 1; // Up arrow
1412 if (keys[SDLK_DOWN])
1415 SelectDown(); // If GUI active...
1418 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1419 gram1[0x427D] = 1; // Down arrow
1422 if (keys[SDLK_RETURN]) // Return
1424 uint8 retval = UserSelectedSomething();
1427 if (retval == REFRESH)
1429 refresh2 = !refresh2;
1430 SetRefreshRate(refresh2);
1434 gram1[0x427A] = 1; // (1)
1436 gram1[0x427C] = 1; // (2)
1438 gram1[0x427E] = 1; // (3)
1440 gram1[0x4280] = 1; // (5)
1441 if (keys[SDLK_q] | keys[29])
1442 gram1[0x4276] = 1; // (Q) Jump
1444 gram1[0x426A] = 1; // (W)
1447 if (keys[SDLK_e] | keys[56]) // (E) Fire
1453 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1460 gram1[0x426C] = 1; // (R)
1462 gram1[0x4262] = 1; // (T)
1464 gram1[0x4260] = 1; // (Y)
1466 gram1[0x41A5]++; // Coin? (F10)
1468 gram1[0x4189]++; // ? (Z) credits l dig
1470 gram1[0x418A]++; // ? (X) credits r dig
1472 gram1[0x418C]++; // ? (C) Start
1474 gram1[0x418D]++; // ? (V)
1476 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1477 // if (keys[SDLK_F8])
1479 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1480 // charbase = false; // Switch chars out...
1482 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1483 if (keys[SDLK_F11]) // (F11)
1485 Execute6809(&cpu1, 10);
1486 Execute6809(&cpu2, 10);
1489 //F12 is used above, but the values are ignored. So we'll do it here too.
1492 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1493 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1495 if (keys[SDLK_d]) // (D) start disassembly
1499 gram1[0x5606] = 0x00;
1502 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1503 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1507 gram1[0x5FF3] = 0x02;
1508 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1518 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1520 Execute6809(&cpu1, 25000);
1521 cpu1.clock -= 25000; // Remove 25K ticks from clock (in case it overflowed)
1524 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1526 Execute6809(&cpu2, 25000);
1527 cpu2.clock -= 25000; // Remove 25K ticks from clock (in case it overflowed)//*/
1529 // We can do this here because we're not executing the cores yet.
1530 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1531 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1532 // while (cpu1.clock < 25000)
1533 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1534 // 25600 cycles/frame
1535 // Setting interleave to 25 and below causes the V6809 core to hang...
1536 // 40 works, until it doesn't... :-P
1537 for(uint32 i=0; i<640; i++)
1539 // Gay, but what are ya gonna do?
1540 // There's better ways, such as keeping track of when slave writes to master, etc...
1541 Execute6809(&cpu1, 40);
1542 Execute6809(&cpu2, 40);
1545 } // END: enable_cpu
1547 // if (refresh_++ == 1) // 30 Hz...
1550 // BlitWilliamsScreen(gram1); // Display the screen...
1552 // BlitChar(screen, chr_rom, gram1);
1553 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1556 //temp, for testing...
1557 BlitChar(screen, chr_rom, gram1);
1559 // Speed throttling happens here...
1560 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1561 SDL_Delay(1); // Release our timeslice...
1563 oldTicks = SDL_GetTicks();
1564 //cout << "Finished frame..." << endl;
1567 // Stop_audio_output();
1569 // ReleaseKeyboard(); // Release the interrupt...
1570 // RestoreOldMode(); // Restore screen
1571 if (brk && (cpu1.pc == brkpnt))
1572 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1573 if (brk2 && (cpu2.pc == brkpnt2))
1574 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1576 lbuff[0] = 'q'; // Temp kludge...
1578 else if (lbuff[0] == 'b') // Set/clear breakpoint
1583 brkpnt = htod(lbuff);
1585 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1590 cout << "Breakpoint cleared" << endl;
1593 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1598 brkpnt2 = htod(lbuff);
1600 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1605 cout << "Breakpoint cleared" << endl;
1608 else if (lbuff[0] == 'i') // Inspect registers
1610 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1611 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1612 cout << " iclk=" << dec << cpu1.clock << endl;
1613 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1614 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1615 cout << " iclk=" << dec << cpu2.clock << endl;
1617 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1619 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1621 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1623 looking_at_rom = !looking_at_rom;
1624 cout << "Swapped: Looking at ";
1625 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1628 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1632 for(int i=0; i<4; i++)
1640 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1643 printf("%04X: ", dpc); // Show data found...
1644 for(int i=0; i<16; i++)
1645 printf("%02X ", gram1[(uint16)(dpc+i)]);
1647 for(int i=0; i<16; i++)
1649 uint8 a = gram1[dpc++];
1651 cout << (char)(a+48);
1652 if ((a>9) && (a<37))
1653 cout << (char)(a+55);
1659 else if (lbuff[0] == 'v') // View screen
1661 // SetVESA2(); // Set up screen
1662 BlitChar(screen, chr_rom, gram1);
1664 // RestoreOldMode();
1667 if (lbuff[0] == 'q')
1668 active = false; //break; // Quit
1671 SDL_Quit(); // Shut down SDL
1673 for(int i=0; i<16; i++)
1675 delete[] psg_adrs[i]; // Deallocate if loaded
1677 for(int i=0; i<14; i++)
1679 delete[] fm_adrs[i]; // Deallocate if loaded
1687 Rolling Thunder Memory map
1688 --------------------------
1689 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1690 map is inferred by program behaviour. The customs also handle internally irq
1692 The main CPU memory map is the same in all games because CUS47 is used by all
1693 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1694 replaced by other chips.
1695 All RAM is shared between main and sub CPU, except for sound RAM which is shared
1696 between main and sound CPU; the portion of object RAM that is overlapped by sound
1697 RAM is used exclusively by the sub CPU.
1701 Address Dir Data Name Description
1702 ------------------- --- -------- --------- -----------------------
1703 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1704 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1705 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1706 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1707 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1708 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1709 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1710 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1711 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1712 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1713 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1714 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1715 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1716 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1717 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1718 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1719 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1720 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1721 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1723 [1] Note that this is partially overlapped by sound RAM
1724 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1729 Address Dir Data Name Description
1730 ------------------- --- -------- --------- -----------------------
1731 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1732 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1733 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1734 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1735 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1736 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1737 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1738 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1739 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1740 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1741 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1742 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1743 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1744 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1746 [1] Only used by Rolling Thunder
1751 Address Dir Data Name Description
1752 ------------------- --- -------- --------- -----------------------
1753 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1754 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1755 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1756 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1757 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1758 0010 0--- --01 ---- n.c.
1759 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1760 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1761 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1762 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1763 1011 0--- ---- ---- W unknown (CUS41)
1764 1011 1--- ---- ---- W unknown (CUS41)
1765 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1770 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1771 in rthunder. The two 6809 in this game synchronize using a semaphore at
1772 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1773 and then increments 5606. While it does its quick things (which require
1774 about 40 clock cycles) it expects CPU2 to clear 5607.
1775 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1776 mode. I haven't investigated on the cause.
1778 - There are two watchdogs, one per CPU (or maybe three). Handling them
1779 separately is necessary to allow entering service mode without manually
1780 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1783 - The sprite hardware buffers spriteram: the program writes the sprite list to
1784 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1785 sprite RAM to signal the chip that the list is complete. The chip will copy
1786 the list from 4-9 to 10-15 and use it from there. This has not been verified
1787 on the real hardware, but it is the most logical way of doing it.
1788 Emulating this behaviour and not using an external buffer is important in
1789 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1790 is not written to. If we buffered spriteram to an external buffer, this would
1791 cause dangling sprites because the buffer would not be updated.
1793 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1794 entering a door. The *closed* door is made of tiles, but the *moving* door is
1795 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1796 there is one frame where neither the tile-based closed door nor the
1797 sprite-based moving door is shown, so it flickers. This behavior has been
1798 confirmed on a real PCB.
1802 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1803 but they don't seem to work as expected. During the first few frames they are
1804 written out of order and hooking them up in the usual way causes the MCU to
1805 stop receiving interrupts.