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);
1246 screen = SDL_SetVideoMode(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 8, SDL_SWSURFACE | SDL_DOUBLEBUF);
1249 cout << "Failed to initialize screen!" << endl;
1253 SDL_Color colors[256];
1254 for(int i=0; i<256; i++)
1256 colors[i].r = palette[i*3+0];
1257 colors[i].g = palette[i*3+1];
1258 colors[i].b = palette[i*3+2];
1260 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1263 // This confirms that we're getting video to the screen...
1264 SDL_LockSurface(screen);
1267 uint8 * pixels = (uint8 *)(screen->pixels);
1269 for(uint32 y=0; y<480; y++)
1270 for(uint32 x=0; x<640; x++)
1271 pixels[(y * 640) + x] = pixel++;
1273 SDL_UnlockSurface(screen);
1274 SDL_UpdateRect(screen, 0, 0, 0, 0);
1277 for(int i=0; i<256; i++)
1278 keys[i] = 0; // Clear keyboard buffer...
1280 oldTicks = SDL_GetTicks();
1282 WriteLog("About to set up audio...\n");
1283 // This crap SHOULD be in sound.cpp (not yet created)...
1284 SDL_AudioSpec desired, obtained;
1285 desired.freq = 22050;
1286 desired.format = AUDIO_U8;
1287 desired.channels = 1;
1288 desired.samples = 600;
1289 desired.callback = SoundFunc;
1290 desired.userdata = NULL;
1291 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1292 if (SDL_OpenAudio(&desired, &obtained) < 0)
1294 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1297 SDL_PauseAudio(0); // Get that audio going!
1299 WriteLog("About to enter main loop...\n");
1302 HandleGUIDebounce(); // Debounce GUI keys
1303 if (game_over_switch)
1305 game_over_switch--; // Countdown...
1306 if (game_over_switch == 0)
1307 gram1[0x4380] = 0; // Kill music!
1309 //testing... (works)
1310 //gram1[0x423D] = 1;
1311 //gram1[0x423D] = self_test; // Reset DSW1-1
1312 gram1[0x4268] = 0; // Reset Video test
1313 gram1[0x427A] = 0; gram1[0x427C] = 0;
1314 gram1[0x427B] = 0; gram1[0x427D] = 0;
1315 gram1[0x427E] = 0; gram1[0x427F] = 0;
1316 gram1[0x4280] = 0; gram1[0x4281] = 0;
1317 gram1[0x4276] = 0; gram1[0x426A] = 0;
1318 gram1[0x4278] = 0; gram1[0x426C] = 0;
1319 gram1[0x4262] = 0; gram1[0x4260] = 0;
1320 //gram1[0x4247] = 0;
1322 // SDL key handling...
1324 SDL_PumpEvents(); // Force key events into the buffer.
1326 if (keys[SDLK_ESCAPE])
1327 running = false; // ESC to exit...
1330 debounce--; // Debounce toggle keys...
1335 self_test = !self_test; // Self-test (F1-toggle)
1336 debounce = 10; // Key debounce value...
1340 gram1[0x4268] = 1; // Video test (F2)
1341 debounce = 10; // Key debounce value...
1345 scr_type = !scr_type; // Toggle screen (F12)
1346 debounce = 10; // Key debounce value...
1350 show_scr = !show_scr; // Toggle bkgrnd (F3)
1355 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1360 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1361 SetRefreshRate(refresh2); // Inform GUI of refresh
1366 debounce = 10; // Key debounce value...
1368 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1370 SpawnSound(USERSOUND, SCAMERA);
1374 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1383 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1384 if (keys[SDLK_RIGHT]) // Right arrow
1387 SelectRight(); // If GUI active...
1390 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1391 gram1[0x427F] = 1; // Stick right
1394 if (keys[SDLK_LEFT])
1397 SelectLeft(); // If GUI active...
1400 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1401 gram1[0x4281] = 1; // Left arrow
1407 SelectUp(); // If GUI active...
1410 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1411 gram1[0x427B] = 1; // Up arrow
1414 if (keys[SDLK_DOWN])
1417 SelectDown(); // If GUI active...
1420 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1421 gram1[0x427D] = 1; // Down arrow
1424 if (keys[SDLK_RETURN]) // Return
1426 uint8 retval = UserSelectedSomething();
1429 if (retval == REFRESH)
1431 refresh2 = !refresh2;
1432 SetRefreshRate(refresh2);
1436 gram1[0x427A] = 1; // (1)
1438 gram1[0x427C] = 1; // (2)
1440 gram1[0x427E] = 1; // (3)
1442 gram1[0x4280] = 1; // (5)
1443 if (keys[SDLK_q] | keys[29])
1444 gram1[0x4276] = 1; // (Q) Jump
1446 gram1[0x426A] = 1; // (W)
1449 if (keys[SDLK_e] | keys[56]) // (E) Fire
1455 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1462 gram1[0x426C] = 1; // (R)
1464 gram1[0x4262] = 1; // (T)
1466 gram1[0x4260] = 1; // (Y)
1468 gram1[0x41A5]++; // Coin? (F10)
1470 gram1[0x4189]++; // ? (Z) credits l dig
1472 gram1[0x418A]++; // ? (X) credits r dig
1474 gram1[0x418C]++; // ? (C) Start
1476 gram1[0x418D]++; // ? (V)
1478 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1479 // if (keys[SDLK_F8])
1481 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1482 // charbase = false; // Switch chars out...
1484 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1485 if (keys[SDLK_F11]) // (F11)
1487 Execute6809(&cpu1, 10);
1488 Execute6809(&cpu2, 10);
1491 //F12 is used above, but the values are ignored. So we'll do it here too.
1494 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1495 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1497 if (keys[SDLK_d]) // (D) start disassembly
1501 gram1[0x5606] = 0x00;
1504 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1505 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1509 gram1[0x5FF3] = 0x02;
1510 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1518 // We can do this here because we're not executing the cores yet.
1519 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1520 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1521 // while (cpu1.clock < 25000)
1522 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1523 // 25600 cycles/frame
1524 // Setting interleave to 25 and below causes the V6809 core to hang...
1525 // 32 gets to the title screen before hanging...
1526 // 40 works, until it doesn't... :-P
1529 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1530 for(uint32 i=0; i<640; i++)
1531 // for(uint32 i=0; i<1280; i++)
1533 // Gay, but what are ya gonna do?
1534 // There's better ways, such as keeping track of when slave writes to master, etc...
1535 Execute6809(&cpu1, 40);
1536 Execute6809(&cpu2, 40);
1538 } // END: enable_cpu
1540 // if (refresh_++ == 1) // 30 Hz...
1543 // BlitWilliamsScreen(gram1); // Display the screen...
1545 // BlitChar(screen, chr_rom, gram1);
1546 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1550 //temp, for testing...
1551 BlitChar(screen, chr_rom, gram1);
1553 // Speed throttling happens here...
1554 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1555 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1556 SDL_Delay(1); // Release our timeslice...
1558 oldTicks = SDL_GetTicks();
1559 //cout << "Finished frame..." << endl;
1562 // Stop_audio_output();
1564 // ReleaseKeyboard(); // Release the interrupt...
1565 // RestoreOldMode(); // Restore screen
1566 if (brk && (cpu1.pc == brkpnt))
1567 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1568 if (brk2 && (cpu2.pc == brkpnt2))
1569 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1571 lbuff[0] = 'q'; // Temp kludge...
1573 else if (lbuff[0] == 'b') // Set/clear breakpoint
1578 brkpnt = htod(lbuff);
1580 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1585 cout << "Breakpoint cleared" << endl;
1588 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1593 brkpnt2 = htod(lbuff);
1595 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1600 cout << "Breakpoint cleared" << endl;
1603 else if (lbuff[0] == 'i') // Inspect registers
1605 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1606 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1607 cout << " iclk=" << dec << cpu1.clock << endl;
1608 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1609 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1610 cout << " iclk=" << dec << cpu2.clock << endl;
1612 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1614 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1616 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1618 looking_at_rom = !looking_at_rom;
1619 cout << "Swapped: Looking at ";
1620 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1623 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1627 for(int i=0; i<4; i++)
1635 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1638 printf("%04X: ", dpc); // Show data found...
1639 for(int i=0; i<16; i++)
1640 printf("%02X ", gram1[(uint16)(dpc+i)]);
1642 for(int i=0; i<16; i++)
1644 uint8 a = gram1[dpc++];
1646 cout << (char)(a+48);
1647 if ((a>9) && (a<37))
1648 cout << (char)(a+55);
1654 else if (lbuff[0] == 'v') // View screen
1656 // SetVESA2(); // Set up screen
1657 BlitChar(screen, chr_rom, gram1);
1659 // RestoreOldMode();
1662 if (lbuff[0] == 'q')
1663 active = false; //break; // Quit
1666 SDL_Quit(); // Shut down SDL
1668 for(int i=0; i<16; i++)
1670 delete[] psg_adrs[i]; // Deallocate if loaded
1672 for(int i=0; i<14; i++)
1674 delete[] fm_adrs[i]; // Deallocate if loaded
1682 Hitachi uC runs at 6.144 MHz
1683 YM2151 runs at 3.579580 MHz
1686 Rolling Thunder Memory map
1687 --------------------------
1688 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1689 map is inferred by program behaviour. The customs also handle internally irq
1691 The main CPU memory map is the same in all games because CUS47 is used by all
1692 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1693 replaced by other chips.
1694 All RAM is shared between main and sub CPU, except for sound RAM which is shared
1695 between main and sound CPU; the portion of object RAM that is overlapped by sound
1696 RAM is used exclusively by the sub CPU.
1700 Address Dir Data Name Description
1701 ------------------- --- -------- --------- -----------------------
1702 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1703 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1704 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1705 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1706 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1707 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1708 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1709 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1710 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1711 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1712 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1713 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1714 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1715 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1716 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1717 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1718 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1719 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1720 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1722 [1] Note that this is partially overlapped by sound RAM
1723 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1728 Address Dir Data Name Description
1729 ------------------- --- -------- --------- -----------------------
1730 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1731 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1732 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1733 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1734 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1735 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1736 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1737 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1738 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1739 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1740 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1741 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1742 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1743 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1745 [1] Only used by Rolling Thunder
1750 Address Dir Data Name Description
1751 ------------------- --- -------- --------- -----------------------
1752 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1753 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1754 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1755 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1756 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1757 0010 0--- --01 ---- n.c.
1758 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1759 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1760 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1761 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1762 1011 0--- ---- ---- W unknown (CUS41)
1763 1011 1--- ---- ---- W unknown (CUS41)
1764 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1769 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1770 in rthunder. The two 6809 in this game synchronize using a semaphore at
1771 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1772 and then increments 5606. While it does its quick things (which require
1773 about 40 clock cycles) it expects CPU2 to clear 5607.
1774 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1775 mode. I haven't investigated on the cause.
1777 - There are two watchdogs, one per CPU (or maybe three). Handling them
1778 separately is necessary to allow entering service mode without manually
1779 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1782 - The sprite hardware buffers spriteram: the program writes the sprite list to
1783 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1784 sprite RAM to signal the chip that the list is complete. The chip will copy
1785 the list from 4-9 to 10-15 and use it from there. This has not been verified
1786 on the real hardware, but it is the most logical way of doing it.
1787 Emulating this behaviour and not using an external buffer is important in
1788 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1789 is not written to. If we buffered spriteram to an external buffer, this would
1790 cause dangling sprites because the buffer would not be updated.
1792 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1793 entering a door. The *closed* door is made of tiles, but the *moving* door is
1794 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1795 there is one frame where neither the tile-based closed door nor the
1796 sprite-based moving door is shown, so it flickers. This behavior has been
1797 confirmed on a real PCB.
1801 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1802 but they don't seem to work as expected. During the first few frames they are
1803 written out of order and hooking them up in the usual way causes the MCU to
1804 stop receiving interrupts.