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
15 #define THUNDER_VERSION "1.0.0"
25 //#include <conio.h> // For getch()
26 #include <curses.h> // For getch()
28 #include "SDL.h" // Get yer SDL out...!
34 using namespace std; // Yes!
37 #define ROM1 "rt3-1b.9c"
38 #define ROM2 "rt3-2b.12c"
39 #define ROM3 "rt3-3.12d"
40 #define ROM4 "rt1-4.6b"
41 #define ROM5 "rt1-5.4r"
42 #define ROM6 "rt1-6.4s"
43 #define ROM7 "rt1-7.7r"
44 #define ROM8 "rt1-8.7s"
45 #define ROM9 "rt1-9.12h"
46 #define ROM10 "rt1-10.12k"
47 #define ROM11 "rt1-11.12l"
48 #define ROM12 "rt1-12.12m"
49 #define ROM13 "rt1-13.12p"
50 #define ROM14 "rt1-14.12r"
51 #define ROM15 "rt1-15.12t"
52 #define ROM16 "rt1-16.12u"
53 #define ROM17 "rt1-17.f1"
54 #define ROM18 "rt1-18.h1"
55 #define ROM19 "rt1-19.k1"
56 #define ROM20 "rt1-20.m1"
57 #define ROM21 "rt1-21.f3"
58 #define ROM22 "rt1-22.h3"
59 #define PROM1 "mb7124e.3r"
60 #define PROM2 "mb7116e.3s"
61 #define PROM3 "mb7138h.4v"
62 #define PROM4 "mb7138h.6v"
63 #define PROM5 "mb7112e.6u"
64 #define MCUROM "rt1-mcu.bin"
71 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
72 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
73 uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
74 uint8_t chr_rom[0x60000]; // Character ROM pointer
78 bool trace1 = false; // ditto...
79 bool looking_at_rom = true; // true = R1, false = R2
80 uint32_t banksw1, banksw2; // Bank switch addresses
81 uint16_t game_over_switch; // Game over delay
82 uint16_t dpc; // Debug pc reg...
83 bool show_scr = true; // Whether or not to show background
84 bool enable_cpu = true; // Whether or not to enable CPUs
85 bool irqGoA = true; // IRQ switch for CPU #1
86 bool irqGoB = true; // IRQ switch for CPU #2
88 uint16_t refresh_ = 0; // Crappy global screen stuff...
91 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
92 uint8_t * psg_adrs[16];
93 uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
95 uint8_t * voc_adrs[32];
96 uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
97 uint8_t * fm_adrs[14];
99 fstream tr; // Tracelog hook
100 uint16_t pcx; // Where we at?
102 static uint8_t * keys; // SDL raw keyboard matrix
104 static char op_mat1[256] = {
105 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
106 0, 0, 5, 5, 0, 0, 4, 4, 0, 5, 8, 0, 8, 5, 6, 6,
107 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
108 7, 7, 7, 7, 6, 6, 6, 6, 0, 5, 5, 5, 8, 5, 5, 5,
109 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
110 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
111 7, 0, 0, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7,
112 2, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,
113 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 3, 9, 0,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
116 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
117 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 0, 9, 0,
118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
120 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0,
131 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
132 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7,
133 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2,
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7,
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
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, 0,
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
148 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
149 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
150 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
155 static char mnemonics[256][6] = {
156 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
157 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
158 "PAGE1","PAGE2","NOP ","SYNC ","??? ","??? ","LBRA ","LBSR ",
159 "??? ","DAA ","ORCC ","??? ","ANDCC","SEX ","EXG ","TFR ",
160 "BRA ","BRN ","BHI ","BLS ","BHS ","BLO ","BNE ","BEQ ",
161 "BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
162 "LEAX ","LEAY ","LEAS ","LEAU ","PSHS ","PULS ","PSHU ","PULU ",
163 "??? ","RTS ","ABX ","RTI ","CWAI ","MUL ","RESET","SWI ",
164 "NEGA ","??? ","??? ","COMA ","LSRA ","??? ","RORA ","ASRA ",
165 "LSLA ","ROLA ","DECA ","??? ","INCA ","TSTA ","??? ","CLRA ",
166 "NEGB ","??? ","??? ","COMB ","LSRB ","??? ","RORB ","ASRB ",
167 "LSLB ","ROLB ","DECB ","??? ","INCB ","TSTB ","??? ","CLRB ",
168 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
169 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
170 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
171 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
172 "SUBA ","CMPA ","SCBA ","SUBD ","ANDA ","BITA ","LDA ","??? ",
173 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","BSR ","LDX ","??? ",
174 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
175 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
176 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
177 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
178 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
179 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
180 "SUBB ","CMPB ","SCBB ","ADDD ","ANDB ","BITB ","LDB ","??? ",
181 "EORB ","ADCB ","ORB ","ADDB ","LDD ","??? ","LDU ","??? ",
182 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
183 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
184 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
185 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
186 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
187 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU " },
188 mnemonics2[256][6] = {
189 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
190 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
191 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
192 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
193 "??? ","LBRN ","LBHI ","LBLS ","LBHS ","LBLO ","LBNE ","LBEQ ",
194 "LBVC ","LBVS ","LBPL ","LBMI ","LBGE ","LBLT ","LBGT ","LBLE ",
195 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
196 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI2 ",
197 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
198 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
199 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
200 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
201 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
202 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
203 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
204 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
205 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
206 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","??? ",
207 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
208 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
209 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
210 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
211 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
212 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
213 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
214 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","??? ",
215 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
216 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
217 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
218 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
219 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
220 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS " },
221 mnemonics3[256][6] = {
222 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
223 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
224 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
225 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
226 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
227 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
228 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
229 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI3 ",
230 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
231 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
232 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
233 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
234 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
235 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
236 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
237 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
238 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
239 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
240 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
241 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
242 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
243 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
244 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
245 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
246 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
247 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
248 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
249 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
250 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
251 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
252 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
253 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? " },
255 "D", "X", "Y", "U", "S", "PC", "??", "??",
256 "A", "B", "CC", "DP", "??", "??", "??", "??" },
257 iregs[4][2] = {"X", "Y", "U", "S" };
261 // Read a byte from memory (without touching PC. Not a Fetch!)
263 uint8_t RdMem(uint16_t addr)
267 // $4000-4300 is RAM shared with the microcontroller...
272 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
284 // Write a byte to memory
286 void WrMem(uint16_t addr, uint8_t b)
289 extern bool charbase; // Needed for screen. Extern it in it??
290 //extern uint16_t sr, ur, xr, yr; // Needed for tracelog
291 //extern uint16_t pcr;
292 /* if ((addr>0x40FF) && (addr<0x4390))
294 tr << hex << addr << ":" << (int)b;
295 //for(int i=0; i<32; i++)
297 // if (gram1[0x4400+i]<0x10) tr << "0";
298 // tr << hex << (uint16_t)gram1[0x4400+i] << " ";
305 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
310 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
312 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
314 banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
315 if (addr > 0x4284 && addr < 0x42A5 && b)
316 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
319 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
321 game_over_switch = 240; // Set game over delay...
323 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
326 charbase = false; // Char banksw1
328 charbase = true; // Char banksw2
329 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
331 if (refresh_++ == 1) // 30 Hz...
333 BlitChar(screen, chr_rom, gram1);
334 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
337 // IRQ Ack (may also be frame go...
338 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
341 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
348 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
350 uint8_t RdMemB(uint16_t addr)
352 // extern uint16_t cpu2.s, cpu2.u, cpu2.x, cpu2.y; // Needed for tracelog
358 b = gram1[addr + 0x4000];
359 if (addr > 0x1FFF && addr < 0x6000)
360 b = gram1[addr - 0x2000];
362 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
367 /* if ((addr>0x3FFF) && (addr<0x4400)) tr << "R-" << hex << pcx << ": "
369 << (int)looking_at_rom
371 << "] XR:" << xr << " YR:" << yr
372 << " SR:" << sr << " UR:" << ur
379 // Write a byte to memory (2nd processor)
381 void WrMemB(uint16_t addr, uint8_t b)
384 extern bool charbase;
385 //extern uint16_t sr, ur, xr, yr; // Needed for tracelog
386 //extern uint16_t pcr;
387 /* if ((addr>0x00FF) && (addr<0x0390))
389 tr << hex << addr << ":" << (int)b;
390 //for(int i=0; i<32; i++)
392 // if (gram1[0x4400+i]<0x10) tr << "0";
393 // tr << hex << (uint16_t)gram1[0x4400+i] << " ";
400 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
405 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
407 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
408 if (addr > 0x0284 && addr < 0x02A5 && b)
409 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
411 banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
414 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
416 game_over_switch = 240; // Set game over delay...
418 if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
421 gram1[addr + 0x4000] = b;
422 if (addr > 0x1FFF && addr < 0x6000)
423 gram1[addr - 0x2000] = b;
429 // IRQ Ack (may also be frame go...)
430 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
433 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
440 // Display bytes in mem in hex
442 void DisplayBytes(uint16_t src, unsigned long dst)
445 WriteLog("%04X: ", src);
448 dst += 0x10000; // That should fix the FFFF bug...
450 for(unsigned long i=src; i<dst; i++)
452 WriteLog("%02X ", (uint8_t)(looking_at_rom ? RdMem(i) : RdMemB(i)));
456 // Pad the leftover spaces...
457 for(unsigned long i=cnt; i<5; i++)
463 uint8_t Fetch(void) { return RdMem(dpc); }
464 uint16_t FetchW(void) { return (uint16_t)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
465 uint8_t FetchB(void) { return RdMemB(dpc); }
466 uint16_t FetchWB(void) { return (uint16_t)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
470 // Decode a 6809 instruction at 'addr'
474 uint8_t (* DFetch)(); // Decode Fetch() pointer...
475 uint16_t (* DFetchW)(); // Decode FetchW() pointer...
476 DFetch = (looking_at_rom ? Fetch : FetchB);
477 DFetchW = (looking_at_rom ? FetchW : FetchWB);
479 /* extern*/ uint16_t pcr, pcrB; // Pull in 'pcr' from '6809.cpp'
480 uint16_t pc_save = pcr, pcB_save = pcrB;
481 pcr = dpc; pcrB = dpc;
482 uint8_t opcode = DFetch(); // Get the opcode ('fetch' cycle)
483 uint8_t opcode2, operand;
485 uint8_t admode = op_mat1[opcode]; // addressing mode
486 char outbuf[80], mnem[6], tmp[30];
488 strcpy(mnem, mnemonics[opcode]); // Copy page 1 opcode
490 if (opcode == 0x10) // Extended opcode?
492 opcode2 = DFetch(); // Then get next byte
493 admode = op_mat2[opcode2]; // And use it as index into 'op_mat2'
494 strcpy(mnem, mnemonics2[opcode2]); // Overwrite mnemonic
497 if (opcode == 0x11) // Same as above...
500 admode = op_mat3[opcode2];
501 strcpy(mnem, mnemonics3[opcode2]); // Overwrite mnemonic
508 sprintf(outbuf, "???");
511 operand = DFetch(); // Get ZP address
512 sprintf(outbuf, "%s $%02X", mnem, operand);
515 loperand = DFetchW(); // Get ABS address
516 sprintf(outbuf, "%s $%04X", mnem, loperand);
520 operand = DFetch(); // Get offset
521 uint16_t tmpc = (looking_at_rom ? pcr : pcrB);
522 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16_t)(int8_t)operand);
525 case 4: // Long Relative
527 loperand = DFetchW(); // Get long offset
528 uint16_t tmpc = (looking_at_rom ? pcr : pcrB);
529 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16_t)loperand);
533 sprintf(outbuf, "%s ", mnem);
535 case 6: // Txfr/exchg/push/pull
537 operand = DFetch(); // Get txfr/exg/push/pull byte
539 if ((opcode == 0x1E) || (opcode == 0x1F)) // Is it TXF/EXG?
541 sprintf(tmp, "%s,%s", tregs[operand>>4], tregs[operand&0x0F]);
546 if (operand&0x01) strcat(tmp, "CC ");
547 if (operand&0x02) strcat(tmp, "A ");
548 if (operand&0x04) strcat(tmp, "B ");
549 if (operand&0x08) strcat(tmp, "DP ");
550 if (operand&0x10) strcat(tmp, "X ");
551 if (operand&0x20) strcat(tmp, "Y ");
552 if (operand&0x40) (((opcode==0x34)||(opcode==0x35))
553 ? strcat(tmp, "U ") : strcat(tmp, "S "));
554 if (operand&0x80) strcat(tmp, "PC");
556 sprintf(outbuf, "%s %s", mnem, tmp);
559 case 7: // Indexed (the tough one!)
561 operand = DFetch(); // Get IDX byte
562 uint8_t reg = ((operand & 0x60) >> 5), idxind = ((operand & 0x10) >> 4),
563 lo_nyb = (operand & 0x0F), boff;
568 if (!(operand & 0x80)) // Hi bit set? Then decode 4 bit offset
570 sprintf(tmp, "(%d),%s", (idxind ? -(16-lo_nyb) : lo_nyb),
573 else // Add the ($nnnn,R) code dude...
579 case 1: sprintf(tmp, "(,%s++)", iregs[reg]); break;
580 case 3: sprintf(tmp, "(,--%s)", iregs[reg]); break;
581 case 4: sprintf(tmp, "(,%s)", iregs[reg]); break;
582 case 5: sprintf(tmp, "(B,%s)", iregs[reg]); break;
583 case 6: sprintf(tmp, "(A,%s)", iregs[reg]); break;
585 { boff = DFetch(); sprintf(tmp, "($%02X,%s)", boff,
586 iregs[reg]); break; }
588 { woff = DFetchW(); sprintf(tmp, "($%04X,%s)", woff,
589 iregs[reg]); break; }
590 case 11: sprintf(tmp, "(D,%s)", iregs[reg]); break;
592 { boff = DFetch(); sprintf(tmp, "($%02X,PC)", boff); break; }
594 { woff = DFetchW(); sprintf(tmp, "($%04X,PC)", woff); break; }
596 { woff = DFetchW(); sprintf(tmp, "[$%04X]", woff); break; }
597 default: strcpy(tmp, "??");
604 case 0: sprintf(tmp, ",%s+", iregs[reg]); break;
605 case 1: sprintf(tmp, ",%s++", iregs[reg]); break;
606 case 2: sprintf(tmp, ",-%s", iregs[reg]); break;
607 case 3: sprintf(tmp, ",--%s", iregs[reg]); break;
608 case 4: sprintf(tmp, ",%s", iregs[reg]); break;
609 case 5: sprintf(tmp, "(B),%s", iregs[reg]); break;
610 case 6: sprintf(tmp, "(A),%s", iregs[reg]); break;
612 { boff = DFetch(); sprintf(tmp, "($%02X),%s", boff,
613 iregs[reg]); break; }
615 { woff = DFetchW(); sprintf(tmp, "($%04X),%s", woff,
616 iregs[reg]); break; }
617 case 11: sprintf(tmp, "(D),%s", iregs[reg]); break;
619 { boff = DFetch(); sprintf(tmp, "($%02X),PC", boff); break; }
621 { woff = DFetchW(); sprintf(tmp, "($%04X),PC", woff); break; }
622 default: strcpy(tmp, "??");
627 sprintf(outbuf, "%s %s", mnem, tmp);
631 operand = DFetch(); // Get IMM byte
632 sprintf(outbuf, "%s #$%02X", mnem, operand);
634 case 9: // Long Immediate
635 loperand = DFetchW(); // Get IMM word
636 sprintf(outbuf, "%s #$%04X", mnem, loperand);
640 DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB)); // Show bytes
642 WriteLog("\n"); // display opcode & addressing, etc
643 dpc = (looking_at_rom ? pcr : pcrB); // Advance debug PC
645 pcrB = pcB_save; // Restore PCs
650 // Convert hex to dec
652 uint16_t htod(char * str)
655 int len = strlen(str);
657 for(int i=0; i<len; i++)
659 if (str[i] >= '0' && str[i] <= '9')
661 value = (value << 4) | (unsigned)(str[i] - '0');
663 else if (str[i] >= 'a' && str[i] <= 'f')
665 value = (value << 4) | (unsigned)((str[i] - 'a') + 10);
667 else if (str[i] >= 'A' && str[i] <= 'F')
669 value = (value << 4) | (unsigned)((str[i] - 'A') + 10);
678 // Load 32K file into ROM image space
680 bool Load32KImg(char * filename, uint16_t address)
685 ff.open(filename, ios::binary | ios::in); // Open 'da file...
689 for(long i=0; i<32768; i++) // Read it in...
692 grom[address+i] = ch;
695 ff.close(); // Close 'da file...
703 // Generic Load file into image space
704 // (No error checking performed! Responsibility of caller!)
706 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
712 strcpy(path, "./ROMs/");
713 strcat(path, filename);
714 // ff.open(filename, ios::binary | ios::in); // Open 'da file...
715 ff.open(path, ios::binary | ios::in); // Open 'da file...
719 for(uint32_t i=0; i<length; i++) // Read it in...
725 ff.close(); // Close 'da file...
735 bool ReadColorPROMs(void)
740 extern uint8_t palette[768]; // Screen physical palette
741 extern uint8_t ccolor[256][8]; // Character color PROM values
742 extern uint8_t scolor[128][16]; // Sprite color PROM values
744 ff1.open("./ROMs/"PROM3, ios::binary|ios::in);
748 for(int i=0; i<256; i++) // Load char pallete with PROM values
750 for(int j=0; j<8; j++)
753 ccolor[i][j] = (uint8_t)ch;
760 ff1.open("./ROMs/"PROM4, ios::binary|ios::in);
764 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
766 for(int j=0; j<16; j++)
769 scolor[i][j] = (uint8_t)ch;
776 ff1.open("./ROMs/"PROM1, ios::binary|ios::in);
777 ff2.open("./ROMs/"PROM2, ios::binary|ios::in);
779 if (ff1) // If open was successful...
781 for(int i=0; i<768; i+=3)
784 palette[i] = (uint8_t)(ch&0x0F);
785 palette[i+1] = (uint8_t)(ch>>4);
787 palette[i+2] = (uint8_t)ch;
790 // Do palette stretching here... I.e. add 0 to hinyb 0, 1 to hinyb 1, etc.
792 for(int i=0; i<768; i++)
793 palette[i] = ((palette[i]<<4)&0xF0) | (palette[i]&0x0F);
806 bool UnpackFonts(void)
808 // uint8_t b1, b2, b3;
812 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
813 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
816 return false; // Return if not found...
818 for(long i=0; i<0x40000; i+=64)
820 for(int j=0; j<64; j+=8)
822 f1.get(b1); f1.get(b2); f2.get(b3);
823 b3 ^= 0xFF; // Invert top data...
824 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
825 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
826 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
827 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
828 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
829 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
830 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
831 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
838 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
839 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
841 for(long i=0x40000; i<0x60000; i+=64)
843 for(int j=0; j<64; j+=8)
845 f1.get(b1); f1.get(b2); f2.get(b3);
846 b3 ^= 0xFF; // Invert top data
847 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
848 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
849 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
850 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
851 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
852 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
853 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
854 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
861 return true; // Made it!
866 // Get length of sample from WAV format
868 uint32_t GetWAVLength(fstream & file)
873 file.ignore(16); // Skip header BS
875 for(int i=0; i<2; i++)
877 file.get(ch); len = (int)(uint8_t)ch;
878 file.get(ch); len |= (int)(uint8_t)ch << 8;
879 file.get(ch); len |= (int)(uint8_t)ch << 16;
880 file.get(ch); len |= (int)(uint8_t)ch << 24;
882 file.ignore(len + 4); // Skip intermediate data
885 file.get(ch); len = (int)(uint8_t)ch; // & finally get length of data
886 file.get(ch); len |= (int)(uint8_t)ch << 8;
887 file.get(ch); len |= (int)(uint8_t)ch << 16;
888 file.get(ch); len |= (int)(uint8_t)ch << 24;
895 // Load PSG samples from disk
903 for(int i=0; i<16; i++)
907 psg_adrs[i] = NULL; // Zero out pointer
908 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
910 fp.open(file, ios::binary | ios::in); // Attempt to open it...
914 len = GetWAVLength(fp); // Get WAV data length...
915 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
917 if (psg_adrs[i] != NULL)
919 for(int j=0; j<(signed)len; j++)
922 psg_adrs[i][j] = ch; // & load it in...
926 // cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
927 printf("Found sample file: %s\t[Length: %u]\n", file, len);
937 // Load FM samples from disk
945 for(int i=0; i<14; i++)
949 fm_adrs[i] = NULL; // Zero out pointer
950 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
952 fp.open(file, ios::binary | ios::in); // Attempt to open it...
956 len = GetWAVLength(fp); // Get WAV length...
957 fm_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
959 if (fm_adrs[i] != NULL)
961 for(int j=0; j<(signed)len; j++)
964 fm_adrs[i][j] = ch; // & load it in...
968 // cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
969 printf("Found sample file: %s\t[Length: %u]\n", file, len);
981 int main(int argc, char * argv[])
983 InitLog("thunder.log");
985 extern bool disasm; // From 'V6809.CPP'
986 extern bool charbase; // From 'SCREEN.CPP'
990 fstream ff; // Declare fstream without file hooks...
991 bool brk = false, brk2 = false; // Breakpoint set flag
992 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
993 bool running; // CPU running state flag...
994 bool self_test = false; // Self-test switch
995 bool scr_type = false; // false=chars, true=pixels
996 uint16_t debounce = 0; // Key de-bounce counter
997 uint16_t fire_debounce = 0; // Fire button debounce counter
998 // bool refresh2 = true; // Default to 60 Hz...
999 uint8_t x; // General placeholder...
1000 bool active = true; // Program running flag
1002 SDL_Event event; // SDL "event"
1003 extern uint8_t palette[768]; // Screen physical palette
1004 uint32_t ticks, oldTicks;
1006 cout << endl << "THUNDER v"THUNDER_VERSION" ";
1007 cout << "by James Hammons" << endl;
1008 cout << "Serial #20149404 / Prerelease" << endl;
1009 cout << "© 2003, 2014 Underground Software" << endl << endl;
1011 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
1014 cout << "Initializing SDL..." << endl;
1016 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
1018 cout << "Couldn't initialize SDL: " << SDL_GetError() << endl;
1022 SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
1024 keys = SDL_GetKeyState(NULL); // Get the SDL keyboard matrix
1026 gram = gram1; grom = grom1; // Needed only for debugger
1028 for(long i=0; i<0x10000; i++)
1030 gram[i] = 0; grom[i] = 0; // Zero out memory
1031 gram2[i] = 0; grom2[i] = 0;
1034 game_over_switch = 0; // Init game over delay
1035 // 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;
1037 cout << "Loading ROMs..." << endl;
1038 // LoadCMOS(); // Load CMOS at $CC00-$CFFF
1039 if (!ReadColorPROMs()) // Load virtual PROMs
1040 { cout << "Could not open PROM files!" << endl; return -1; }
1042 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
1043 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
1045 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
1046 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
1048 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
1049 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
1051 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
1052 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
1054 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
1055 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
1057 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
1058 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
1060 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
1061 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
1063 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
1064 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
1066 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
1067 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
1069 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
1070 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
1072 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
1073 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
1075 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
1076 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
1078 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
1079 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
1081 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
1082 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
1084 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
1085 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
1087 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
1088 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
1090 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
1091 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
1093 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
1094 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
1096 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
1098 cout << "Could not open font files!" << endl;
1102 // Load samples if they're there...
1106 // Quick 'n' Dirty voice dump (sound 0x0E)
1107 /* uint32_t adc = (voice_rom[26]<<8) | voice_rom[27];
1108 bool doneWitIt = false;
1112 if (voice_rom[adc] < 0x10) tr << "0";
1113 tr << hex << (int)voice_rom[adc] << " ";
1114 if (crh++ > 24) { crh = 0; tr << endl; }
1115 if ((voice_rom[adc] == 0xFF) && (voice_rom[adc-1] != 0x00))
1120 // Set up V6809 execution contexts
1122 memset(&cpu1, 0, sizeof(V6809REGS));
1125 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1127 memset(&cpu2, 0, sizeof(V6809REGS));
1128 cpu2.RdMem = RdMemB;
1129 cpu2.WrMem = WrMemB;
1130 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1132 bool firstTime = true; // kludge...
1134 WriteLog("About to go to the main loop...\n");
1140 firstTime = false; // crappy kludge...
1147 if (lbuff[0] == 'd')
1154 printf("%04X: ", dpc);
1155 uint16_t pc_save = cpu1.pc, pcB_save = cpu2.pc;
1156 cpu1.pc = dpc; cpu2.pc = dpc;
1157 for(int i=0; i<16; i++)
1158 printf("%02X ", (looking_at_rom ? Fetch() : FetchB()));
1160 cpu1.pc = dpc; cpu2.pc = dpc;
1161 for(int i=0; i<16; i++)
1163 uint8_t a = (looking_at_rom ? Fetch() : FetchB());
1164 if (a<10) cout << (char)(a+48);
1165 if ((a>9) && (a<37)) cout << (char)(a+55);
1166 if (a>36) cout << ".";
1169 dpc = (looking_at_rom ? cpu1.pc : cpu2.pc);
1170 cpu1.pc = pc_save; cpu2.pc = pcB_save;
1172 else if (lbuff[0] == 'e')
1179 printf("%04X: ", dpc);
1180 for(int i=0; i<16; i++) printf("%02X ", (uint8_t)gram[dpc++]);
1183 else if (lbuff[0] == 'l')
1190 for(int i=0; i<23; i++)
1193 else if (lbuff[0] == 't')
1204 Execute6809(&cpu1, 1);
1206 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1207 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1208 cout << " iclock=" << cpu1.clock << endl;
1214 Execute6809(&cpu2, 1);
1216 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1217 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1218 cout << " iclock=" << cpu2.clock << endl;
1221 else if ((lbuff[0] == 'r') || (lbuff[0] == 'c')) // Run/continue...
1223 WriteLog("Executing 'run' command...\n");
1224 uint32_t my_clock = 0;
1225 running = true; // Set running status...
1227 SetRefreshRate(refresh2); // Tell GUI our refresh rate
1228 //for(uint16_t i=0; i<0x8000; i++) gram2[i] = grom3[i]; //Temp
1230 if (lbuff[0] == 'r') // If run, then reset CPUs
1232 WriteLog("Executing secondary 'run' command...\n");
1234 // This is data that is supposed to come from the MCU... So that's why it hangs
1235 gram1[0x4182] = 0xA6; // Temp kludge
1236 gram1[0x4184] = 0xA6;
1237 gram1[0x4183] = 0x00; // More of the same
1238 gram1[0x4185] = 0x00;
1240 banksw1 = 0; // Will this work?
1242 // iclock = 0; // Reset instr clock #1...
1243 InitGUI(); // Reset # of coins
1246 cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
1249 lbuff[0] = 32; cpu1.pc = htod(lbuff);
1251 else cpu1.cc = 0xFF; // Set CC register
1253 cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
1254 cpu2.cc = 0xFF; // Set CC register
1255 while(iclock < 8000) // was 17000, 20000, 5000
1257 Execute6809(&cpu1, 1); Execute6809(&cpu2, 1);
1261 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
1262 // Will *this* help video sync? NO
1263 while (cpu1.clock < 8000) // was 17000, 20000, 5000
1265 Execute6809(&cpu1, 1);
1266 Execute6809(&cpu2, 1);
1271 WriteLog("About to set up screen...\n");
1272 screen = SDL_SetVideoMode(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 8, SDL_SWSURFACE | SDL_DOUBLEBUF);
1275 cout << "Failed to initialize screen!" << endl;
1279 SDL_Color colors[256];
1281 for(int i=0; i<256; i++)
1283 colors[i].r = palette[i*3+0];
1284 colors[i].g = palette[i*3+1];
1285 colors[i].b = palette[i*3+2];
1288 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1291 // This confirms that we're getting video to the screen...
1292 SDL_LockSurface(screen);
1295 uint8_t * pixels = (uint8_t *)(screen->pixels);
1297 for(uint32_t y=0; y<480; y++)
1298 for(uint32_t x=0; x<640; x++)
1299 pixels[(y * 640) + x] = pixel++;
1301 SDL_UnlockSurface(screen);
1302 SDL_UpdateRect(screen, 0, 0, 0, 0);
1305 for(int i=0; i<256; i++)
1306 keys[i] = 0; // Clear keyboard buffer...
1308 oldTicks = SDL_GetTicks();
1310 WriteLog("About to set up audio...\n");
1311 // This crap SHOULD be in sound.cpp (not yet created)...
1312 SDL_AudioSpec desired, obtained;
1313 desired.freq = 22050;
1314 desired.format = AUDIO_U8;
1315 desired.channels = 1;
1316 desired.samples = 600;
1317 desired.callback = SoundFunc;
1318 desired.userdata = NULL;
1319 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1320 if (SDL_OpenAudio(&desired, &obtained) < 0)
1322 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1326 SDL_PauseAudio(0); // Get that audio going!
1328 WriteLog("About to enter main loop...\n");
1331 HandleGUIDebounce(); // Debounce GUI keys
1332 if (game_over_switch)
1334 game_over_switch--; // Countdown...
1335 if (game_over_switch == 0)
1336 gram1[0x4380] = 0; // Kill music!
1338 //testing... (works)
1339 //gram1[0x423D] = 1;
1340 //gram1[0x423D] = self_test; // Reset DSW1-1
1341 gram1[0x4268] = 0; // Reset Video test
1342 gram1[0x427A] = 0; gram1[0x427C] = 0;
1343 gram1[0x427B] = 0; gram1[0x427D] = 0;
1344 gram1[0x427E] = 0; gram1[0x427F] = 0;
1345 gram1[0x4280] = 0; gram1[0x4281] = 0;
1346 gram1[0x4276] = 0; gram1[0x426A] = 0;
1347 gram1[0x4278] = 0; gram1[0x426C] = 0;
1348 gram1[0x4262] = 0; gram1[0x4260] = 0;
1349 //gram1[0x4247] = 0;
1351 // SDL key handling...
1353 SDL_PumpEvents(); // Force key events into the buffer.
1355 if (keys[SDLK_ESCAPE])
1356 running = false; // ESC to exit...
1359 debounce--; // Debounce toggle keys...
1364 self_test = !self_test; // Self-test (F1-toggle)
1365 debounce = 10; // Key debounce value...
1369 gram1[0x4268] = 1; // Video test (F2)
1370 debounce = 10; // Key debounce value...
1374 scr_type = !scr_type; // Toggle screen (F12)
1375 debounce = 10; // Key debounce value...
1379 show_scr = !show_scr; // Toggle bkgrnd (F3)
1384 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1389 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1390 SetRefreshRate(refresh2); // Inform GUI of refresh
1395 debounce = 10; // Key debounce value...
1397 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1399 SpawnSound(USERSOUND, SCAMERA);
1403 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1413 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1414 if (keys[SDLK_RIGHT]) // Right arrow
1417 SelectRight(); // If GUI active...
1420 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1421 gram1[0x427F] = 1; // Stick right
1424 if (keys[SDLK_LEFT])
1427 SelectLeft(); // If GUI active...
1430 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1431 gram1[0x4281] = 1; // Left arrow
1437 SelectUp(); // If GUI active...
1440 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1441 gram1[0x427B] = 1; // Up arrow
1444 if (keys[SDLK_DOWN])
1447 SelectDown(); // If GUI active...
1450 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1451 gram1[0x427D] = 1; // Down arrow
1454 if (keys[SDLK_RETURN]) // Return
1456 uint8_t retval = UserSelectedSomething();
1461 if (retval == REFRESH)
1463 refresh2 = !refresh2;
1464 SetRefreshRate(refresh2);
1469 gram1[0x427A] = 1; // (1)
1472 gram1[0x427C] = 1; // (2)
1475 gram1[0x427E] = 1; // (3)
1478 gram1[0x4280] = 1; // (5)
1480 if (keys[SDLK_q] | keys[29])
1481 gram1[0x4276] = 1; // (Q) Jump
1484 gram1[0x426A] = 1; // (W)
1489 if (keys[SDLK_e] | keys[56]) // (E) Fire
1495 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1503 gram1[0x426C] = 1; // (R)
1506 gram1[0x4262] = 1; // (T)
1509 gram1[0x4260] = 1; // (Y)
1512 gram1[0x41A5]++; // Coin? (F10)
1515 gram1[0x4189]++; // ? (Z) credits l dig
1518 gram1[0x418A]++; // ? (X) credits r dig
1521 gram1[0x418C]++; // ? (C) Start
1524 gram1[0x418D]++; // ? (V)
1527 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1529 // if (keys[SDLK_F8])
1531 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1532 // charbase = false; // Switch chars out...
1534 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1536 if (keys[SDLK_F11]) // (F11)
1538 Execute6809(&cpu1, 10);
1539 Execute6809(&cpu2, 10);
1542 //F12 is used above, but the values are ignored. So we'll do it here too.
1545 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1546 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1549 if (keys[SDLK_d]) // (D) start disassembly
1553 gram1[0x5606] = 0x00;
1556 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1557 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1561 gram1[0x5FF3] = 0x02;
1562 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1570 // We can do this here because we're not executing the cores yet.
1571 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1572 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1573 // while (cpu1.clock < 25000)
1574 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1575 // 25600 cycles/frame
1576 // Setting interleave to 25 and below causes the V6809 core to hang...
1577 // 32 gets to the title screen before hanging...
1578 // 40 works, until it doesn't... :-P
1581 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1582 for(uint32_t i=0; i<640; i++)
1583 // for(uint32_t i=0; i<1280; i++)
1585 // Gay, but what are ya gonna do?
1586 // There's better ways, such as keeping track of when slave writes to master, etc...
1587 Execute6809(&cpu1, 40);
1588 Execute6809(&cpu2, 40);
1590 } // END: enable_cpu
1592 // if (refresh_++ == 1) // 30 Hz...
1595 // BlitWilliamsScreen(gram1); // Display the screen...
1597 // BlitChar(screen, chr_rom, gram1);
1598 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1602 //temp, for testing...
1603 BlitChar(screen, chr_rom, gram1);
1605 // Speed throttling happens here...
1606 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1607 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1608 SDL_Delay(1); // Release our timeslice...
1610 oldTicks = SDL_GetTicks();
1611 //cout << "Finished frame..." << endl;
1614 // Stop_audio_output();
1616 // ReleaseKeyboard(); // Release the interrupt...
1617 // RestoreOldMode(); // Restore screen
1618 if (brk && (cpu1.pc == brkpnt))
1619 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1620 if (brk2 && (cpu2.pc == brkpnt2))
1621 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1623 lbuff[0] = 'q'; // Temp kludge...
1625 else if (lbuff[0] == 'b') // Set/clear breakpoint
1630 brkpnt = htod(lbuff);
1632 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1637 cout << "Breakpoint cleared" << endl;
1640 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1645 brkpnt2 = htod(lbuff);
1647 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1652 cout << "Breakpoint cleared" << endl;
1655 else if (lbuff[0] == 'i') // Inspect registers
1657 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1658 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1659 cout << " iclk=" << dec << cpu1.clock << endl;
1660 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1661 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1662 cout << " iclk=" << dec << cpu2.clock << endl;
1665 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1668 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1670 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1672 looking_at_rom = !looking_at_rom;
1673 cout << "Swapped: Looking at ";
1674 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1677 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1682 for(int i=0; i<4; i++)
1691 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1694 printf("%04X: ", dpc); // Show data found...
1696 for(int i=0; i<16; i++)
1697 printf("%02X ", gram1[(uint16_t)(dpc+i)]);
1701 for(int i=0; i<16; i++)
1703 uint8_t a = gram1[dpc++];
1706 cout << (char)(a+48);
1707 if ((a>9) && (a<37))
1708 cout << (char)(a+55);
1715 else if (lbuff[0] == 'v') // View screen
1717 BlitChar(screen, chr_rom, gram1);
1721 if (lbuff[0] == 'q')
1722 active = false; //break; // Quit
1725 SDL_Quit(); // Shut down SDL
1727 for(int i=0; i<16; i++)
1729 delete[] psg_adrs[i]; // Deallocate if loaded
1731 for(int i=0; i<14; i++)
1733 delete[] fm_adrs[i]; // Deallocate if loaded
1741 Hitachi uC runs at 6.144 MHz
1742 YM2151 runs at 3.579580 MHz
1745 Rolling Thunder Memory map
1746 --------------------------
1747 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1748 map is inferred by program behaviour. The customs also handle internally irq
1751 The main CPU memory map is the same in all games because CUS47 is used by all
1752 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1753 replaced by other chips.
1755 All RAM is shared between main and sub CPU, except for sound RAM which is
1756 shared between main and sound CPU; the portion of object RAM that is overlapped
1757 by sound RAM is used exclusively by the sub CPU.
1761 Address Dir Data Name Description
1762 ------------------- --- -------- --------- -----------------------
1763 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1764 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1765 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1766 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1767 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1768 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1769 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1770 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1771 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1772 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1773 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1774 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1775 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1776 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1777 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1778 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1779 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1780 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1781 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1783 [1] Note that this is partially overlapped by sound RAM
1784 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1789 Address Dir Data Name Description
1790 ------------------- --- -------- --------- -----------------------
1791 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1792 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1793 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1794 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1795 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1796 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1797 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1798 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1799 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1800 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1801 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1802 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1803 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1804 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1806 [1] Only used by Rolling Thunder
1811 Address Dir Data Name Description
1812 ------------------- --- -------- --------- -----------------------
1813 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1814 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1815 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1816 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1817 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1818 0010 0--- --01 ---- n.c.
1819 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1820 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1821 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1822 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1823 1011 0--- ---- ---- W unknown (CUS41)
1824 1011 1--- ---- ---- W unknown (CUS41)
1825 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1830 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1831 in rthunder. The two 6809 in this game synchronize using a semaphore at
1832 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1833 and then increments 5606. While it does its quick things (which require
1834 about 40 clock cycles) it expects CPU2 to clear 5607.
1835 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1836 mode. I haven't investigated on the cause.
1838 - There are two watchdogs, one per CPU (or maybe three). Handling them
1839 separately is necessary to allow entering service mode without manually
1840 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1843 - The sprite hardware buffers spriteram: the program writes the sprite list to
1844 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1845 sprite RAM to signal the chip that the list is complete. The chip will copy
1846 the list from 4-9 to 10-15 and use it from there. This has not been verified
1847 on the real hardware, but it is the most logical way of doing it.
1848 Emulating this behaviour and not using an external buffer is important in
1849 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1850 is not written to. If we buffered spriteram to an external buffer, this would
1851 cause dangling sprites because the buffer would not be updated.
1853 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1854 entering a door. The *closed* door is made of tiles, but the *moving* door is
1855 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1856 there is one frame where neither the tile-based closed door nor the
1857 sprite-based moving door is shown, so it flickers. This behavior has been
1858 confirmed on a real PCB.
1862 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1863 but they don't seem to work as expected. During the first few frames they are
1864 written out of order and hooking them up in the usual way causes the MCU to
1865 stop receiving interrupts.