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 ;-)
12 // JLH 08/12/2009 Stabilized emulation so that it works
15 #define THUNDER_VERSION "0.9.9"
24 //#include <conio.h> // For getch()
25 #include <curses.h> // For getch()
27 #include "SDL.h" // Get yer SDL out...!
34 using namespace std; // Yes!
37 #define ROM1 "RT3-1B.ROM"
38 #define ROM2 "RT3-2B.ROM"
39 #define ROM3 "RT3-3.ROM"
40 #define ROM4 "RT3-4.ROM"
41 #define ROM5 "RT1-5.ROM"
42 #define ROM6 "RT1-6.ROM"
43 #define ROM7 "RT1-7.ROM"
44 #define ROM8 "RT1-8.ROM"
45 #define ROM9 "RT1-9.ROM"
46 #define ROM10 "RT1-10.ROM"
47 #define ROM11 "RT1-11.ROM"
48 #define ROM12 "RT1-12.ROM"
49 #define ROM13 "RT1-13.ROM"
50 #define ROM14 "RT1-14.ROM"
51 #define ROM15 "RT1-15.ROM"
52 #define ROM16 "RT1-16.ROM"
53 #define ROM17 "RT1-17.ROM"
54 #define ROM18 "RT1-18.ROM"
55 #define ROM19 "RT3-19.ROM"
56 #define ROM20 "RT3-20.ROM"
57 #define ROM21 "RT1-21.ROM"
58 #define ROM22 "RT2-22.ROM"
59 #define PROM1 "RT1-1.BIN"
60 #define PROM2 "RT1-2.BIN"
61 #define PROM3 "RT1-3.BIN"
62 #define PROM4 "RT1-4.BIN"
63 #define PROM5 "RT1-5.BIN"
65 #define ROM1 "rt3-1b.9c"
66 #define ROM2 "rt3-2b.12c"
67 #define ROM3 "rt3-3.12d"
68 #define ROM4 "rt1-4.6b"
69 #define ROM5 "rt1-5.4r"
70 #define ROM6 "rt1-6.4s"
71 #define ROM7 "rt1-7.7r"
72 #define ROM8 "rt1-8.7s"
73 #define ROM9 "rt1-9.12h"
74 #define ROM10 "rt1-10.12k"
75 #define ROM11 "rt1-11.12l"
76 #define ROM12 "rt1-12.12m"
77 #define ROM13 "rt1-13.12p"
78 #define ROM14 "rt1-14.12r"
79 #define ROM15 "rt1-15.12t"
80 #define ROM16 "rt1-16.12u"
81 #define ROM17 "rt1-17.f1"
82 #define ROM18 "rt1-18.h1"
83 #define ROM19 "rt1-19.k1"
84 #define ROM20 "rt1-20.m1"
85 #define ROM21 "rt1-21.f3"
86 #define ROM22 "rt1-22.h3"
87 #define PROM1 "mb7124e.3r"
88 #define PROM2 "mb7116e.3s"
89 #define PROM3 "mb7138h.4v"
90 #define PROM4 "mb7138h.6v"
91 #define PROM5 "mb7112e.6u"
92 #define MCUROM "rt1-mcu.bin"
99 uint8 * gram, * grom; // Allocate RAM & ROM pointers
100 uint8 gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
101 uint8 grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
102 uint8 chr_rom[0x60000]; // Character ROM pointer
104 V6809REGS cpu1, cpu2;
106 bool trace1 = false; // ditto...
107 bool looking_at_rom = true; // true = R1, false = R2
108 uint32 banksw1, banksw2; // Bank switch addresses
109 uint16 game_over_switch; // Game over delay
110 uint16 dpc; // Debug pc reg...
111 bool show_scr = true; // Whether or not to show background
112 bool enable_cpu = true; // Whether or not to enable CPUs
113 bool irqGoA = true; // IRQ switch for CPU #1
114 bool irqGoB = true; // IRQ switch for CPU #2
116 uint16 refresh_ = 0; // Crappy global screen stuff...
117 bool refresh2 = true;
119 uint32 psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
120 uint8 * psg_adrs[16];
121 uint32 voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
123 uint8 * voc_adrs[32];
124 uint32 fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
127 fstream tr; // Tracelog hook
128 uint16 pcx; // Where we at?
130 static uint8 * keys; // SDL raw keyboard matrix
132 static char op_mat1[256] = {
133 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
134 0, 0, 5, 5, 0, 0, 4, 4, 0, 5, 8, 0, 8, 5, 6, 6,
135 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
136 7, 7, 7, 7, 6, 6, 6, 6, 0, 5, 5, 5, 8, 5, 5, 5,
137 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
138 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
139 7, 0, 0, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7,
140 2, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,
141 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 3, 9, 0,
142 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
143 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
144 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
145 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 0, 9, 0,
146 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
147 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
148 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
158 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0,
159 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
160 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7,
161 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2,
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7,
165 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
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, 0,
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
176 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
177 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
178 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
183 static char mnemonics[256][6] = {
184 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
185 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
186 "PAGE1","PAGE2","NOP ","SYNC ","??? ","??? ","LBRA ","LBSR ",
187 "??? ","DAA ","ORCC ","??? ","ANDCC","SEX ","EXG ","TFR ",
188 "BRA ","BRN ","BHI ","BLS ","BHS ","BLO ","BNE ","BEQ ",
189 "BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
190 "LEAX ","LEAY ","LEAS ","LEAU ","PSHS ","PULS ","PSHU ","PULU ",
191 "??? ","RTS ","ABX ","RTI ","CWAI ","MUL ","RESET","SWI ",
192 "NEGA ","??? ","??? ","COMA ","LSRA ","??? ","RORA ","ASRA ",
193 "LSLA ","ROLA ","DECA ","??? ","INCA ","TSTA ","??? ","CLRA ",
194 "NEGB ","??? ","??? ","COMB ","LSRB ","??? ","RORB ","ASRB ",
195 "LSLB ","ROLB ","DECB ","??? ","INCB ","TSTB ","??? ","CLRB ",
196 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
197 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
198 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
199 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
200 "SUBA ","CMPA ","SCBA ","SUBD ","ANDA ","BITA ","LDA ","??? ",
201 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","BSR ","LDX ","??? ",
202 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
203 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
204 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
205 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
206 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
207 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
208 "SUBB ","CMPB ","SCBB ","ADDD ","ANDB ","BITB ","LDB ","??? ",
209 "EORB ","ADCB ","ORB ","ADDB ","LDD ","??? ","LDU ","??? ",
210 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
211 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
212 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
213 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
214 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
215 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU " },
216 mnemonics2[256][6] = {
217 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
218 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
219 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
220 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
221 "??? ","LBRN ","LBHI ","LBLS ","LBHS ","LBLO ","LBNE ","LBEQ ",
222 "LBVC ","LBVS ","LBPL ","LBMI ","LBGE ","LBLT ","LBGT ","LBLE ",
223 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
224 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI2 ",
225 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
226 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
227 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
228 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
229 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
230 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
231 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
232 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
233 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
234 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","??? ",
235 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
236 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
237 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
238 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
239 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
240 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
241 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
242 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","??? ",
243 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
244 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
245 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
246 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
247 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
248 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS " },
249 mnemonics3[256][6] = {
250 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
251 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
252 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
253 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
254 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
255 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
256 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
257 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI3 ",
258 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
259 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
260 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
261 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
262 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
263 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
264 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
265 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
266 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
267 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
268 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
269 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
270 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
271 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
272 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
273 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
274 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
275 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
276 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
277 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
278 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
279 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
280 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
281 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? " },
283 "D", "X", "Y", "U", "S", "PC", "??", "??",
284 "A", "B", "CC", "DP", "??", "??", "??", "??" },
285 iregs[4][2] = {"X", "Y", "U", "S" };
288 // Read a byte from memory (without touching PC. Not a Fetch!)
290 uint8 RdMem(uint16 addr)
294 // $4000-4300 is RAM shared with the microcontroller...
299 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
310 // Write a byte to memory
312 void WrMem(uint16 addr, uint8 b)
315 extern bool charbase; // Needed for screen. Extern it in it??
316 //extern uint16 sr, ur, xr, yr; // Needed for tracelog
318 /* if ((addr>0x40FF) && (addr<0x4390))
320 tr << hex << addr << ":" << (int)b;
321 //for(int i=0; i<32; i++)
323 // if (gram1[0x4400+i]<0x10) tr << "0";
324 // tr << hex << (uint16)gram1[0x4400+i] << " ";
331 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
336 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
338 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
340 banksw1 = (uint32)b << 13; // Set char data bankswitch base address
341 if (addr > 0x4284 && addr < 0x42A5 && b)
342 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
345 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
347 game_over_switch = 240; // Set game over delay...
349 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
352 charbase = false; // Char banksw1
354 charbase = true; // Char banksw2
355 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
357 if (refresh_++ == 1) // 30 Hz...
359 BlitChar(screen, chr_rom, gram1);
360 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
363 // IRQ Ack (may also be frame go...
364 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
367 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
373 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
375 uint8 RdMemB(uint16 addr)
377 // extern uint16 cpu2.s, cpu2.u, cpu2.x, cpu2.y; // Needed for tracelog
383 b = gram1[addr + 0x4000];
384 if (addr > 0x1FFF && addr < 0x6000)
385 b = gram1[addr - 0x2000];
387 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
392 /* if ((addr>0x3FFF) && (addr<0x4400)) tr << "R-" << hex << pcx << ": "
394 << (int)looking_at_rom
396 << "] XR:" << xr << " YR:" << yr
397 << " SR:" << sr << " UR:" << ur
403 // Write a byte to memory (2nd processor)
405 void WrMemB(uint16 addr, uint8 b)
408 extern bool charbase;
409 //extern uint16 sr, ur, xr, yr; // Needed for tracelog
411 /* if ((addr>0x00FF) && (addr<0x0390))
413 tr << hex << addr << ":" << (int)b;
414 //for(int i=0; i<32; i++)
416 // if (gram1[0x4400+i]<0x10) tr << "0";
417 // tr << hex << (uint16)gram1[0x4400+i] << " ";
424 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
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 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
457 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
463 // Display bytes in mem in hex
465 void DisplayBytes(uint16 src, unsigned long dst)
470 WriteLog("%04X: ", src);
471 cnt = 0; // Init counter...
472 if (src > dst) dst += 0x10000; // That should fix the FFFF bug...
473 for(i=src; i<dst; i++)
475 WriteLog("%02X ", (uint8)(looking_at_rom ? RdMem(i) : RdMemB(i)));
476 cnt++; // Bump counter...
478 for(i=cnt; i<5; i++) // Pad the leftover spaces...
485 uint8 Fetch(void) { return RdMem(dpc); }
486 uint16 FetchW(void) { return (uint16)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
487 uint8 FetchB(void) { return RdMemB(dpc); }
488 uint16 FetchWB(void) { return (uint16)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
491 // Decode a 6809 instruction at 'addr'
495 uint8 (* DFetch)(); // Decode Fetch() pointer...
496 uint16 (* DFetchW)(); // Decode FetchW() pointer...
497 DFetch = (looking_at_rom ? Fetch : FetchB);
498 DFetchW = (looking_at_rom ? FetchW : FetchWB);
500 /* extern*/ uint16 pcr, pcrB; // Pull in 'pcr' from '6809.cpp'
501 uint16 pc_save = pcr, pcB_save = pcrB;
502 pcr = dpc; pcrB = dpc;
503 uint8 opcode = DFetch(); // Get the opcode ('fetch' cycle)
504 uint8 opcode2, operand;
506 uint8 admode = op_mat1[opcode]; // addressing mode
507 char outbuf[80], mnem[6], tmp[30];
509 strcpy(mnem, mnemonics[opcode]); // Copy page 1 opcode
510 if (opcode == 0x10) // Extended opcode?
512 opcode2 = DFetch(); // Then get next byte
513 admode = op_mat2[opcode2]; // And use it as index into 'op_mat2'
514 strcpy(mnem, mnemonics2[opcode2]); // Overwrite mnemonic
516 if (opcode == 0x11) // Same as above...
519 admode = op_mat3[opcode2];
520 strcpy(mnem, mnemonics3[opcode2]); // Overwrite mnemonic
522 switch(admode) // Decode it...
525 { sprintf(outbuf, "???"); break; }
527 { operand = DFetch(); // Get ZP address
528 sprintf(outbuf, "%s $%02X", mnem, operand);
531 { loperand = DFetchW(); // Get ABS address
532 sprintf(outbuf, "%s $%04X", mnem, loperand);
535 { operand = DFetch(); // Get offset
536 uint16 tmpc = (looking_at_rom ? pcr : pcrB);
537 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16)(int8)operand);
539 case 4: // Long Relative
540 { loperand = DFetchW(); // Get long offset
541 uint16 tmpc = (looking_at_rom ? pcr : pcrB);
542 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16)loperand);
545 { sprintf(outbuf, "%s ", mnem);
547 case 6: // Txfr/exchg/push/pull
548 { operand = DFetch(); // Get txfr/exg/push/pull byte
549 if ((opcode == 0x1E) || (opcode == 0x1F)) // Is it TXF/EXG?
551 sprintf(tmp, "%s,%s", tregs[operand>>4], tregs[operand&0x0F]);
556 if (operand&0x01) strcat(tmp, "CC ");
557 if (operand&0x02) strcat(tmp, "A ");
558 if (operand&0x04) strcat(tmp, "B ");
559 if (operand&0x08) strcat(tmp, "DP ");
560 if (operand&0x10) strcat(tmp, "X ");
561 if (operand&0x20) strcat(tmp, "Y ");
562 if (operand&0x40) (((opcode==0x34)||(opcode==0x35))
563 ? strcat(tmp, "U ") : strcat(tmp, "S "));
564 if (operand&0x80) strcat(tmp, "PC");
566 sprintf(outbuf, "%s %s", mnem, tmp);
568 case 7: // Indexed (the tough one!)
569 { operand = DFetch(); // Get IDX byte
570 uint8 reg = ((operand & 0x60) >> 5), idxind = ((operand & 0x10) >> 4),
571 lo_nyb = (operand & 0x0F), boff;
575 if (!(operand & 0x80)) // Hi bit set? Then decode 4 bit offset
577 sprintf(tmp, "(%d),%s", (idxind ? -(16-lo_nyb) : lo_nyb),
580 else // Add the ($nnnn,R) code dude...
586 case 1: sprintf(tmp, "(,%s++)", iregs[reg]); break;
587 case 3: sprintf(tmp, "(,--%s)", iregs[reg]); break;
588 case 4: sprintf(tmp, "(,%s)", iregs[reg]); break;
589 case 5: sprintf(tmp, "(B,%s)", iregs[reg]); break;
590 case 6: sprintf(tmp, "(A,%s)", iregs[reg]); break;
592 { boff = DFetch(); sprintf(tmp, "($%02X,%s)", boff,
593 iregs[reg]); break; }
595 { woff = DFetchW(); sprintf(tmp, "($%04X,%s)", woff,
596 iregs[reg]); break; }
597 case 11: sprintf(tmp, "(D,%s)", iregs[reg]); break;
599 { boff = DFetch(); sprintf(tmp, "($%02X,PC)", boff); break; }
601 { woff = DFetchW(); sprintf(tmp, "($%04X,PC)", woff); break; }
603 { woff = DFetchW(); sprintf(tmp, "[$%04X]", woff); break; }
604 default: strcpy(tmp, "??");
611 case 0: sprintf(tmp, ",%s+", iregs[reg]); break;
612 case 1: sprintf(tmp, ",%s++", iregs[reg]); break;
613 case 2: sprintf(tmp, ",-%s", iregs[reg]); break;
614 case 3: sprintf(tmp, ",--%s", iregs[reg]); break;
615 case 4: sprintf(tmp, ",%s", iregs[reg]); break;
616 case 5: sprintf(tmp, "(B),%s", iregs[reg]); break;
617 case 6: sprintf(tmp, "(A),%s", iregs[reg]); break;
619 { boff = DFetch(); sprintf(tmp, "($%02X),%s", boff,
620 iregs[reg]); break; }
622 { woff = DFetchW(); sprintf(tmp, "($%04X),%s", woff,
623 iregs[reg]); break; }
624 case 11: sprintf(tmp, "(D),%s", iregs[reg]); break;
626 { boff = DFetch(); sprintf(tmp, "($%02X),PC", boff); break; }
628 { woff = DFetchW(); sprintf(tmp, "($%04X),PC", woff); break; }
629 default: strcpy(tmp, "??");
633 sprintf(outbuf, "%s %s", mnem, tmp);
636 { operand = DFetch(); // Get IMM byte
637 sprintf(outbuf, "%s #$%02X", mnem, operand);
639 case 9: // Long Immediate
640 { loperand = DFetchW(); // Get IMM word
641 sprintf(outbuf, "%s #$%04X", mnem, loperand);
644 DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB)); // Show bytes
645 WriteLog(outbuf); WriteLog("\n"); // display opcode & addressing, etc
646 dpc = (looking_at_rom ? pcr : pcrB); // Advance debug PC
647 pcr = pc_save; pcrB = pcB_save; // Restore PCs
651 // Convert hex to dec
653 uint16 htod(char *str)
656 int len = strlen(str);
658 for(int i=0; i<len; i++)
660 if (str[i]>='0' && str[i]<='9')
662 value = (value<<4) | (unsigned)(str[i]-'0');
664 if (str[i]>='a' && str[i]<='f')
666 value = (value<<4) | (unsigned)(str[i]-'a')+10;
668 if (str[i]>='A' && str[i]<='F')
670 value = (value<<4) | (unsigned)(str[i]-'A')+10;
677 // Load 32K file into ROM image space
679 bool Load32KImg(char * filename, uint16 address)
684 ff.open(filename, ios::binary | ios::in); // Open 'da file...
687 for(long i=0; i<32768; i++) // Read it in...
690 grom[address+i] = ch;
692 ff.close(); // Close 'da file...
698 // Generic Load file into image space
699 // (No error checking performed! Responsibility of caller!)
701 bool LoadImg(const char * filename, uint8 * mem, uint32 address, uint32 length)
707 strcpy(path, "./ROMs/");
708 strcat(path, filename);
709 // ff.open(filename, ios::binary | ios::in); // Open 'da file...
710 ff.open(path, ios::binary | ios::in); // Open 'da file...
713 for(uint32 i=0; i<length; i++) // Read it in...
718 ff.close(); // Close 'da file...
726 bool ReadColorPROMs(void)
731 extern uint8 palette[768]; // Screen physical palette
732 extern uint8 ccolor[256][8]; // Character color PROM values
733 extern uint8 scolor[128][16]; // Sprite color PROM values
735 ff1.open("./ROMs/"PROM3, ios::binary|ios::in);
738 for(int i=0; i<256; i++) // Load char pallete with PROM values
740 for(int j=0; j<8; j++)
743 ccolor[i][j] = (uint8)ch;
748 ff1.open("./ROMs/"PROM4, ios::binary|ios::in);
751 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
753 for(int j=0; j<16; j++)
756 scolor[i][j] = (uint8)ch;
762 ff1.open("./ROMs/"PROM1, ios::binary|ios::in);
763 ff2.open("./ROMs/"PROM2, ios::binary|ios::in);
764 if (ff1) // If open was successful...
766 for(int i=0; i<768; i+=3)
769 palette[i] = (uint8)(ch&0x0F);
770 palette[i+1] = (uint8)(ch>>4);
772 palette[i+2] = (uint8)ch;
775 // Do palette stretching here... I.e. add 0 to hinyb 0, 1 to hinyb 1, etc.
777 for(int i=0; i<768; i++)
778 palette[i] = ((palette[i]<<4)&0xF0) | (palette[i]&0x0F);
789 bool UnpackFonts(void)
795 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
796 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
797 if ((!f1) || (!f2)) return false; // Return if not found...
798 for(long i=0; i<0x40000; i+=64)
800 for(int j=0; j<64; j+=8)
802 f1.get(b1); f1.get(b2); f2.get(b3);
803 b3 ^= 0xFF; // Invert top data...
804 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
805 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
806 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
807 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
808 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
809 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
810 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
811 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
817 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
818 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
819 for(long i=0x40000; i<0x60000; i+=64)
821 for(int j=0; j<64; j+=8)
823 f1.get(b1); f1.get(b2); f2.get(b3);
824 b3 ^= 0xFF; // Invert top data
825 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
826 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
827 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
828 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
829 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
830 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
831 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
832 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
837 return true; // Made it!
841 // Get length of sample from WAV format
843 uint32 GetWAVLength(fstream &file)
848 file.ignore(16); // Skip header BS
850 for(int i=0; i<2; i++)
852 file.get(ch); len = (int)(uint8)ch;
853 file.get(ch); len |= (int)(uint8)ch << 8;
854 file.get(ch); len |= (int)(uint8)ch << 16;
855 file.get(ch); len |= (int)(uint8)ch << 24;
857 file.ignore(len + 4); // Skip intermediate data
860 file.get(ch); len = (int)(uint8)ch; // & finally get length of data
861 file.get(ch); len |= (int)(uint8)ch << 8;
862 file.get(ch); len |= (int)(uint8)ch << 16;
863 file.get(ch); len |= (int)(uint8)ch << 24;
869 // Load PSG samples from disk
877 for(int i=0; i<16; i++)
881 psg_adrs[i] = NULL; // Zero out pointer
882 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
884 fp.open(file, ios::binary | ios::in); // Attempt to open it...
888 len = GetWAVLength(fp); // Get WAV data length...
889 psg_adrs[i] = new uint8[len]; // Attempt to allocate space...
891 if (psg_adrs[i] != NULL)
893 for(int j=0; j<(signed)len; j++)
896 psg_adrs[i][j] = ch; // & load it in...
900 // cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
901 printf("Found sample file: %s\t[Length: %u]\n", file, len);
910 // Load FM samples from disk
918 for(int i=0; i<14; i++)
922 fm_adrs[i] = NULL; // Zero out pointer
923 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
925 fp.open(file, ios::binary | ios::in); // Attempt to open it...
929 len = GetWAVLength(fp); // Get WAV length...
930 fm_adrs[i] = new uint8[len]; // Attempt to allocate space...
932 if (fm_adrs[i] != NULL)
934 for(int j=0; j<(signed)len; j++)
937 fm_adrs[i][j] = ch; // & load it in...
941 // cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
942 printf("Found sample file: %s\t[Length: %u]\n", file, len);
953 int main(int argc, char * argv[])
955 InitLog("thunder.log");
957 extern bool disasm; // From 'V6809.CPP'
958 extern bool charbase; // From 'SCREEN.CPP'
962 fstream ff; // Declare fstream without file hooks...
963 bool brk = false, brk2 = false; // Breakpoint set flag
964 uint16 brkpnt, brkpnt2; // Where the breakpoint is...
965 bool running; // CPU running state flag...
966 bool self_test = false; // Self-test switch
967 bool scr_type = false; // false=chars, true=pixels
968 uint16 debounce = 0; // Key de-bounce counter
969 uint16 fire_debounce = 0; // Fire button debounce counter
970 // bool refresh2 = true; // Default to 60 Hz...
971 uint8 x; // General placeholder...
972 bool active = true; // Program running flag
974 SDL_Event event; // SDL "event"
975 extern uint8 palette[768]; // Screen physical palette
976 uint32 ticks, oldTicks;
978 cout << endl << "THUNDER v"THUNDER_VERSION" ";
979 cout << "by James Hammons" << endl;
980 cout << "Serial #20090723 / Prerelease" << endl;
981 cout << "(C) 2003, 2009 Underground Software" << endl << endl;
983 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
986 cout << "Initializing SDL..." << endl;
988 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
990 cout << "Couldn't initialize SDL: " << SDL_GetError() << endl;
994 SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
996 keys = SDL_GetKeyState(NULL); // Get the SDL keyboard matrix
998 gram = gram1; grom = grom1; // Needed only for debugger
1000 for(long i=0; i<0x10000; i++)
1002 gram[i] = 0; grom[i] = 0; // Zero out memory
1003 gram2[i] = 0; grom2[i] = 0;
1005 game_over_switch = 0; // Init game over delay
1006 // 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;
1008 cout << "Loading ROMs..." << endl;
1009 // LoadCMOS(); // Load CMOS at $CC00-$CFFF
1010 if (!ReadColorPROMs()) // Load virtual PROMs
1011 { cout << "Could not open PROM files!" << endl; return -1; }
1013 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
1014 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
1016 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
1017 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
1019 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
1020 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
1022 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
1023 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
1025 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
1026 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
1028 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
1029 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
1031 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
1032 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
1034 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
1035 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
1037 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
1038 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
1040 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
1041 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
1043 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
1044 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
1046 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
1047 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
1049 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
1050 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
1052 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
1053 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
1055 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
1056 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
1058 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
1059 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
1061 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
1062 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
1064 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
1065 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
1067 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
1069 cout << "Could not open font files!" << endl;
1073 // Load samples if they're there...
1077 // Quick 'n' Dirty voice dump (sound 0x0E)
1078 /* uint32 adc = (voice_rom[26]<<8) | voice_rom[27];
1079 bool doneWitIt = false;
1083 if (voice_rom[adc] < 0x10) tr << "0";
1084 tr << hex << (int)voice_rom[adc] << " ";
1085 if (crh++ > 24) { crh = 0; tr << endl; }
1086 if ((voice_rom[adc] == 0xFF) && (voice_rom[adc-1] != 0x00))
1091 // Set up V6809 execution contexts
1093 memset(&cpu1, 0, sizeof(V6809REGS));
1096 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1098 memset(&cpu2, 0, sizeof(V6809REGS));
1099 cpu2.RdMem = RdMemB;
1100 cpu2.WrMem = WrMemB;
1101 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1103 bool firstTime = true; // kludge...
1105 WriteLog("About to go to the main loop...\n");
1111 firstTime = false; // crappy kludge...
1118 if (lbuff[0] == 'd')
1125 printf("%04X: ", dpc);
1126 uint16 pc_save = cpu1.pc, pcB_save = cpu2.pc;
1127 cpu1.pc = dpc; cpu2.pc = dpc;
1128 for(int i=0; i<16; i++)
1129 printf("%02X ", (looking_at_rom ? Fetch() : FetchB()));
1131 cpu1.pc = dpc; cpu2.pc = dpc;
1132 for(int i=0; i<16; i++)
1134 uint8 a = (looking_at_rom ? Fetch() : FetchB());
1135 if (a<10) cout << (char)(a+48);
1136 if ((a>9) && (a<37)) cout << (char)(a+55);
1137 if (a>36) cout << ".";
1140 dpc = (looking_at_rom ? cpu1.pc : cpu2.pc);
1141 cpu1.pc = pc_save; cpu2.pc = pcB_save;
1143 else if (lbuff[0] == 'e')
1150 printf("%04X: ", dpc);
1151 for(int i=0; i<16; i++) printf("%02X ", (uint8)gram[dpc++]);
1154 else if (lbuff[0] == 'l')
1161 for(int i=0; i<23; i++)
1164 else if (lbuff[0] == 't')
1175 Execute6809(&cpu1, 1);
1177 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1178 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1179 cout << " iclock=" << cpu1.clock << endl;
1185 Execute6809(&cpu2, 1);
1187 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1188 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1189 cout << " iclock=" << cpu2.clock << endl;
1192 else if ((lbuff[0] == 'r') || (lbuff[0] == 'c')) // Run/continue...
1194 WriteLog("Executing 'run' command...\n");
1195 uint32 my_clock = 0;
1196 running = true; // Set running status...
1198 SetRefreshRate(refresh2); // Tell GUI our refresh rate
1199 //for(uint16 i=0; i<0x8000; i++) gram2[i] = grom3[i]; //Temp
1201 if (lbuff[0] == 'r') // If run, then reset CPUs
1203 WriteLog("Executing secondary 'run' command...\n");
1205 // This is data that is supposed to come from the MCU... So that's why it hangs
1206 gram1[0x4182] = 0xA6; // Temp kludge
1207 gram1[0x4184] = 0xA6;
1208 gram1[0x4183] = 0x00; // More of the same
1209 gram1[0x4185] = 0x00;
1211 banksw1 = 0; // Will this work?
1213 // iclock = 0; // Reset instr clock #1...
1214 InitGUI(); // Reset # of coins
1217 cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
1220 lbuff[0] = 32; cpu1.pc = htod(lbuff);
1222 else cpu1.cc = 0xFF; // Set CC register
1224 cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
1225 cpu2.cc = 0xFF; // Set CC register
1226 while(iclock < 8000) // was 17000, 20000, 5000
1228 Execute6809(&cpu1, 1); Execute6809(&cpu2, 1);
1232 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
1233 // Will *this* help video sync? NO
1234 while (cpu1.clock < 8000) // was 17000, 20000, 5000
1236 Execute6809(&cpu1, 1);
1237 Execute6809(&cpu2, 1);
1242 WriteLog("About to set up screen...\n");
1243 // if (!SetVESA2()) running = false; // Set up screen
1244 // Set up screen (windowed)
1245 screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE); //video_bpp, videoflags);
1248 cout << "Failed to initialize screen!" << endl;
1252 SDL_Color colors[256];
1253 for(int i=0; i<256; i++)
1255 colors[i].r = palette[i*3+0];
1256 colors[i].g = palette[i*3+1];
1257 colors[i].b = palette[i*3+2];
1259 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1262 // This confirms that we're getting video to the screen...
1263 SDL_LockSurface(screen);
1266 uint8 * pixels = (uint8 *)(screen->pixels);
1268 for(uint32 y=0; y<480; y++)
1269 for(uint32 x=0; x<640; x++)
1270 pixels[(y * 640) + x] = pixel++;
1272 SDL_UnlockSurface(screen);
1273 SDL_UpdateRect(screen, 0, 0, 0, 0);
1276 for(int i=0; i<256; i++)
1277 keys[i] = 0; // Clear keyboard buffer...
1279 oldTicks = SDL_GetTicks();
1281 WriteLog("About to set up audio...\n");
1282 // This crap SHOULD be in sound.cpp (not yet created)...
1283 SDL_AudioSpec desired, obtained;
1284 desired.freq = 22050;
1285 desired.format = AUDIO_U8;
1286 desired.channels = 1;
1287 desired.samples = 600;
1288 desired.callback = SoundFunc;
1289 desired.userdata = NULL;
1290 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1291 if (SDL_OpenAudio(&desired, &obtained) < 0)
1293 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1296 SDL_PauseAudio(0); // Get that audio going!
1298 WriteLog("About to enter main loop...\n");
1301 HandleGUIDebounce(); // Debounce GUI keys
1302 if (game_over_switch)
1304 game_over_switch--; // Countdown...
1305 if (game_over_switch == 0)
1306 gram1[0x4380] = 0; // Kill music!
1308 //testing... (works)
1309 //gram1[0x423D] = 1;
1310 //gram1[0x423D] = self_test; // Reset DSW1-1
1311 gram1[0x4268] = 0; // Reset Video test
1312 gram1[0x427A] = 0; gram1[0x427C] = 0;
1313 gram1[0x427B] = 0; gram1[0x427D] = 0;
1314 gram1[0x427E] = 0; gram1[0x427F] = 0;
1315 gram1[0x4280] = 0; gram1[0x4281] = 0;
1316 gram1[0x4276] = 0; gram1[0x426A] = 0;
1317 gram1[0x4278] = 0; gram1[0x426C] = 0;
1318 gram1[0x4262] = 0; gram1[0x4260] = 0;
1319 //gram1[0x4247] = 0;
1321 // SDL key handling...
1323 SDL_PumpEvents(); // Force key events into the buffer.
1325 if (keys[SDLK_ESCAPE])
1326 running = false; // ESC to exit...
1329 debounce--; // Debounce toggle keys...
1334 self_test = !self_test; // Self-test (F1-toggle)
1335 debounce = 10; // Key debounce value...
1339 gram1[0x4268] = 1; // Video test (F2)
1340 debounce = 10; // Key debounce value...
1344 scr_type = !scr_type; // Toggle screen (F12)
1345 debounce = 10; // Key debounce value...
1349 show_scr = !show_scr; // Toggle bkgrnd (F3)
1354 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1359 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1360 SetRefreshRate(refresh2); // Inform GUI of refresh
1365 debounce = 10; // Key debounce value...
1367 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1369 SpawnSound(USERSOUND, SCAMERA);
1373 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1382 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1383 if (keys[SDLK_RIGHT]) // Right arrow
1386 SelectRight(); // If GUI active...
1389 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1390 gram1[0x427F] = 1; // Stick right
1393 if (keys[SDLK_LEFT])
1396 SelectLeft(); // If GUI active...
1399 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1400 gram1[0x4281] = 1; // Left arrow
1406 SelectUp(); // If GUI active...
1409 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1410 gram1[0x427B] = 1; // Up arrow
1413 if (keys[SDLK_DOWN])
1416 SelectDown(); // If GUI active...
1419 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1420 gram1[0x427D] = 1; // Down arrow
1423 if (keys[SDLK_RETURN]) // Return
1425 uint8 retval = UserSelectedSomething();
1428 if (retval == REFRESH)
1430 refresh2 = !refresh2;
1431 SetRefreshRate(refresh2);
1435 gram1[0x427A] = 1; // (1)
1437 gram1[0x427C] = 1; // (2)
1439 gram1[0x427E] = 1; // (3)
1441 gram1[0x4280] = 1; // (5)
1442 if (keys[SDLK_q] | keys[29])
1443 gram1[0x4276] = 1; // (Q) Jump
1445 gram1[0x426A] = 1; // (W)
1448 if (keys[SDLK_e] | keys[56]) // (E) Fire
1454 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1461 gram1[0x426C] = 1; // (R)
1463 gram1[0x4262] = 1; // (T)
1465 gram1[0x4260] = 1; // (Y)
1467 gram1[0x41A5]++; // Coin? (F10)
1469 gram1[0x4189]++; // ? (Z) credits l dig
1471 gram1[0x418A]++; // ? (X) credits r dig
1473 gram1[0x418C]++; // ? (C) Start
1475 gram1[0x418D]++; // ? (V)
1477 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1478 // if (keys[SDLK_F8])
1480 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1481 // charbase = false; // Switch chars out...
1483 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1484 if (keys[SDLK_F11]) // (F11)
1486 Execute6809(&cpu1, 10);
1487 Execute6809(&cpu2, 10);
1490 //F12 is used above, but the values are ignored. So we'll do it here too.
1493 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1494 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1496 if (keys[SDLK_d]) // (D) start disassembly
1500 gram1[0x5606] = 0x00;
1503 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1504 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1508 gram1[0x5FF3] = 0x02;
1509 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1517 // We can do this here because we're not executing the cores yet.
1518 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1519 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1520 // while (cpu1.clock < 25000)
1521 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1522 // 25600 cycles/frame
1523 // Setting interleave to 25 and below causes the V6809 core to hang...
1524 // 32 gets to the title screen before hanging...
1525 // 40 works, until it doesn't... :-P
1528 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1529 for(uint32 i=0; i<640; i++)
1530 // for(uint32 i=0; i<1280; i++)
1532 // Gay, but what are ya gonna do?
1533 // There's better ways, such as keeping track of when slave writes to master, etc...
1534 Execute6809(&cpu1, 40);
1535 Execute6809(&cpu2, 40);
1537 } // END: enable_cpu
1539 // if (refresh_++ == 1) // 30 Hz...
1542 // BlitWilliamsScreen(gram1); // Display the screen...
1544 // BlitChar(screen, chr_rom, gram1);
1545 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1549 //temp, for testing...
1550 BlitChar(screen, chr_rom, gram1);
1552 // Speed throttling happens here...
1553 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1554 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1555 SDL_Delay(1); // Release our timeslice...
1557 oldTicks = SDL_GetTicks();
1558 //cout << "Finished frame..." << endl;
1561 // Stop_audio_output();
1563 // ReleaseKeyboard(); // Release the interrupt...
1564 // RestoreOldMode(); // Restore screen
1565 if (brk && (cpu1.pc == brkpnt))
1566 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1567 if (brk2 && (cpu2.pc == brkpnt2))
1568 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1570 lbuff[0] = 'q'; // Temp kludge...
1572 else if (lbuff[0] == 'b') // Set/clear breakpoint
1577 brkpnt = htod(lbuff);
1579 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1584 cout << "Breakpoint cleared" << endl;
1587 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1592 brkpnt2 = htod(lbuff);
1594 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1599 cout << "Breakpoint cleared" << endl;
1602 else if (lbuff[0] == 'i') // Inspect registers
1604 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1605 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1606 cout << " iclk=" << dec << cpu1.clock << endl;
1607 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1608 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1609 cout << " iclk=" << dec << cpu2.clock << endl;
1611 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1613 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1615 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1617 looking_at_rom = !looking_at_rom;
1618 cout << "Swapped: Looking at ";
1619 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1622 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1626 for(int i=0; i<4; i++)
1634 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1637 printf("%04X: ", dpc); // Show data found...
1638 for(int i=0; i<16; i++)
1639 printf("%02X ", gram1[(uint16)(dpc+i)]);
1641 for(int i=0; i<16; i++)
1643 uint8 a = gram1[dpc++];
1645 cout << (char)(a+48);
1646 if ((a>9) && (a<37))
1647 cout << (char)(a+55);
1653 else if (lbuff[0] == 'v') // View screen
1655 // SetVESA2(); // Set up screen
1656 BlitChar(screen, chr_rom, gram1);
1658 // RestoreOldMode();
1661 if (lbuff[0] == 'q')
1662 active = false; //break; // Quit
1665 SDL_Quit(); // Shut down SDL
1667 for(int i=0; i<16; i++)
1669 delete[] psg_adrs[i]; // Deallocate if loaded
1671 for(int i=0; i<14; i++)
1673 delete[] fm_adrs[i]; // Deallocate if loaded
1681 Hitachi uC runs at 6.144 MHz
1682 YM2151 runs at 3.579580 MHz
1685 Rolling Thunder Memory map
1686 --------------------------
1687 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1688 map is inferred by program behaviour. The customs also handle internally irq
1690 The main CPU memory map is the same in all games because CUS47 is used by all
1691 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1692 replaced by other chips.
1693 All RAM is shared between main and sub CPU, except for sound RAM which is shared
1694 between main and sound CPU; the portion of object RAM that is overlapped by sound
1695 RAM is used exclusively by the sub CPU.
1699 Address Dir Data Name Description
1700 ------------------- --- -------- --------- -----------------------
1701 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1702 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1703 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1704 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1705 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1706 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1707 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1708 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1709 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1710 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1711 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1712 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1713 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1714 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1715 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1716 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1717 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1718 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1719 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1721 [1] Note that this is partially overlapped by sound RAM
1722 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1727 Address Dir Data Name Description
1728 ------------------- --- -------- --------- -----------------------
1729 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1730 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1731 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1732 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1733 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1734 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1735 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1736 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1737 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1738 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1739 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1740 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1741 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1742 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1744 [1] Only used by Rolling Thunder
1749 Address Dir Data Name Description
1750 ------------------- --- -------- --------- -----------------------
1751 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1752 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1753 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1754 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1755 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1756 0010 0--- --01 ---- n.c.
1757 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1758 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1759 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1760 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1761 1011 0--- ---- ---- W unknown (CUS41)
1762 1011 1--- ---- ---- W unknown (CUS41)
1763 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1768 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1769 in rthunder. The two 6809 in this game synchronize using a semaphore at
1770 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1771 and then increments 5606. While it does its quick things (which require
1772 about 40 clock cycles) it expects CPU2 to clear 5607.
1773 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1774 mode. I haven't investigated on the cause.
1776 - There are two watchdogs, one per CPU (or maybe three). Handling them
1777 separately is necessary to allow entering service mode without manually
1778 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1781 - The sprite hardware buffers spriteram: the program writes the sprite list to
1782 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1783 sprite RAM to signal the chip that the list is complete. The chip will copy
1784 the list from 4-9 to 10-15 and use it from there. This has not been verified
1785 on the real hardware, but it is the most logical way of doing it.
1786 Emulating this behaviour and not using an external buffer is important in
1787 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1788 is not written to. If we buffered spriteram to an external buffer, this would
1789 cause dangling sprites because the buffer would not be updated.
1791 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1792 entering a door. The *closed* door is made of tiles, but the *moving* door is
1793 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1794 there is one frame where neither the tile-based closed door nor the
1795 sprite-based moving door is shown, so it flickers. This behavior has been
1796 confirmed on a real PCB.
1800 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1801 but they don't seem to work as expected. During the first few frames they are
1802 written out of order and hooking them up in the usual way causes the MCU to
1803 stop receiving interrupts.