2 // Thunder: A Rolling Thunder Emulator w/6809 debugger
5 // (C) 2004, 2014 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 07/23/2009 Added changelog ;-)
12 // JLH 08/12/2009 Stabilized emulation so that it works
13 // JLH 04/04/2014 Converted to SDL 2
16 #define THUNDER_VERSION "1.0.0"
26 //#include <conio.h> // For getch()
27 #include <curses.h> // For getch()
29 #include <SDL2/SDL.h> // Get yer SDL out...!
39 #define ROM1 "rt3-1b.9c"
40 #define ROM2 "rt3-2b.12c"
41 #define ROM3 "rt3-3.12d"
42 #define ROM4 "rt1-4.6b"
43 #define ROM5 "rt1-5.4r"
44 #define ROM6 "rt1-6.4s"
45 #define ROM7 "rt1-7.7r"
46 #define ROM8 "rt1-8.7s"
47 #define ROM9 "rt1-9.12h"
48 #define ROM10 "rt1-10.12k"
49 #define ROM11 "rt1-11.12l"
50 #define ROM12 "rt1-12.12m"
51 #define ROM13 "rt1-13.12p"
52 #define ROM14 "rt1-14.12r"
53 #define ROM15 "rt1-15.12t"
54 #define ROM16 "rt1-16.12u"
55 #define ROM17 "rt1-17.f1"
56 #define ROM18 "rt1-18.h1"
57 #define ROM19 "rt1-19.k1"
58 #define ROM20 "rt1-20.m1"
59 #define ROM21 "rt1-21.f3"
60 #define ROM22 "rt1-22.h3"
61 #define PROM1 "mb7124e.3r"
62 #define PROM2 "mb7116e.3s"
63 #define PROM3 "mb7138h.4v"
64 #define PROM4 "mb7138h.6v"
65 #define PROM5 "mb7112e.6u"
66 #define MCUROM "rt1-mcu.bin"
73 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
74 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
75 uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
76 uint8_t chr_rom[0x60000]; // Character ROM pointer
80 bool trace1 = false; // ditto...
81 bool looking_at_rom = true; // true = R1, false = R2
82 uint32_t banksw1, banksw2; // Bank switch addresses
83 uint16_t game_over_switch; // Game over delay
84 uint16_t dpc; // Debug pc reg...
85 bool show_scr = true; // Whether or not to show background
86 bool enable_cpu = true; // Whether or not to enable CPUs
87 bool irqGoA = true; // IRQ switch for CPU #1
88 bool irqGoB = true; // IRQ switch for CPU #2
90 uint16_t refresh_ = 0; // Crappy global screen stuff...
93 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
94 uint8_t * psg_adrs[16];
95 uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
97 uint8_t * voc_adrs[32];
98 uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
99 uint8_t * fm_adrs[14];
101 fstream tr; // Tracelog hook
102 uint16_t pcx; // Where we at?
105 //static uint8_t keys[0x1000]; // SDL raw keyboard matrix
107 static char op_mat1[256] = {
108 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
109 0, 0, 5, 5, 0, 0, 4, 4, 0, 5, 8, 0, 8, 5, 6, 6,
110 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
111 7, 7, 7, 7, 6, 6, 6, 6, 0, 5, 5, 5, 8, 5, 5, 5,
112 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
113 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
114 7, 0, 0, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7,
115 2, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,
116 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 3, 9, 0,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
119 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
120 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 0, 9, 0,
121 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
122 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
123 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0,
134 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
135 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7,
136 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2,
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
151 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
152 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
153 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
158 static char mnemonics[256][6] = {
159 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
160 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
161 "PAGE1","PAGE2","NOP ","SYNC ","??? ","??? ","LBRA ","LBSR ",
162 "??? ","DAA ","ORCC ","??? ","ANDCC","SEX ","EXG ","TFR ",
163 "BRA ","BRN ","BHI ","BLS ","BHS ","BLO ","BNE ","BEQ ",
164 "BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
165 "LEAX ","LEAY ","LEAS ","LEAU ","PSHS ","PULS ","PSHU ","PULU ",
166 "??? ","RTS ","ABX ","RTI ","CWAI ","MUL ","RESET","SWI ",
167 "NEGA ","??? ","??? ","COMA ","LSRA ","??? ","RORA ","ASRA ",
168 "LSLA ","ROLA ","DECA ","??? ","INCA ","TSTA ","??? ","CLRA ",
169 "NEGB ","??? ","??? ","COMB ","LSRB ","??? ","RORB ","ASRB ",
170 "LSLB ","ROLB ","DECB ","??? ","INCB ","TSTB ","??? ","CLRB ",
171 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
172 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
173 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
174 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
175 "SUBA ","CMPA ","SCBA ","SUBD ","ANDA ","BITA ","LDA ","??? ",
176 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","BSR ","LDX ","??? ",
177 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
178 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
179 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
180 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
181 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
182 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
183 "SUBB ","CMPB ","SCBB ","ADDD ","ANDB ","BITB ","LDB ","??? ",
184 "EORB ","ADCB ","ORB ","ADDB ","LDD ","??? ","LDU ","??? ",
185 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
186 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
187 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
188 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
189 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
190 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU " },
191 mnemonics2[256][6] = {
192 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
193 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
194 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
195 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
196 "??? ","LBRN ","LBHI ","LBLS ","LBHS ","LBLO ","LBNE ","LBEQ ",
197 "LBVC ","LBVS ","LBPL ","LBMI ","LBGE ","LBLT ","LBGT ","LBLE ",
198 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
199 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI2 ",
200 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
201 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
202 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
203 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
204 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
205 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
206 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
207 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
208 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
209 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","??? ",
210 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
211 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
212 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
213 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
214 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
215 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
216 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
217 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","??? ",
218 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
219 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
220 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
221 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
222 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
223 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS " },
224 mnemonics3[256][6] = {
225 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
226 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
227 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
228 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
229 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
230 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
231 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
232 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI3 ",
233 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
234 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
235 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
236 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
237 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
238 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
239 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
240 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
241 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
242 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
243 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
244 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
245 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
246 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
247 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
248 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
249 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
250 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
251 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
252 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
253 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
254 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
255 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
256 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? " },
258 "D", "X", "Y", "U", "S", "PC", "??", "??",
259 "A", "B", "CC", "DP", "??", "??", "??", "??" },
260 iregs[4][2] = {"X", "Y", "U", "S" };
264 // Read a byte from memory (without touching PC. Not a Fetch!)
266 uint8_t RdMem(uint16_t addr)
270 // $4000-4300 is RAM shared with the microcontroller...
275 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
287 // Write a byte to memory
289 void WrMem(uint16_t addr, uint8_t b)
292 extern bool charbase; // Needed for screen. Extern it in it??
293 //extern uint16_t sr, ur, xr, yr; // Needed for tracelog
294 //extern uint16_t pcr;
295 /* if ((addr>0x40FF) && (addr<0x4390))
297 tr << hex << addr << ":" << (int)b;
298 //for(int i=0; i<32; i++)
300 // if (gram1[0x4400+i]<0x10) tr << "0";
301 // tr << hex << (uint16_t)gram1[0x4400+i] << " ";
308 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
313 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
315 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
317 banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
318 if (addr > 0x4284 && addr < 0x42A5 && b)
319 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
322 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
324 game_over_switch = 240; // Set game over delay...
326 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
329 charbase = false; // Char banksw1
331 charbase = true; // Char banksw2
332 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
334 if (refresh_++ == 1) // 30 Hz...
336 BlitChar(screen, chr_rom, gram1);
337 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
340 // IRQ Ack (may also be frame go...
341 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
344 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
351 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
353 uint8_t RdMemB(uint16_t addr)
355 // extern uint16_t cpu2.s, cpu2.u, cpu2.x, cpu2.y; // Needed for tracelog
361 b = gram1[addr + 0x4000];
362 if (addr > 0x1FFF && addr < 0x6000)
363 b = gram1[addr - 0x2000];
365 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
370 /* if ((addr>0x3FFF) && (addr<0x4400)) tr << "R-" << hex << pcx << ": "
372 << (int)looking_at_rom
374 << "] XR:" << xr << " YR:" << yr
375 << " SR:" << sr << " UR:" << ur
382 // Write a byte to memory (2nd processor)
384 void WrMemB(uint16_t addr, uint8_t b)
387 extern bool charbase;
388 //extern uint16_t sr, ur, xr, yr; // Needed for tracelog
389 //extern uint16_t pcr;
390 /* if ((addr>0x00FF) && (addr<0x0390))
392 tr << hex << addr << ":" << (int)b;
393 //for(int i=0; i<32; i++)
395 // if (gram1[0x4400+i]<0x10) tr << "0";
396 // tr << hex << (uint16_t)gram1[0x4400+i] << " ";
403 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
408 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
410 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
411 if (addr > 0x0284 && addr < 0x02A5 && b)
412 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
414 banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
417 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
419 game_over_switch = 240; // Set game over delay...
421 if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
424 gram1[addr + 0x4000] = b;
425 if (addr > 0x1FFF && addr < 0x6000)
426 gram1[addr - 0x2000] = b;
432 // IRQ Ack (may also be frame go...)
433 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
436 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
443 // Display bytes in mem in hex
445 void DisplayBytes(uint16_t src, unsigned long dst)
448 WriteLog("%04X: ", src);
451 dst += 0x10000; // That should fix the FFFF bug...
453 for(unsigned long i=src; i<dst; i++)
455 WriteLog("%02X ", (uint8_t)(looking_at_rom ? RdMem(i) : RdMemB(i)));
459 // Pad the leftover spaces...
460 for(unsigned long i=cnt; i<5; i++)
466 uint8_t Fetch(void) { return RdMem(dpc); }
467 uint16_t FetchW(void) { return (uint16_t)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
468 uint8_t FetchB(void) { return RdMemB(dpc); }
469 uint16_t FetchWB(void) { return (uint16_t)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
473 // Decode a 6809 instruction at 'addr'
477 uint8_t (* DFetch)(); // Decode Fetch() pointer...
478 uint16_t (* DFetchW)(); // Decode FetchW() pointer...
479 DFetch = (looking_at_rom ? Fetch : FetchB);
480 DFetchW = (looking_at_rom ? FetchW : FetchWB);
482 /* extern*/ uint16_t pcr, pcrB; // Pull in 'pcr' from '6809.cpp'
483 uint16_t pc_save = pcr, pcB_save = pcrB;
484 pcr = dpc; pcrB = dpc;
485 uint8_t opcode = DFetch(); // Get the opcode ('fetch' cycle)
486 uint8_t opcode2, operand;
488 uint8_t admode = op_mat1[opcode]; // addressing mode
489 char outbuf[80], mnem[6], tmp[30];
491 strcpy(mnem, mnemonics[opcode]); // Copy page 1 opcode
493 if (opcode == 0x10) // Extended opcode?
495 opcode2 = DFetch(); // Then get next byte
496 admode = op_mat2[opcode2]; // And use it as index into 'op_mat2'
497 strcpy(mnem, mnemonics2[opcode2]); // Overwrite mnemonic
500 if (opcode == 0x11) // Same as above...
503 admode = op_mat3[opcode2];
504 strcpy(mnem, mnemonics3[opcode2]); // Overwrite mnemonic
511 sprintf(outbuf, "???");
514 operand = DFetch(); // Get ZP address
515 sprintf(outbuf, "%s $%02X", mnem, operand);
518 loperand = DFetchW(); // Get ABS address
519 sprintf(outbuf, "%s $%04X", mnem, loperand);
523 operand = DFetch(); // Get offset
524 uint16_t tmpc = (looking_at_rom ? pcr : pcrB);
525 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16_t)(int8_t)operand);
528 case 4: // Long Relative
530 loperand = DFetchW(); // Get long offset
531 uint16_t tmpc = (looking_at_rom ? pcr : pcrB);
532 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16_t)loperand);
536 sprintf(outbuf, "%s ", mnem);
538 case 6: // Txfr/exchg/push/pull
540 operand = DFetch(); // Get txfr/exg/push/pull byte
542 if ((opcode == 0x1E) || (opcode == 0x1F)) // Is it TXF/EXG?
544 sprintf(tmp, "%s,%s", tregs[operand>>4], tregs[operand&0x0F]);
549 if (operand&0x01) strcat(tmp, "CC ");
550 if (operand&0x02) strcat(tmp, "A ");
551 if (operand&0x04) strcat(tmp, "B ");
552 if (operand&0x08) strcat(tmp, "DP ");
553 if (operand&0x10) strcat(tmp, "X ");
554 if (operand&0x20) strcat(tmp, "Y ");
555 if (operand&0x40) (((opcode==0x34)||(opcode==0x35))
556 ? strcat(tmp, "U ") : strcat(tmp, "S "));
557 if (operand&0x80) strcat(tmp, "PC");
559 sprintf(outbuf, "%s %s", mnem, tmp);
562 case 7: // Indexed (the tough one!)
564 operand = DFetch(); // Get IDX byte
565 uint8_t reg = ((operand & 0x60) >> 5), idxind = ((operand & 0x10) >> 4),
566 lo_nyb = (operand & 0x0F), boff;
571 if (!(operand & 0x80)) // Hi bit set? Then decode 4 bit offset
573 sprintf(tmp, "(%d),%s", (idxind ? -(16-lo_nyb) : lo_nyb),
576 else // Add the ($nnnn,R) code dude...
582 case 1: sprintf(tmp, "(,%s++)", iregs[reg]); break;
583 case 3: sprintf(tmp, "(,--%s)", iregs[reg]); break;
584 case 4: sprintf(tmp, "(,%s)", iregs[reg]); break;
585 case 5: sprintf(tmp, "(B,%s)", iregs[reg]); break;
586 case 6: sprintf(tmp, "(A,%s)", iregs[reg]); break;
588 { boff = DFetch(); sprintf(tmp, "($%02X,%s)", boff,
589 iregs[reg]); break; }
591 { woff = DFetchW(); sprintf(tmp, "($%04X,%s)", woff,
592 iregs[reg]); break; }
593 case 11: sprintf(tmp, "(D,%s)", iregs[reg]); break;
595 { boff = DFetch(); sprintf(tmp, "($%02X,PC)", boff); break; }
597 { woff = DFetchW(); sprintf(tmp, "($%04X,PC)", woff); break; }
599 { woff = DFetchW(); sprintf(tmp, "[$%04X]", woff); break; }
600 default: strcpy(tmp, "??");
607 case 0: sprintf(tmp, ",%s+", iregs[reg]); break;
608 case 1: sprintf(tmp, ",%s++", iregs[reg]); break;
609 case 2: sprintf(tmp, ",-%s", iregs[reg]); break;
610 case 3: sprintf(tmp, ",--%s", iregs[reg]); break;
611 case 4: sprintf(tmp, ",%s", iregs[reg]); break;
612 case 5: sprintf(tmp, "(B),%s", iregs[reg]); break;
613 case 6: sprintf(tmp, "(A),%s", iregs[reg]); break;
615 { boff = DFetch(); sprintf(tmp, "($%02X),%s", boff,
616 iregs[reg]); break; }
618 { woff = DFetchW(); sprintf(tmp, "($%04X),%s", woff,
619 iregs[reg]); break; }
620 case 11: sprintf(tmp, "(D),%s", iregs[reg]); break;
622 { boff = DFetch(); sprintf(tmp, "($%02X),PC", boff); break; }
624 { woff = DFetchW(); sprintf(tmp, "($%04X),PC", woff); break; }
625 default: strcpy(tmp, "??");
630 sprintf(outbuf, "%s %s", mnem, tmp);
634 operand = DFetch(); // Get IMM byte
635 sprintf(outbuf, "%s #$%02X", mnem, operand);
637 case 9: // Long Immediate
638 loperand = DFetchW(); // Get IMM word
639 sprintf(outbuf, "%s #$%04X", mnem, loperand);
643 DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB)); // Show bytes
645 WriteLog("\n"); // display opcode & addressing, etc
646 dpc = (looking_at_rom ? pcr : pcrB); // Advance debug PC
648 pcrB = pcB_save; // Restore PCs
653 // Convert hex to dec
655 uint16_t htod(char * str)
658 int len = strlen(str);
660 for(int i=0; i<len; i++)
662 if (str[i] >= '0' && str[i] <= '9')
663 value = (value << 4) | (unsigned)(str[i] - '0');
664 else if (str[i] >= 'a' && str[i] <= 'f')
665 value = (value << 4) | (unsigned)((str[i] - 'a') + 10);
666 else if (str[i] >= 'A' && str[i] <= 'F')
667 value = (value << 4) | (unsigned)((str[i] - 'A') + 10);
675 // Load 32K file into ROM image space
677 bool Load32KImg(char * filename, uint16_t address)
683 ff.open(filename, ios::binary | ios::in);
688 for(long i=0; i<32768; i++)
691 grom[address+i] = ch;
699 FILE * file = fopen(filename, "rb");
704 fread(&grom[address], 1, 0x8000, file);
712 // Generic Load file into image space
713 // (No error checking performed! Responsibility of caller!)
715 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
721 strcpy(path, "./ROMs/");
722 strcat(path, filename);
723 // ff.open(filename, ios::binary | ios::in); // Open 'da file...
724 ff.open(path, ios::binary | ios::in); // Open 'da file...
728 for(uint32_t i=0; i<length; i++) // Read it in...
734 ff.close(); // Close 'da file...
744 bool ReadColorPROMs(void)
749 // extern uint8_t palette[768]; // Screen physical palette
750 extern uint32_t palette[256]; // Screen physical palette
751 extern uint8_t ccolor[256][8]; // Character color PROM values
752 extern uint8_t scolor[128][16]; // Sprite color PROM values
754 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
758 for(int i=0; i<256; i++) // Load char pallete with PROM values
760 for(int j=0; j<8; j++)
763 ccolor[i][j] = (uint8_t)ch;
770 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
774 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
776 for(int j=0; j<16; j++)
779 scolor[i][j] = (uint8_t)ch;
786 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
787 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
789 // If open was successful...
792 // Palette is 12-bit RGB, we stretch it to 24-bit
793 for(int i=0; i<256; i++)
799 r = (uint8_t)c1 & 0x0F;
800 g = (uint8_t)c1 >> 4;
802 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
809 // PROM5 has the following in it (tile address decoder):
810 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
811 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
820 bool UnpackFonts(void)
822 // uint8_t b1, b2, b3;
826 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
827 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
830 return false; // Return if not found...
832 for(long i=0; i<0x40000; i+=64)
834 for(int j=0; j<64; j+=8)
836 f1.get(b1); f1.get(b2); f2.get(b3);
837 b3 ^= 0xFF; // Invert top data...
838 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
839 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
840 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
841 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
842 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
843 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
844 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
845 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
852 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
853 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
855 for(long i=0x40000; i<0x60000; i+=64)
857 for(int j=0; j<64; j+=8)
859 f1.get(b1); f1.get(b2); f2.get(b3);
860 b3 ^= 0xFF; // Invert top data
861 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
862 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
863 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
864 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
865 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
866 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
867 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
868 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
875 return true; // Made it!
880 // Get length of sample from WAV format
882 uint32_t GetWAVLength(fstream & file)
887 file.ignore(16); // Skip header BS
889 for(int i=0; i<2; i++)
891 file.get(ch); len = (int)(uint8_t)ch;
892 file.get(ch); len |= (int)(uint8_t)ch << 8;
893 file.get(ch); len |= (int)(uint8_t)ch << 16;
894 file.get(ch); len |= (int)(uint8_t)ch << 24;
896 file.ignore(len + 4); // Skip intermediate data
899 file.get(ch); len = (int)(uint8_t)ch; // & finally get length of data
900 file.get(ch); len |= (int)(uint8_t)ch << 8;
901 file.get(ch); len |= (int)(uint8_t)ch << 16;
902 file.get(ch); len |= (int)(uint8_t)ch << 24;
909 // Load PSG samples from disk
917 for(int i=0; i<16; i++)
921 psg_adrs[i] = NULL; // Zero out pointer
922 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
924 fp.open(file, ios::binary | ios::in); // Attempt to open it...
928 len = GetWAVLength(fp); // Get WAV data length...
929 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
931 if (psg_adrs[i] != NULL)
933 for(int j=0; j<(signed)len; j++)
936 psg_adrs[i][j] = ch; // & load it in...
940 // cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
941 printf("Found sample file: %s\t[Length: %u]\n", file, len);
951 // Load FM samples from disk
959 for(int i=0; i<14; i++)
963 fm_adrs[i] = NULL; // Zero out pointer
964 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
966 fp.open(file, ios::binary | ios::in); // Attempt to open it...
970 len = GetWAVLength(fp); // Get WAV length...
971 fm_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
973 if (fm_adrs[i] != NULL)
975 for(int j=0; j<(signed)len; j++)
978 fm_adrs[i][j] = ch; // & load it in...
982 // cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
983 printf("Found sample file: %s\t[Length: %u]\n", file, len);
995 int main(int argc, char * argv[])
997 InitLog("thunder.log");
999 extern bool disasm; // From 'V6809.CPP'
1000 extern bool charbase; // From 'SCREEN.CPP'
1004 fstream ff; // Declare fstream without file hooks...
1005 bool brk = false, brk2 = false; // Breakpoint set flag
1006 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
1007 bool running; // CPU running state flag...
1008 bool self_test = false; // Self-test switch
1009 bool scr_type = false; // false=chars, true=pixels
1010 uint16_t debounce = 0; // Key de-bounce counter
1011 uint16_t fire_debounce = 0; // Fire button debounce counter
1012 // bool refresh2 = true; // Default to 60 Hz...
1013 uint8_t x; // General placeholder...
1014 bool active = true; // Program running flag
1016 SDL_Event event; // SDL "event"
1017 extern uint8_t palette[768]; // Screen physical palette
1018 uint32_t ticks, oldTicks;
1020 cout << endl << "THUNDER v"THUNDER_VERSION" ";
1021 cout << "by James Hammons" << endl;
1022 cout << "Serial #20149404 / Prerelease" << endl;
1023 cout << "© 2003, 2014 Underground Software" << endl << endl;
1025 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
1029 cout << "Initializing SDL..." << endl;
1031 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
1033 cout << "Couldn't initialize SDL: " << SDL_GetError() << endl;
1038 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
1040 // keys = SDL_GetKeyState(NULL); // Get the SDL keyboard matrix
1042 gram = gram1; grom = grom1; // Needed only for debugger
1045 for(long i=0; i<0x10000; i++)
1047 gram[i] = 0; grom[i] = 0; // Zero out memory
1048 gram2[i] = 0; grom2[i] = 0;
1051 memset(gram, 0, 0x10000);
1052 memset(grom, 0, 0x10000);
1053 memset(gram2, 0, 0x10000);
1054 memset(grom2, 0, 0x10000);
1057 game_over_switch = 0; // Init game over delay
1058 // 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;
1060 cout << "Loading ROMs..." << endl;
1061 // LoadCMOS(); // Load CMOS at $CC00-$CFFF
1062 if (!ReadColorPROMs()) // Load virtual PROMs
1063 { cout << "Could not open PROM files!" << endl; return -1; }
1065 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
1066 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
1068 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
1069 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
1071 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
1072 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
1074 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
1075 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
1077 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
1078 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
1080 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
1081 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
1083 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
1084 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
1086 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
1087 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
1089 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
1090 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
1092 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
1093 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
1095 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
1096 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
1098 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
1099 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
1101 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
1102 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
1104 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
1105 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
1107 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
1108 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
1110 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
1111 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
1113 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
1114 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
1116 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
1117 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
1119 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
1121 cout << "Could not open font files!" << endl;
1125 // Load samples if they're there...
1129 // Quick 'n' Dirty voice dump (sound 0x0E)
1130 /* uint32_t adc = (voice_rom[26]<<8) | voice_rom[27];
1131 bool doneWitIt = false;
1135 if (voice_rom[adc] < 0x10) tr << "0";
1136 tr << hex << (int)voice_rom[adc] << " ";
1137 if (crh++ > 24) { crh = 0; tr << endl; }
1138 if ((voice_rom[adc] == 0xFF) && (voice_rom[adc-1] != 0x00))
1143 // Set up V6809 execution contexts
1145 memset(&cpu1, 0, sizeof(V6809REGS));
1148 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1150 memset(&cpu2, 0, sizeof(V6809REGS));
1151 cpu2.RdMem = RdMemB;
1152 cpu2.WrMem = WrMemB;
1153 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1155 bool firstTime = true; // kludge...
1157 WriteLog("About to go to the main loop...\n");
1163 firstTime = false; // crappy kludge...
1170 if (lbuff[0] == 'd')
1177 printf("%04X: ", dpc);
1178 uint16_t pc_save = cpu1.pc, pcB_save = cpu2.pc;
1179 cpu1.pc = dpc; cpu2.pc = dpc;
1180 for(int i=0; i<16; i++)
1181 printf("%02X ", (looking_at_rom ? Fetch() : FetchB()));
1183 cpu1.pc = dpc; cpu2.pc = dpc;
1184 for(int i=0; i<16; i++)
1186 uint8_t a = (looking_at_rom ? Fetch() : FetchB());
1187 if (a<10) cout << (char)(a+48);
1188 if ((a>9) && (a<37)) cout << (char)(a+55);
1189 if (a>36) cout << ".";
1192 dpc = (looking_at_rom ? cpu1.pc : cpu2.pc);
1193 cpu1.pc = pc_save; cpu2.pc = pcB_save;
1195 else if (lbuff[0] == 'e')
1202 printf("%04X: ", dpc);
1203 for(int i=0; i<16; i++) printf("%02X ", (uint8_t)gram[dpc++]);
1206 else if (lbuff[0] == 'l')
1213 for(int i=0; i<23; i++)
1216 else if (lbuff[0] == 't')
1227 Execute6809(&cpu1, 1);
1229 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1230 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1231 cout << " iclock=" << cpu1.clock << endl;
1237 Execute6809(&cpu2, 1);
1239 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1240 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1241 cout << " iclock=" << cpu2.clock << endl;
1244 else if ((lbuff[0] == 'r') || (lbuff[0] == 'c')) // Run/continue...
1246 WriteLog("Executing 'run' command...\n");
1247 uint32_t my_clock = 0;
1248 running = true; // Set running status...
1250 SetRefreshRate(refresh2); // Tell GUI our refresh rate
1251 //for(uint16_t i=0; i<0x8000; i++) gram2[i] = grom3[i]; //Temp
1253 if (lbuff[0] == 'r') // If run, then reset CPUs
1255 WriteLog("Executing secondary 'run' command...\n");
1257 // This is data that is supposed to come from the MCU... So that's why it hangs
1258 gram1[0x4182] = 0xA6; // Temp kludge
1259 gram1[0x4184] = 0xA6;
1260 gram1[0x4183] = 0x00; // More of the same
1261 gram1[0x4185] = 0x00;
1263 banksw1 = 0; // Will this work?
1265 // iclock = 0; // Reset instr clock #1...
1266 InitGUI(); // Reset # of coins
1269 cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
1272 lbuff[0] = 32; cpu1.pc = htod(lbuff);
1274 else cpu1.cc = 0xFF; // Set CC register
1276 cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
1277 cpu2.cc = 0xFF; // Set CC register
1278 while(iclock < 8000) // was 17000, 20000, 5000
1280 Execute6809(&cpu1, 1); Execute6809(&cpu2, 1);
1284 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
1285 // Will *this* help video sync? NO
1286 while (cpu1.clock < 8000) // was 17000, 20000, 5000
1288 Execute6809(&cpu1, 1);
1289 Execute6809(&cpu2, 1);
1294 WriteLog("About to set up screen...\n");
1296 screen = SDL_SetVideoMode(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 8, SDL_SWSURFACE | SDL_DOUBLEBUF);
1299 cout << "Failed to initialize screen!" << endl;
1307 SDL_Color colors[256];
1309 for(int i=0; i<256; i++)
1311 colors[i].r = palette[i*3+0];
1312 colors[i].g = palette[i*3+1];
1313 colors[i].b = palette[i*3+2];
1316 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1320 for(int i=0; i<256; i++)
1321 keys[i] = 0; // Clear keyboard buffer...
1324 oldTicks = SDL_GetTicks();
1326 WriteLog("About to set up audio...\n");
1328 // This crap SHOULD be in sound.cpp (not yet created)...
1329 SDL_AudioSpec desired, obtained;
1330 desired.freq = 22050;
1331 desired.format = AUDIO_U8;
1332 desired.channels = 1;
1333 desired.samples = 600;
1334 desired.callback = SoundFunc;
1335 desired.userdata = NULL;
1336 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1337 if (SDL_OpenAudio(&desired, &obtained) < 0)
1339 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1343 SDL_PauseAudio(0); // Get that audio going!
1346 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
1347 RenderScreenBuffer();
1348 WriteLog("About to enter main loop...\n");
1351 HandleGUIDebounce(); // Debounce GUI keys
1353 if (game_over_switch)
1355 game_over_switch--; // Countdown...
1357 if (game_over_switch == 0)
1358 gram1[0x4380] = 0; // Kill music!
1361 //testing... (works)
1362 //gram1[0x423D] = 1;
1363 //gram1[0x423D] = self_test; // Reset DSW1-1
1364 gram1[0x4268] = 0; // Reset Video test
1365 gram1[0x427A] = 0; gram1[0x427C] = 0;
1366 gram1[0x427B] = 0; gram1[0x427D] = 0;
1367 gram1[0x427E] = 0; gram1[0x427F] = 0;
1368 gram1[0x4280] = 0; gram1[0x4281] = 0;
1369 gram1[0x4276] = 0; gram1[0x426A] = 0;
1370 gram1[0x4278] = 0; gram1[0x426C] = 0;
1371 gram1[0x4262] = 0; gram1[0x4260] = 0;
1372 //gram1[0x4247] = 0;
1374 // SDL key handling...
1376 // SDL_PumpEvents(); // Force key events into the buffer.
1379 while (SDL_PollEvent(&event))
1384 if (event.key.keysym.sym == SDLK_ESCAPE)
1390 if (keys[SDLK_ESCAPE])
1391 running = false; // ESC to exit...
1394 debounce--; // Debounce toggle keys...
1399 self_test = !self_test; // Self-test (F1-toggle)
1400 debounce = 10; // Key debounce value...
1404 gram1[0x4268] = 1; // Video test (F2)
1405 debounce = 10; // Key debounce value...
1409 scr_type = !scr_type; // Toggle screen (F12)
1410 debounce = 10; // Key debounce value...
1414 show_scr = !show_scr; // Toggle bkgrnd (F3)
1419 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1424 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1425 SetRefreshRate(refresh2); // Inform GUI of refresh
1430 debounce = 10; // Key debounce value...
1432 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1434 SpawnSound(USERSOUND, SCAMERA);
1438 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1448 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1449 if (keys[SDLK_RIGHT]) // Right arrow
1452 SelectRight(); // If GUI active...
1455 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1456 gram1[0x427F] = 1; // Stick right
1459 if (keys[SDLK_LEFT])
1462 SelectLeft(); // If GUI active...
1465 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1466 gram1[0x4281] = 1; // Left arrow
1472 SelectUp(); // If GUI active...
1475 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1476 gram1[0x427B] = 1; // Up arrow
1479 if (keys[SDLK_DOWN])
1482 SelectDown(); // If GUI active...
1485 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1486 gram1[0x427D] = 1; // Down arrow
1489 if (keys[SDLK_RETURN]) // Return
1491 uint8_t retval = UserSelectedSomething();
1496 if (retval == REFRESH)
1498 refresh2 = !refresh2;
1499 SetRefreshRate(refresh2);
1504 gram1[0x427A] = 1; // (1)
1507 gram1[0x427C] = 1; // (2)
1510 gram1[0x427E] = 1; // (3)
1513 gram1[0x4280] = 1; // (5)
1515 if (keys[SDLK_q] | keys[29])
1516 gram1[0x4276] = 1; // (Q) Jump
1519 gram1[0x426A] = 1; // (W)
1524 if (keys[SDLK_e] | keys[56]) // (E) Fire
1530 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1538 gram1[0x426C] = 1; // (R)
1541 gram1[0x4262] = 1; // (T)
1544 gram1[0x4260] = 1; // (Y)
1547 gram1[0x41A5]++; // Coin? (F10)
1550 gram1[0x4189]++; // ? (Z) credits l dig
1553 gram1[0x418A]++; // ? (X) credits r dig
1556 gram1[0x418C]++; // ? (C) Start
1559 gram1[0x418D]++; // ? (V)
1562 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1564 // if (keys[SDLK_F8])
1566 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1567 // charbase = false; // Switch chars out...
1569 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1571 if (keys[SDLK_F11]) // (F11)
1573 Execute6809(&cpu1, 10);
1574 Execute6809(&cpu2, 10);
1577 //F12 is used above, but the values are ignored. So we'll do it here too.
1580 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1581 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1584 if (keys[SDLK_d]) // (D) start disassembly
1588 gram1[0x5606] = 0x00;
1591 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1592 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1596 gram1[0x5FF3] = 0x02;
1597 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1605 // We can do this here because we're not executing the cores yet.
1606 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1607 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1608 // while (cpu1.clock < 25000)
1609 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1610 // 25600 cycles/frame
1611 // Setting interleave to 25 and below causes the V6809 core to hang...
1612 // 32 gets to the title screen before hanging...
1613 // 40 works, until it doesn't... :-P
1616 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1617 for(uint32_t i=0; i<640; i++)
1618 // for(uint32_t i=0; i<1280; i++)
1620 // Gay, but what are ya gonna do?
1621 // There's better ways, such as keeping track of when slave writes to master, etc...
1622 Execute6809(&cpu1, 40);
1623 Execute6809(&cpu2, 40);
1625 } // END: enable_cpu
1627 // if (refresh_++ == 1) // 30 Hz...
1630 // BlitWilliamsScreen(gram1); // Display the screen...
1632 // BlitChar(screen, chr_rom, gram1);
1633 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1637 //temp, for testing...
1638 BlitChar(screen, chr_rom, gram1);
1640 // Speed throttling happens here...
1641 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1642 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1643 SDL_Delay(1); // Release our timeslice...
1645 oldTicks = SDL_GetTicks();
1646 //cout << "Finished frame..." << endl;
1649 // Stop_audio_output();
1651 // ReleaseKeyboard(); // Release the interrupt...
1652 // RestoreOldMode(); // Restore screen
1653 if (brk && (cpu1.pc == brkpnt))
1654 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1655 if (brk2 && (cpu2.pc == brkpnt2))
1656 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1658 lbuff[0] = 'q'; // Temp kludge...
1660 else if (lbuff[0] == 'b') // Set/clear breakpoint
1665 brkpnt = htod(lbuff);
1667 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1672 cout << "Breakpoint cleared" << endl;
1675 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1680 brkpnt2 = htod(lbuff);
1682 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1687 cout << "Breakpoint cleared" << endl;
1690 else if (lbuff[0] == 'i') // Inspect registers
1692 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1693 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1694 cout << " iclk=" << dec << cpu1.clock << endl;
1695 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1696 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1697 cout << " iclk=" << dec << cpu2.clock << endl;
1700 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1703 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1705 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1707 looking_at_rom = !looking_at_rom;
1708 cout << "Swapped: Looking at ";
1709 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1712 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1717 for(int i=0; i<4; i++)
1726 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1729 printf("%04X: ", dpc); // Show data found...
1731 for(int i=0; i<16; i++)
1732 printf("%02X ", gram1[(uint16_t)(dpc+i)]);
1736 for(int i=0; i<16; i++)
1738 uint8_t a = gram1[dpc++];
1741 cout << (char)(a+48);
1742 if ((a>9) && (a<37))
1743 cout << (char)(a+55);
1750 else if (lbuff[0] == 'v') // View screen
1752 BlitChar(screen, chr_rom, gram1);
1756 if (lbuff[0] == 'q')
1757 active = false; //break; // Quit
1760 SDL_Quit(); // Shut down SDL
1762 for(int i=0; i<16; i++)
1764 delete[] psg_adrs[i]; // Deallocate if loaded
1766 for(int i=0; i<14; i++)
1768 delete[] fm_adrs[i]; // Deallocate if loaded
1776 Hitachi uC runs at 6.144 MHz
1777 YM2151 runs at 3.579580 MHz
1780 Rolling Thunder Memory map
1781 --------------------------
1782 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1783 map is inferred by program behaviour. The customs also handle internally irq
1786 The main CPU memory map is the same in all games because CUS47 is used by all
1787 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1788 replaced by other chips.
1790 All RAM is shared between main and sub CPU, except for sound RAM which is
1791 shared between main and sound CPU; the portion of object RAM that is overlapped
1792 by sound RAM is used exclusively by the sub CPU.
1796 Address Dir Data Name Description
1797 ------------------- --- -------- --------- -----------------------
1798 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1799 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1800 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1801 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1802 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1803 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1804 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1805 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1806 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1807 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1808 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1809 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1810 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1811 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1812 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1813 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1814 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1815 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1816 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1818 [1] Note that this is partially overlapped by sound RAM
1819 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1824 Address Dir Data Name Description
1825 ------------------- --- -------- --------- -----------------------
1826 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1827 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1828 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1829 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1830 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1831 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1832 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1833 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1834 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1835 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1836 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1837 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1838 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1839 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1841 [1] Only used by Rolling Thunder
1846 Address Dir Data Name Description
1847 ------------------- --- -------- --------- -----------------------
1848 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1849 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1850 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1851 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1852 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1853 0010 0--- --01 ---- n.c.
1854 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1855 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1856 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1857 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1858 1011 0--- ---- ---- W unknown (CUS41)
1859 1011 1--- ---- ---- W unknown (CUS41)
1860 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1865 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1866 in rthunder. The two 6809 in this game synchronize using a semaphore at
1867 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1868 and then increments 5606. While it does its quick things (which require
1869 about 40 clock cycles) it expects CPU2 to clear 5607.
1870 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1871 mode. I haven't investigated on the cause.
1873 - There are two watchdogs, one per CPU (or maybe three). Handling them
1874 separately is necessary to allow entering service mode without manually
1875 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1878 - The sprite hardware buffers spriteram: the program writes the sprite list to
1879 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1880 sprite RAM to signal the chip that the list is complete. The chip will copy
1881 the list from 4-9 to 10-15 and use it from there. This has not been verified
1882 on the real hardware, but it is the most logical way of doing it.
1883 Emulating this behaviour and not using an external buffer is important in
1884 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1885 is not written to. If we buffered spriteram to an external buffer, this would
1886 cause dangling sprites because the buffer would not be updated.
1888 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1889 entering a door. The *closed* door is made of tiles, but the *moving* door is
1890 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1891 there is one frame where neither the tile-based closed door nor the
1892 sprite-based moving door is shown, so it flickers. This behavior has been
1893 confirmed on a real PCB.
1897 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1898 but they don't seem to work as expected. During the first few frames they are
1899 written out of order and hooking them up in the usual way causes the MCU to
1900 stop receiving interrupts.