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
1412 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1413 if (keys[SDLK_RIGHT]) // Right arrow
1416 SelectRight(); // If GUI active...
1419 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1420 gram1[0x427F] = 1; // Stick right
1423 if (keys[SDLK_LEFT])
1426 SelectLeft(); // If GUI active...
1429 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1430 gram1[0x4281] = 1; // Left arrow
1436 SelectUp(); // If GUI active...
1439 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1440 gram1[0x427B] = 1; // Up arrow
1443 if (keys[SDLK_DOWN])
1446 SelectDown(); // If GUI active...
1449 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1450 gram1[0x427D] = 1; // Down arrow
1453 if (keys[SDLK_RETURN]) // Return
1455 uint8_t retval = UserSelectedSomething();
1458 if (retval == REFRESH)
1460 refresh2 = !refresh2;
1461 SetRefreshRate(refresh2);
1465 gram1[0x427A] = 1; // (1)
1467 gram1[0x427C] = 1; // (2)
1469 gram1[0x427E] = 1; // (3)
1471 gram1[0x4280] = 1; // (5)
1472 if (keys[SDLK_q] | keys[29])
1473 gram1[0x4276] = 1; // (Q) Jump
1475 gram1[0x426A] = 1; // (W)
1478 if (keys[SDLK_e] | keys[56]) // (E) Fire
1484 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1491 gram1[0x426C] = 1; // (R)
1493 gram1[0x4262] = 1; // (T)
1495 gram1[0x4260] = 1; // (Y)
1497 gram1[0x41A5]++; // Coin? (F10)
1499 gram1[0x4189]++; // ? (Z) credits l dig
1501 gram1[0x418A]++; // ? (X) credits r dig
1503 gram1[0x418C]++; // ? (C) Start
1505 gram1[0x418D]++; // ? (V)
1507 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1508 // if (keys[SDLK_F8])
1510 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1511 // charbase = false; // Switch chars out...
1513 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1514 if (keys[SDLK_F11]) // (F11)
1516 Execute6809(&cpu1, 10);
1517 Execute6809(&cpu2, 10);
1520 //F12 is used above, but the values are ignored. So we'll do it here too.
1523 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1524 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1526 if (keys[SDLK_d]) // (D) start disassembly
1530 gram1[0x5606] = 0x00;
1533 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1534 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1538 gram1[0x5FF3] = 0x02;
1539 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1547 // We can do this here because we're not executing the cores yet.
1548 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1549 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1550 // while (cpu1.clock < 25000)
1551 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1552 // 25600 cycles/frame
1553 // Setting interleave to 25 and below causes the V6809 core to hang...
1554 // 32 gets to the title screen before hanging...
1555 // 40 works, until it doesn't... :-P
1558 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1559 for(uint32_t i=0; i<640; i++)
1560 // for(uint32_t i=0; i<1280; i++)
1562 // Gay, but what are ya gonna do?
1563 // There's better ways, such as keeping track of when slave writes to master, etc...
1564 Execute6809(&cpu1, 40);
1565 Execute6809(&cpu2, 40);
1567 } // END: enable_cpu
1569 // if (refresh_++ == 1) // 30 Hz...
1572 // BlitWilliamsScreen(gram1); // Display the screen...
1574 // BlitChar(screen, chr_rom, gram1);
1575 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1579 //temp, for testing...
1580 BlitChar(screen, chr_rom, gram1);
1582 // Speed throttling happens here...
1583 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1584 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1585 SDL_Delay(1); // Release our timeslice...
1587 oldTicks = SDL_GetTicks();
1588 //cout << "Finished frame..." << endl;
1591 // Stop_audio_output();
1593 // ReleaseKeyboard(); // Release the interrupt...
1594 // RestoreOldMode(); // Restore screen
1595 if (brk && (cpu1.pc == brkpnt))
1596 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1597 if (brk2 && (cpu2.pc == brkpnt2))
1598 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1600 lbuff[0] = 'q'; // Temp kludge...
1602 else if (lbuff[0] == 'b') // Set/clear breakpoint
1607 brkpnt = htod(lbuff);
1609 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1614 cout << "Breakpoint cleared" << endl;
1617 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1622 brkpnt2 = htod(lbuff);
1624 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1629 cout << "Breakpoint cleared" << endl;
1632 else if (lbuff[0] == 'i') // Inspect registers
1634 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1635 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1636 cout << " iclk=" << dec << cpu1.clock << endl;
1637 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1638 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1639 cout << " iclk=" << dec << cpu2.clock << endl;
1642 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1645 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1647 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1649 looking_at_rom = !looking_at_rom;
1650 cout << "Swapped: Looking at ";
1651 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1654 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1659 for(int i=0; i<4; i++)
1668 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1671 printf("%04X: ", dpc); // Show data found...
1673 for(int i=0; i<16; i++)
1674 printf("%02X ", gram1[(uint16_t)(dpc+i)]);
1678 for(int i=0; i<16; i++)
1680 uint8_t a = gram1[dpc++];
1683 cout << (char)(a+48);
1684 if ((a>9) && (a<37))
1685 cout << (char)(a+55);
1692 else if (lbuff[0] == 'v') // View screen
1694 BlitChar(screen, chr_rom, gram1);
1698 if (lbuff[0] == 'q')
1699 active = false; //break; // Quit
1702 SDL_Quit(); // Shut down SDL
1704 for(int i=0; i<16; i++)
1706 delete[] psg_adrs[i]; // Deallocate if loaded
1708 for(int i=0; i<14; i++)
1710 delete[] fm_adrs[i]; // Deallocate if loaded
1718 Hitachi uC runs at 6.144 MHz
1719 YM2151 runs at 3.579580 MHz
1722 Rolling Thunder Memory map
1723 --------------------------
1724 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1725 map is inferred by program behaviour. The customs also handle internally irq
1728 The main CPU memory map is the same in all games because CUS47 is used by all
1729 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1730 replaced by other chips.
1732 All RAM is shared between main and sub CPU, except for sound RAM which is
1733 shared between main and sound CPU; the portion of object RAM that is overlapped
1734 by sound RAM is used exclusively by the sub CPU.
1738 Address Dir Data Name Description
1739 ------------------- --- -------- --------- -----------------------
1740 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1741 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1742 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1743 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1744 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1745 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1746 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1747 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1748 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1749 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1750 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1751 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1752 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1753 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1754 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1755 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1756 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1757 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1758 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1760 [1] Note that this is partially overlapped by sound RAM
1761 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1766 Address Dir Data Name Description
1767 ------------------- --- -------- --------- -----------------------
1768 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1769 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1770 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1771 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1772 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1773 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1774 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1775 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1776 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1777 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1778 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1779 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1780 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1781 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1783 [1] Only used by Rolling Thunder
1788 Address Dir Data Name Description
1789 ------------------- --- -------- --------- -----------------------
1790 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1791 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1792 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1793 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1794 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1795 0010 0--- --01 ---- n.c.
1796 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1797 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1798 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1799 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1800 1011 0--- ---- ---- W unknown (CUS41)
1801 1011 1--- ---- ---- W unknown (CUS41)
1802 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1807 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1808 in rthunder. The two 6809 in this game synchronize using a semaphore at
1809 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1810 and then increments 5606. While it does its quick things (which require
1811 about 40 clock cycles) it expects CPU2 to clear 5607.
1812 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1813 mode. I haven't investigated on the cause.
1815 - There are two watchdogs, one per CPU (or maybe three). Handling them
1816 separately is necessary to allow entering service mode without manually
1817 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1820 - The sprite hardware buffers spriteram: the program writes the sprite list to
1821 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1822 sprite RAM to signal the chip that the list is complete. The chip will copy
1823 the list from 4-9 to 10-15 and use it from there. This has not been verified
1824 on the real hardware, but it is the most logical way of doing it.
1825 Emulating this behaviour and not using an external buffer is important in
1826 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1827 is not written to. If we buffered spriteram to an external buffer, this would
1828 cause dangling sprites because the buffer would not be updated.
1830 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1831 entering a door. The *closed* door is made of tiles, but the *moving* door is
1832 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1833 there is one frame where neither the tile-based closed door nor the
1834 sprite-based moving door is shown, so it flickers. This behavior has been
1835 confirmed on a real PCB.
1839 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1840 but they don't seem to work as expected. During the first few frames they are
1841 written out of order and hooking them up in the usual way causes the MCU to
1842 stop receiving interrupts.