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)
314 extern bool charbase; // Needed for screen. Extern it in it??
315 //extern uint16 sr, ur, xr, yr; // Needed for tracelog
317 /* if ((addr>0x40FF) && (addr<0x4390))
319 tr << hex << addr << ":" << (int)b;
320 //for(int i=0; i<32; i++)
322 // if (gram1[0x4400+i]<0x10) tr << "0";
323 // tr << hex << (uint16)gram1[0x4400+i] << " ";
330 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
335 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
337 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
339 banksw1 = (uint32)b << 13; // Set char data bankswitch base address
340 if (addr > 0x4284 && addr < 0x42A5 && b)
341 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
344 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
346 game_over_switch = 240; // Set game over delay...
348 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
351 charbase = false; // Char banksw1
353 charbase = true; // Char banksw2
354 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
356 if (refresh_++ == 1) // 30 Hz...
358 BlitChar(screen, chr_rom, gram1);
359 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
361 //Seems they're more regular than this...
362 // irqGoA = true; // Will this work??? no...
363 // cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok???
364 // IRQ Ack (may also be frame go...
365 // cpu1.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
368 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
370 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
375 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
377 uint8 RdMemB(uint16 addr)
379 // extern uint16 cpu2.s, cpu2.u, cpu2.x, cpu2.y; // Needed for tracelog
385 b = gram1[addr + 0x4000];
386 if (addr > 0x1FFF && addr < 0x6000)
387 b = gram1[addr - 0x2000];
389 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
394 /* if ((addr>0x3FFF) && (addr<0x4400)) tr << "R-" << hex << pcx << ": "
396 << (int)looking_at_rom
398 << "] XR:" << xr << " YR:" << yr
399 << " SR:" << sr << " UR:" << ur
405 // Write a byte to memory (2nd processor)
407 void WrMemB(uint16 addr, uint8 b)
410 extern bool charbase;
411 //extern uint16 sr, ur, xr, yr; // Needed for tracelog
413 /* if ((addr>0x00FF) && (addr<0x0390))
415 tr << hex << addr << ":" << (int)b;
416 //for(int i=0; i<32; i++)
418 // if (gram1[0x4400+i]<0x10) tr << "0";
419 // tr << hex << (uint16)gram1[0x4400+i] << " ";
426 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
430 // Bah. Dunno if this is accurate or not!
431 // if (addr == 0x8800)
432 // irqGoB = true; // Will it work??? no...
433 // cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok???
435 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
437 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
438 if (addr > 0x0284 && addr < 0x02A5 && b)
439 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
441 banksw2 = (uint32)(b & 0x03) << 13; // Set sprite data bank switch
444 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
446 game_over_switch = 240; // Set game over delay...
448 if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
451 gram1[addr + 0x4000] = b;
452 if (addr > 0x1FFF && addr < 0x6000)
453 gram1[addr - 0x2000] = b;
459 // IRQ Ack (may also be frame go...)
460 // cpu2.cpuFlags &= ~V6809_ASSERT_LINE_IRQ;
463 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
465 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
470 // Display bytes in mem in hex
472 void DisplayBytes(uint16 src, unsigned long dst)
477 WriteLog("%04X: ", src);
478 cnt = 0; // Init counter...
479 if (src > dst) dst += 0x10000; // That should fix the FFFF bug...
480 for(i=src; i<dst; i++)
482 WriteLog("%02X ", (uint8)(looking_at_rom ? RdMem(i) : RdMemB(i)));
483 cnt++; // Bump counter...
485 for(i=cnt; i<5; i++) // Pad the leftover spaces...
492 uint8 Fetch(void) { return RdMem(dpc); }
493 uint16 FetchW(void) { return (uint16)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
494 uint8 FetchB(void) { return RdMemB(dpc); }
495 uint16 FetchWB(void) { return (uint16)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
498 // Decode a 6809 instruction at 'addr'
502 uint8 (* DFetch)(); // Decode Fetch() pointer...
503 uint16 (* DFetchW)(); // Decode FetchW() pointer...
504 DFetch = (looking_at_rom ? Fetch : FetchB);
505 DFetchW = (looking_at_rom ? FetchW : FetchWB);
507 /* extern*/ uint16 pcr, pcrB; // Pull in 'pcr' from '6809.cpp'
508 uint16 pc_save = pcr, pcB_save = pcrB;
509 pcr = dpc; pcrB = dpc;
510 uint8 opcode = DFetch(); // Get the opcode ('fetch' cycle)
511 uint8 opcode2, operand;
513 uint8 admode = op_mat1[opcode]; // addressing mode
514 char outbuf[80], mnem[6], tmp[30];
516 strcpy(mnem, mnemonics[opcode]); // Copy page 1 opcode
517 if (opcode == 0x10) // Extended opcode?
519 opcode2 = DFetch(); // Then get next byte
520 admode = op_mat2[opcode2]; // And use it as index into 'op_mat2'
521 strcpy(mnem, mnemonics2[opcode2]); // Overwrite mnemonic
523 if (opcode == 0x11) // Same as above...
526 admode = op_mat3[opcode2];
527 strcpy(mnem, mnemonics3[opcode2]); // Overwrite mnemonic
529 switch(admode) // Decode it...
532 { sprintf(outbuf, "???"); break; }
534 { operand = DFetch(); // Get ZP address
535 sprintf(outbuf, "%s $%02X", mnem, operand);
538 { loperand = DFetchW(); // Get ABS address
539 sprintf(outbuf, "%s $%04X", mnem, loperand);
542 { operand = DFetch(); // Get offset
543 uint16 tmpc = (looking_at_rom ? pcr : pcrB);
544 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16)(int8)operand);
546 case 4: // Long Relative
547 { loperand = DFetchW(); // Get long offset
548 uint16 tmpc = (looking_at_rom ? pcr : pcrB);
549 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16)loperand);
552 { sprintf(outbuf, "%s ", mnem);
554 case 6: // Txfr/exchg/push/pull
555 { operand = DFetch(); // Get txfr/exg/push/pull byte
556 if ((opcode == 0x1E) || (opcode == 0x1F)) // Is it TXF/EXG?
558 sprintf(tmp, "%s,%s", tregs[operand>>4], tregs[operand&0x0F]);
563 if (operand&0x01) strcat(tmp, "CC ");
564 if (operand&0x02) strcat(tmp, "A ");
565 if (operand&0x04) strcat(tmp, "B ");
566 if (operand&0x08) strcat(tmp, "DP ");
567 if (operand&0x10) strcat(tmp, "X ");
568 if (operand&0x20) strcat(tmp, "Y ");
569 if (operand&0x40) (((opcode==0x34)||(opcode==0x35))
570 ? strcat(tmp, "U ") : strcat(tmp, "S "));
571 if (operand&0x80) strcat(tmp, "PC");
573 sprintf(outbuf, "%s %s", mnem, tmp);
575 case 7: // Indexed (the tough one!)
576 { operand = DFetch(); // Get IDX byte
577 uint8 reg = ((operand & 0x60) >> 5), idxind = ((operand & 0x10) >> 4),
578 lo_nyb = (operand & 0x0F), boff;
582 if (!(operand & 0x80)) // Hi bit set? Then decode 4 bit offset
584 sprintf(tmp, "(%d),%s", (idxind ? -(16-lo_nyb) : lo_nyb),
587 else // Add the ($nnnn,R) code dude...
593 case 1: sprintf(tmp, "(,%s++)", iregs[reg]); break;
594 case 3: sprintf(tmp, "(,--%s)", iregs[reg]); break;
595 case 4: sprintf(tmp, "(,%s)", iregs[reg]); break;
596 case 5: sprintf(tmp, "(B,%s)", iregs[reg]); break;
597 case 6: sprintf(tmp, "(A,%s)", iregs[reg]); break;
599 { boff = DFetch(); sprintf(tmp, "($%02X,%s)", boff,
600 iregs[reg]); break; }
602 { woff = DFetchW(); sprintf(tmp, "($%04X,%s)", woff,
603 iregs[reg]); break; }
604 case 11: sprintf(tmp, "(D,%s)", iregs[reg]); break;
606 { boff = DFetch(); sprintf(tmp, "($%02X,PC)", boff); break; }
608 { woff = DFetchW(); sprintf(tmp, "($%04X,PC)", woff); break; }
610 { woff = DFetchW(); sprintf(tmp, "[$%04X]", woff); break; }
611 default: strcpy(tmp, "??");
618 case 0: sprintf(tmp, ",%s+", iregs[reg]); break;
619 case 1: sprintf(tmp, ",%s++", iregs[reg]); break;
620 case 2: sprintf(tmp, ",-%s", iregs[reg]); break;
621 case 3: sprintf(tmp, ",--%s", iregs[reg]); break;
622 case 4: sprintf(tmp, ",%s", iregs[reg]); break;
623 case 5: sprintf(tmp, "(B),%s", iregs[reg]); break;
624 case 6: sprintf(tmp, "(A),%s", iregs[reg]); break;
626 { boff = DFetch(); sprintf(tmp, "($%02X),%s", boff,
627 iregs[reg]); break; }
629 { woff = DFetchW(); sprintf(tmp, "($%04X),%s", woff,
630 iregs[reg]); break; }
631 case 11: sprintf(tmp, "(D),%s", iregs[reg]); break;
633 { boff = DFetch(); sprintf(tmp, "($%02X),PC", boff); break; }
635 { woff = DFetchW(); sprintf(tmp, "($%04X),PC", woff); break; }
636 default: strcpy(tmp, "??");
640 sprintf(outbuf, "%s %s", mnem, tmp);
643 { operand = DFetch(); // Get IMM byte
644 sprintf(outbuf, "%s #$%02X", mnem, operand);
646 case 9: // Long Immediate
647 { loperand = DFetchW(); // Get IMM word
648 sprintf(outbuf, "%s #$%04X", mnem, loperand);
651 DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB)); // Show bytes
652 WriteLog(outbuf); WriteLog("\n"); // display opcode & addressing, etc
653 dpc = (looking_at_rom ? pcr : pcrB); // Advance debug PC
654 pcr = pc_save; pcrB = pcB_save; // Restore PCs
658 // Convert hex to dec
660 uint16 htod(char *str)
663 int len = strlen(str);
665 for(int i=0; i<len; i++)
667 if (str[i]>='0' && str[i]<='9')
669 value = (value<<4) | (unsigned)(str[i]-'0');
671 if (str[i]>='a' && str[i]<='f')
673 value = (value<<4) | (unsigned)(str[i]-'a')+10;
675 if (str[i]>='A' && str[i]<='F')
677 value = (value<<4) | (unsigned)(str[i]-'A')+10;
684 // Load 32K file into ROM image space
686 bool Load32KImg(char * filename, uint16 address)
691 ff.open(filename, ios::binary | ios::in); // Open 'da file...
694 for(long i=0; i<32768; i++) // Read it in...
697 grom[address+i] = ch;
699 ff.close(); // Close 'da file...
705 // Generic Load file into image space
706 // (No error checking performed! Responsibility of caller!)
708 bool LoadImg(const char * filename, uint8 * mem, uint32 address, uint32 length)
714 strcpy(path, "./ROMs/");
715 strcat(path, filename);
716 // ff.open(filename, ios::binary | ios::in); // Open 'da file...
717 ff.open(path, ios::binary | ios::in); // Open 'da file...
720 for(uint32 i=0; i<length; i++) // Read it in...
725 ff.close(); // Close 'da file...
733 bool ReadColorPROMs(void)
738 extern uint8 palette[768]; // Screen physical palette
739 extern uint8 ccolor[256][8]; // Character color PROM values
740 extern uint8 scolor[128][16]; // Sprite color PROM values
742 ff1.open("./ROMs/"PROM3, ios::binary|ios::in);
745 for(int i=0; i<256; i++) // Load char pallete with PROM values
747 for(int j=0; j<8; j++)
750 ccolor[i][j] = (uint8)ch;
755 ff1.open("./ROMs/"PROM4, ios::binary|ios::in);
758 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
760 for(int j=0; j<16; j++)
763 scolor[i][j] = (uint8)ch;
769 ff1.open("./ROMs/"PROM1, ios::binary|ios::in);
770 ff2.open("./ROMs/"PROM2, ios::binary|ios::in);
771 if (ff1) // If open was successful...
773 for(int i=0; i<768; i+=3)
776 palette[i] = (uint8)(ch&0x0F);
777 palette[i+1] = (uint8)(ch>>4);
779 palette[i+2] = (uint8)ch;
782 // Do palette stretching here... I.e. add 0 to hinyb 0, 1 to hinyb 1, etc.
784 for(int i=0; i<768; i++)
785 palette[i] = ((palette[i]<<4)&0xF0) | (palette[i]&0x0F);
796 bool UnpackFonts(void)
802 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
803 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
804 if ((!f1) || (!f2)) return false; // Return if not found...
805 for(long i=0; i<0x40000; i+=64)
807 for(int j=0; j<64; j+=8)
809 f1.get(b1); f1.get(b2); f2.get(b3);
810 b3 ^= 0xFF; // Invert top data...
811 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
812 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
813 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
814 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
815 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
816 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
817 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
818 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
824 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
825 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
826 for(long i=0x40000; i<0x60000; i+=64)
828 for(int j=0; j<64; j+=8)
830 f1.get(b1); f1.get(b2); f2.get(b3);
831 b3 ^= 0xFF; // Invert top data
832 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
833 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
834 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
835 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
836 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
837 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
838 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
839 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
844 return true; // Made it!
848 // Get length of sample from WAV format
850 uint32 GetWAVLength(fstream &file)
855 file.ignore(16); // Skip header BS
857 for(int i=0; i<2; i++)
859 file.get(ch); len = (int)(uint8)ch;
860 file.get(ch); len |= (int)(uint8)ch << 8;
861 file.get(ch); len |= (int)(uint8)ch << 16;
862 file.get(ch); len |= (int)(uint8)ch << 24;
864 file.ignore(len + 4); // Skip intermediate data
867 file.get(ch); len = (int)(uint8)ch; // & finally get length of data
868 file.get(ch); len |= (int)(uint8)ch << 8;
869 file.get(ch); len |= (int)(uint8)ch << 16;
870 file.get(ch); len |= (int)(uint8)ch << 24;
876 // Load PSG samples from disk
884 for(int i=0; i<16; i++)
888 psg_adrs[i] = NULL; // Zero out pointer
889 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
891 fp.open(file, ios::binary | ios::in); // Attempt to open it...
895 len = GetWAVLength(fp); // Get WAV data length...
897 psg_adrs[i] = new uint8[len]; // Attempt to allocate space...
899 if (psg_adrs[i] != NULL)
901 for(int j=0; j<(signed)len; j++)
904 psg_adrs[i][j] = ch; // & load it in...
908 // cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
909 printf("Found sample file: %s\t[Length: %u]\n", file, len);
918 // Load FM samples from disk
926 for(int i=0; i<14; i++)
930 fm_adrs[i] = NULL; // Zero out pointer
931 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
933 fp.open(file, ios::binary | ios::in); // Attempt to open it...
937 len = GetWAVLength(fp); // Get WAV length...
939 fm_adrs[i] = new uint8[len]; // Attempt to allocate space...
941 if (fm_adrs[i] != NULL)
943 for(int j=0; j<(signed)len; j++)
946 fm_adrs[i][j] = ch; // & load it in...
950 // cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
951 printf("Found sample file: %s\t[Length: %u]\n", file, len);
962 int main(int argc, char * argv[])
964 InitLog("thunder.log");
966 extern bool disasm; // From 'V6809.CPP'
967 extern bool charbase; // From 'SCREEN.CPP'
971 fstream ff; // Declare fstream without file hooks...
972 bool brk = false, brk2 = false; // Breakpoint set flag
973 uint16 brkpnt, brkpnt2; // Where the breakpoint is...
974 bool running; // CPU running state flag...
975 bool self_test = false; // Self-test switch
976 bool scr_type = false; // false=chars, true=pixels
977 uint16 debounce = 0; // Key de-bounce counter
978 uint16 fire_debounce = 0; // Fire button debounce counter
979 // bool refresh2 = true; // Default to 60 Hz...
980 uint8 x; // General placeholder...
981 bool active = true; // Program running flag
983 SDL_Event event; // SDL "event"
984 extern uint8 palette[768]; // Screen physical palette
985 uint32 ticks, oldTicks;
987 cout << endl << "THUNDER v"THUNDER_VERSION" ";
988 cout << "by James Hammons" << endl;
989 cout << "Serial #20090723 / Prerelease" << endl;
990 cout << "(C) 2003, 2009 Underground Software" << endl << endl;
992 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
995 cout << "Initializing SDL..." << endl;
997 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
999 cout << "Couldn't initialize SDL: " << SDL_GetError() << endl;
1003 SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
1005 keys = SDL_GetKeyState(NULL); // Get the SDL keyboard matrix
1007 gram = gram1; grom = grom1; // Needed only for debugger
1009 for(long i=0; i<0x10000; i++)
1011 gram[i] = 0; grom[i] = 0; // Zero out memory
1012 gram2[i] = 0; grom2[i] = 0;
1014 game_over_switch = 0; // Init game over delay
1015 // 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;
1017 cout << "Loading ROMs..." << endl;
1018 // LoadCMOS(); // Load CMOS at $CC00-$CFFF
1019 if (!ReadColorPROMs()) // Load virtual PROMs
1020 { cout << "Could not open PROM files!" << endl; return -1; }
1022 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
1023 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
1025 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
1026 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
1028 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
1029 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
1031 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
1032 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
1034 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
1035 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
1037 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
1038 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
1040 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
1041 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
1043 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
1044 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
1046 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
1047 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
1049 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
1050 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
1052 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
1053 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
1055 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
1056 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
1058 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
1059 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
1061 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
1062 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
1064 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
1065 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
1067 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
1068 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
1070 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
1071 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
1073 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
1074 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
1076 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
1078 cout << "Could not open font files!" << endl;
1082 // Load samples if they're there...
1086 // Quick 'n' Dirty voice dump (sound 0x0E)
1087 /* uint32 adc = (voice_rom[26]<<8) | voice_rom[27];
1088 bool doneWitIt = false;
1092 if (voice_rom[adc] < 0x10) tr << "0";
1093 tr << hex << (int)voice_rom[adc] << " ";
1094 if (crh++ > 24) { crh = 0; tr << endl; }
1095 if ((voice_rom[adc] == 0xFF) && (voice_rom[adc-1] != 0x00))
1100 // Set up V6809 execution contexts
1102 memset(&cpu1, 0, sizeof(V6809REGS));
1105 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1107 memset(&cpu2, 0, sizeof(V6809REGS));
1108 cpu2.RdMem = RdMemB;
1109 cpu2.WrMem = WrMemB;
1110 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1112 bool firstTime = true; // kludge...
1114 WriteLog("About to go to the main loop...\n");
1120 firstTime = false; // crappy kludge...
1127 if (lbuff[0] == 'd')
1134 printf("%04X: ", dpc);
1135 uint16 pc_save = cpu1.pc, pcB_save = cpu2.pc;
1136 cpu1.pc = dpc; cpu2.pc = dpc;
1137 for(int i=0; i<16; i++)
1138 printf("%02X ", (looking_at_rom ? Fetch() : FetchB()));
1140 cpu1.pc = dpc; cpu2.pc = dpc;
1141 for(int i=0; i<16; i++)
1143 uint8 a = (looking_at_rom ? Fetch() : FetchB());
1144 if (a<10) cout << (char)(a+48);
1145 if ((a>9) && (a<37)) cout << (char)(a+55);
1146 if (a>36) cout << ".";
1149 dpc = (looking_at_rom ? cpu1.pc : cpu2.pc);
1150 cpu1.pc = pc_save; cpu2.pc = pcB_save;
1152 else if (lbuff[0] == 'e')
1159 printf("%04X: ", dpc);
1160 for(int i=0; i<16; i++) printf("%02X ", (uint8)gram[dpc++]);
1163 else if (lbuff[0] == 'l')
1170 for(int i=0; i<23; i++)
1173 else if (lbuff[0] == 't')
1184 Execute6809(&cpu1, 1);
1186 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1187 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1188 cout << " iclock=" << cpu1.clock << endl;
1194 Execute6809(&cpu2, 1);
1196 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1197 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1198 cout << " iclock=" << cpu2.clock << endl;
1201 else if ((lbuff[0] == 'r') || (lbuff[0] == 'c')) // Run/continue...
1203 WriteLog("Executing 'run' command...\n");
1204 uint32 my_clock = 0;
1205 running = true; // Set running status...
1207 SetRefreshRate(refresh2); // Tell GUI our refresh rate
1208 //for(uint16 i=0; i<0x8000; i++) gram2[i] = grom3[i]; //Temp
1210 if (lbuff[0] == 'r') // If run, then reset CPUs
1212 WriteLog("Executing secondary 'run' command...\n");
1214 // This is data that is supposed to come from the MCU... So that's why it hangs
1215 gram1[0x4182] = 0xA6; // Temp kludge
1216 gram1[0x4184] = 0xA6;
1217 gram1[0x4183] = 0x00; // More of the same
1218 gram1[0x4185] = 0x00;
1220 banksw1 = 0; // Will this work?
1222 // iclock = 0; // Reset instr clock #1...
1223 InitGUI(); // Reset # of coins
1226 cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
1229 lbuff[0] = 32; cpu1.pc = htod(lbuff);
1231 else cpu1.cc = 0xFF; // Set CC register
1233 cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
1234 cpu2.cc = 0xFF; // Set CC register
1235 while(iclock < 8000) // was 17000, 20000, 5000
1237 Execute6809(&cpu1, 1); Execute6809(&cpu2, 1);
1241 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
1242 // Will *this* help video sync? NO
1243 while (cpu1.clock < 8000) // was 17000, 20000, 5000
1245 Execute6809(&cpu1, 1);
1246 Execute6809(&cpu2, 1);
1251 WriteLog("About to set up screen...\n");
1252 // if (!SetVESA2()) running = false; // Set up screen
1253 // Set up screen (windowed)
1254 screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE); //video_bpp, videoflags);
1257 cout << "Failed to initialize screen!" << endl;
1261 SDL_Color colors[256];
1262 for(int i=0; i<256; i++)
1264 colors[i].r = palette[i*3+0];
1265 colors[i].g = palette[i*3+1];
1266 colors[i].b = palette[i*3+2];
1268 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1271 // This confirms that we're getting video to the screen...
1272 SDL_LockSurface(screen);
1275 uint8 * pixels = (uint8 *)(screen->pixels);
1277 for(uint32 y=0; y<480; y++)
1278 for(uint32 x=0; x<640; x++)
1279 pixels[(y * 640) + x] = pixel++;
1281 SDL_UnlockSurface(screen);
1282 SDL_UpdateRect(screen, 0, 0, 0, 0);
1285 for(int i=0; i<256; i++)
1286 keys[i] = 0; // Clear keyboard buffer...
1288 oldTicks = SDL_GetTicks();
1290 WriteLog("About to set up audio...\n");
1291 // This crap SHOULD be in sound.cpp (not yet created)...
1292 SDL_AudioSpec desired, obtained;
1293 desired.freq = 22050;
1294 desired.format = AUDIO_U8;
1295 desired.channels = 1;
1296 desired.samples = 600;
1297 desired.callback = SoundFunc;
1298 desired.userdata = NULL;
1299 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1300 if (SDL_OpenAudio(&desired, &obtained) < 0)
1302 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1305 SDL_PauseAudio(0); // Get that audio going!
1307 WriteLog("About to enter main loop...\n");
1310 HandleGUIDebounce(); // Debounce GUI keys
1311 if (game_over_switch)
1313 game_over_switch--; // Countdown...
1314 if (game_over_switch == 0)
1315 gram1[0x4380] = 0; // Kill music!
1317 //testing... (works)
1318 //gram1[0x423D] = 1;
1319 //gram1[0x423D] = self_test; // Reset DSW1-1
1320 gram1[0x4268] = 0; // Reset Video test
1321 gram1[0x427A] = 0; gram1[0x427C] = 0;
1322 gram1[0x427B] = 0; gram1[0x427D] = 0;
1323 gram1[0x427E] = 0; gram1[0x427F] = 0;
1324 gram1[0x4280] = 0; gram1[0x4281] = 0;
1325 gram1[0x4276] = 0; gram1[0x426A] = 0;
1326 gram1[0x4278] = 0; gram1[0x426C] = 0;
1327 gram1[0x4262] = 0; gram1[0x4260] = 0;
1328 //gram1[0x4247] = 0;
1330 // SDL key handling...
1332 SDL_PumpEvents(); // Force key events into the buffer.
1334 if (keys[SDLK_ESCAPE])
1335 running = false; // ESC to exit...
1338 debounce--; // Debounce toggle keys...
1343 self_test = !self_test; // Self-test (F1-toggle)
1344 debounce = 10; // Key debounce value...
1348 gram1[0x4268] = 1; // Video test (F2)
1349 debounce = 10; // Key debounce value...
1353 scr_type = !scr_type; // Toggle screen (F12)
1354 debounce = 10; // Key debounce value...
1358 show_scr = !show_scr; // Toggle bkgrnd (F3)
1363 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1368 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1369 SetRefreshRate(refresh2); // Inform GUI of refresh
1374 debounce = 10; // Key debounce value...
1376 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1378 SpawnSound(USERSOUND, SCAMERA);
1382 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1391 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1392 if (keys[SDLK_RIGHT]) // Right arrow
1395 SelectRight(); // If GUI active...
1398 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1399 gram1[0x427F] = 1; // Stick right
1402 if (keys[SDLK_LEFT])
1405 SelectLeft(); // If GUI active...
1408 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1409 gram1[0x4281] = 1; // Left arrow
1415 SelectUp(); // If GUI active...
1418 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1419 gram1[0x427B] = 1; // Up arrow
1422 if (keys[SDLK_DOWN])
1425 SelectDown(); // If GUI active...
1428 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1429 gram1[0x427D] = 1; // Down arrow
1432 if (keys[SDLK_RETURN]) // Return
1434 uint8 retval = UserSelectedSomething();
1437 if (retval == REFRESH)
1439 refresh2 = !refresh2;
1440 SetRefreshRate(refresh2);
1444 gram1[0x427A] = 1; // (1)
1446 gram1[0x427C] = 1; // (2)
1448 gram1[0x427E] = 1; // (3)
1450 gram1[0x4280] = 1; // (5)
1451 if (keys[SDLK_q] | keys[29])
1452 gram1[0x4276] = 1; // (Q) Jump
1454 gram1[0x426A] = 1; // (W)
1457 if (keys[SDLK_e] | keys[56]) // (E) Fire
1463 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1470 gram1[0x426C] = 1; // (R)
1472 gram1[0x4262] = 1; // (T)
1474 gram1[0x4260] = 1; // (Y)
1476 gram1[0x41A5]++; // Coin? (F10)
1478 gram1[0x4189]++; // ? (Z) credits l dig
1480 gram1[0x418A]++; // ? (X) credits r dig
1482 gram1[0x418C]++; // ? (C) Start
1484 gram1[0x418D]++; // ? (V)
1486 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1487 // if (keys[SDLK_F8])
1489 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1490 // charbase = false; // Switch chars out...
1492 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1493 if (keys[SDLK_F11]) // (F11)
1495 Execute6809(&cpu1, 10);
1496 Execute6809(&cpu2, 10);
1499 //F12 is used above, but the values are ignored. So we'll do it here too.
1502 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1503 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1505 if (keys[SDLK_d]) // (D) start disassembly
1509 gram1[0x5606] = 0x00;
1512 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1513 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1517 gram1[0x5FF3] = 0x02;
1518 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1526 // We can do this here because we're not executing the cores yet.
1527 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1528 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1529 // while (cpu1.clock < 25000)
1530 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1531 // 25600 cycles/frame
1532 // Setting interleave to 25 and below causes the V6809 core to hang...
1533 // 32 gets to the title screen before hanging...
1534 // 40 works, until it doesn't... :-P
1537 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1538 for(uint32 i=0; i<640; i++)
1539 // for(uint32 i=0; i<1280; i++)
1541 // Gay, but what are ya gonna do?
1542 // There's better ways, such as keeping track of when slave writes to master, etc...
1543 Execute6809(&cpu1, 40);
1544 Execute6809(&cpu2, 40);
1546 } // END: enable_cpu
1548 // if (refresh_++ == 1) // 30 Hz...
1551 // BlitWilliamsScreen(gram1); // Display the screen...
1553 // BlitChar(screen, chr_rom, gram1);
1554 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1558 //temp, for testing...
1559 BlitChar(screen, chr_rom, gram1);
1561 // Speed throttling happens here...
1562 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1563 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1564 SDL_Delay(1); // Release our timeslice...
1566 oldTicks = SDL_GetTicks();
1567 //cout << "Finished frame..." << endl;
1570 // Stop_audio_output();
1572 // ReleaseKeyboard(); // Release the interrupt...
1573 // RestoreOldMode(); // Restore screen
1574 if (brk && (cpu1.pc == brkpnt))
1575 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1576 if (brk2 && (cpu2.pc == brkpnt2))
1577 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1579 lbuff[0] = 'q'; // Temp kludge...
1581 else if (lbuff[0] == 'b') // Set/clear breakpoint
1586 brkpnt = htod(lbuff);
1588 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1593 cout << "Breakpoint cleared" << endl;
1596 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1601 brkpnt2 = htod(lbuff);
1603 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1608 cout << "Breakpoint cleared" << endl;
1611 else if (lbuff[0] == 'i') // Inspect registers
1613 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1614 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1615 cout << " iclk=" << dec << cpu1.clock << endl;
1616 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1617 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1618 cout << " iclk=" << dec << cpu2.clock << endl;
1620 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1622 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1624 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1626 looking_at_rom = !looking_at_rom;
1627 cout << "Swapped: Looking at ";
1628 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1631 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1635 for(int i=0; i<4; i++)
1643 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1646 printf("%04X: ", dpc); // Show data found...
1647 for(int i=0; i<16; i++)
1648 printf("%02X ", gram1[(uint16)(dpc+i)]);
1650 for(int i=0; i<16; i++)
1652 uint8 a = gram1[dpc++];
1654 cout << (char)(a+48);
1655 if ((a>9) && (a<37))
1656 cout << (char)(a+55);
1662 else if (lbuff[0] == 'v') // View screen
1664 // SetVESA2(); // Set up screen
1665 BlitChar(screen, chr_rom, gram1);
1667 // RestoreOldMode();
1670 if (lbuff[0] == 'q')
1671 active = false; //break; // Quit
1674 SDL_Quit(); // Shut down SDL
1676 for(int i=0; i<16; i++)
1678 delete[] psg_adrs[i]; // Deallocate if loaded
1680 for(int i=0; i<14; i++)
1682 delete[] fm_adrs[i]; // Deallocate if loaded
1690 Rolling Thunder Memory map
1691 --------------------------
1692 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1693 map is inferred by program behaviour. The customs also handle internally irq
1695 The main CPU memory map is the same in all games because CUS47 is used by all
1696 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1697 replaced by other chips.
1698 All RAM is shared between main and sub CPU, except for sound RAM which is shared
1699 between main and sound CPU; the portion of object RAM that is overlapped by sound
1700 RAM is used exclusively by the sub CPU.
1704 Address Dir Data Name Description
1705 ------------------- --- -------- --------- -----------------------
1706 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1707 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1708 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1709 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1710 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1711 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1712 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1713 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1714 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1715 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1716 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1717 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1718 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1719 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1720 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1721 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1722 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1723 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1724 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1726 [1] Note that this is partially overlapped by sound RAM
1727 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1732 Address Dir Data Name Description
1733 ------------------- --- -------- --------- -----------------------
1734 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1735 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1736 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1737 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1738 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1739 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1740 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1741 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1742 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1743 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1744 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1745 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1746 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1747 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1749 [1] Only used by Rolling Thunder
1754 Address Dir Data Name Description
1755 ------------------- --- -------- --------- -----------------------
1756 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1757 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1758 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1759 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1760 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1761 0010 0--- --01 ---- n.c.
1762 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1763 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1764 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1765 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1766 1011 0--- ---- ---- W unknown (CUS41)
1767 1011 1--- ---- ---- W unknown (CUS41)
1768 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1773 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1774 in rthunder. The two 6809 in this game synchronize using a semaphore at
1775 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1776 and then increments 5606. While it does its quick things (which require
1777 about 40 clock cycles) it expects CPU2 to clear 5607.
1778 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1779 mode. I haven't investigated on the cause.
1781 - There are two watchdogs, one per CPU (or maybe three). Handling them
1782 separately is necessary to allow entering service mode without manually
1783 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1786 - The sprite hardware buffers spriteram: the program writes the sprite list to
1787 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1788 sprite RAM to signal the chip that the list is complete. The chip will copy
1789 the list from 4-9 to 10-15 and use it from there. This has not been verified
1790 on the real hardware, but it is the most logical way of doing it.
1791 Emulating this behaviour and not using an external buffer is important in
1792 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1793 is not written to. If we buffered spriteram to an external buffer, this would
1794 cause dangling sprites because the buffer would not be updated.
1796 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1797 entering a door. The *closed* door is made of tiles, but the *moving* door is
1798 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1799 there is one frame where neither the tile-based closed door nor the
1800 sprite-based moving door is shown, so it flickers. This behavior has been
1801 confirmed on a real PCB.
1805 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1806 but they don't seem to work as expected. During the first few frames they are
1807 written out of order and hooking them up in the usual way causes the MCU to
1808 stop receiving interrupts.