2 // Thunder: A Rolling Thunder Emulator w/6809 debugger
5 // (C) 2004, 2014 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 07/23/2009 Added changelog ;-)
12 // JLH 08/12/2009 Stabilized emulation so that it works
13 // JLH 04/04/2014 Converted to SDL 2
16 #define THUNDER_VERSION "1.0.0"
26 //#include <conio.h> // For getch()
27 #include <curses.h> // For getch()
29 #include <SDL2/SDL.h> // Get yer SDL out...!
39 #define ROM1 "rt3-1b.9c"
40 #define ROM2 "rt3-2b.12c"
41 #define ROM3 "rt3-3.12d"
42 #define ROM4 "rt1-4.6b"
43 #define ROM5 "rt1-5.4r"
44 #define ROM6 "rt1-6.4s"
45 #define ROM7 "rt1-7.7r"
46 #define ROM8 "rt1-8.7s"
47 #define ROM9 "rt1-9.12h"
48 #define ROM10 "rt1-10.12k"
49 #define ROM11 "rt1-11.12l"
50 #define ROM12 "rt1-12.12m"
51 #define ROM13 "rt1-13.12p"
52 #define ROM14 "rt1-14.12r"
53 #define ROM15 "rt1-15.12t"
54 #define ROM16 "rt1-16.12u"
55 #define ROM17 "rt1-17.f1"
56 #define ROM18 "rt1-18.h1"
57 #define ROM19 "rt1-19.k1"
58 #define ROM20 "rt1-20.m1"
59 #define ROM21 "rt1-21.f3"
60 #define ROM22 "rt1-22.h3"
61 #define PROM1 "mb7124e.3r"
62 #define PROM2 "mb7116e.3s"
63 #define PROM3 "mb7138h.4v"
64 #define PROM4 "mb7138h.6v"
65 #define PROM5 "mb7112e.6u"
66 #define MCUROM "rt1-mcu.bin"
73 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
74 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
75 uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
76 uint8_t chr_rom[0x60000]; // Character ROM pointer
80 bool trace1 = false; // ditto...
81 bool looking_at_rom = true; // true = R1, false = R2
82 uint32_t banksw1, banksw2; // Bank switch addresses
83 uint16_t game_over_switch; // Game over delay
84 uint16_t dpc; // Debug pc reg...
85 bool show_scr = true; // Whether or not to show background
86 bool enable_cpu = true; // Whether or not to enable CPUs
87 bool irqGoA = true; // IRQ switch for CPU #1
88 bool irqGoB = true; // IRQ switch for CPU #2
90 uint16_t refresh_ = 0; // Crappy global screen stuff...
93 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
94 uint8_t * psg_adrs[16];
95 uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
97 uint8_t * voc_adrs[32];
98 uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
99 uint8_t * fm_adrs[14];
101 fstream tr; // Tracelog hook
102 uint16_t pcx; // Where we at?
105 //static uint8_t keys[0x1000]; // SDL raw keyboard matrix
107 static char op_mat1[256] = {
108 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
109 0, 0, 5, 5, 0, 0, 4, 4, 0, 5, 8, 0, 8, 5, 6, 6,
110 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
111 7, 7, 7, 7, 6, 6, 6, 6, 0, 5, 5, 5, 8, 5, 5, 5,
112 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
113 5, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 0, 5, 5, 0, 5,
114 7, 0, 0, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7,
115 2, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,
116 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 3, 9, 0,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
119 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
120 8, 8, 8, 9, 8, 8, 8, 0, 8, 8, 8, 8, 9, 0, 9, 0,
121 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
122 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
123 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0,
134 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
135 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7,
136 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2,
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
151 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
152 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
153 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
158 static char mnemonics[256][6] = {
159 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
160 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
161 "PAGE1","PAGE2","NOP ","SYNC ","??? ","??? ","LBRA ","LBSR ",
162 "??? ","DAA ","ORCC ","??? ","ANDCC","SEX ","EXG ","TFR ",
163 "BRA ","BRN ","BHI ","BLS ","BHS ","BLO ","BNE ","BEQ ",
164 "BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
165 "LEAX ","LEAY ","LEAS ","LEAU ","PSHS ","PULS ","PSHU ","PULU ",
166 "??? ","RTS ","ABX ","RTI ","CWAI ","MUL ","RESET","SWI ",
167 "NEGA ","??? ","??? ","COMA ","LSRA ","??? ","RORA ","ASRA ",
168 "LSLA ","ROLA ","DECA ","??? ","INCA ","TSTA ","??? ","CLRA ",
169 "NEGB ","??? ","??? ","COMB ","LSRB ","??? ","RORB ","ASRB ",
170 "LSLB ","ROLB ","DECB ","??? ","INCB ","TSTB ","??? ","CLRB ",
171 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
172 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
173 "NEG ","??? ","??? ","COM ","LSR ","??? ","ROR ","ASR ",
174 "LSL ","ROL ","DEC ","??? ","INC ","TST ","JMP ","CLR ",
175 "SUBA ","CMPA ","SCBA ","SUBD ","ANDA ","BITA ","LDA ","??? ",
176 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","BSR ","LDX ","??? ",
177 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
178 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
179 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
180 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
181 "SUBA ","CMPA ","SBCA ","SUBD ","ANDA ","BITA ","LDA ","STA ",
182 "EORA ","ADCA ","ORA ","ADDA ","CMPX ","JSR ","LDX ","STX ",
183 "SUBB ","CMPB ","SCBB ","ADDD ","ANDB ","BITB ","LDB ","??? ",
184 "EORB ","ADCB ","ORB ","ADDB ","LDD ","??? ","LDU ","??? ",
185 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
186 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
187 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
188 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU ",
189 "SUBB ","CMPB ","SBCB ","ADDD ","ANDB ","BITB ","LDB ","STB ",
190 "EORB ","ADCB ","ORB ","ADDB ","LDD ","STD ","LDU ","STU " },
191 mnemonics2[256][6] = {
192 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
193 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
194 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
195 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
196 "??? ","LBRN ","LBHI ","LBLS ","LBHS ","LBLO ","LBNE ","LBEQ ",
197 "LBVC ","LBVS ","LBPL ","LBMI ","LBGE ","LBLT ","LBGT ","LBLE ",
198 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
199 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI2 ",
200 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
201 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
202 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
203 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
204 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
205 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
206 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
207 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
208 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
209 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","??? ",
210 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
211 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
212 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
213 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
214 "??? ","??? ","??? ","CMPD ","??? ","??? ","??? ","??? ",
215 "??? ","??? ","??? ","??? ","CMPY ","??? ","LDY ","STY ",
216 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
217 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","??? ",
218 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
219 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
220 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
221 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS ",
222 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
223 "??? ","??? ","??? ","??? ","??? ","??? ","LDS ","STS " },
224 mnemonics3[256][6] = {
225 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
226 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
227 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
228 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
229 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
230 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
231 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
232 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","SWI3 ",
233 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
234 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
235 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
236 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
237 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
238 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
239 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
240 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
241 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
242 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
243 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
244 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
245 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
246 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
247 "??? ","??? ","??? ","CMPU ","??? ","??? ","??? ","??? ",
248 "??? ","??? ","??? ","??? ","CMPS ","??? ","??? ","??? ",
249 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
250 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
251 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
252 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
253 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
254 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
255 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? ",
256 "??? ","??? ","??? ","??? ","??? ","??? ","??? ","??? " },
258 "D", "X", "Y", "U", "S", "PC", "??", "??",
259 "A", "B", "CC", "DP", "??", "??", "??", "??" },
260 iregs[4][2] = {"X", "Y", "U", "S" };
264 // Read a byte from memory (without touching PC. Not a Fetch!)
266 uint8_t RdMem(uint16_t addr)
270 // $4000-4300 is RAM shared with the microcontroller...
275 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
287 // Write a byte to memory
289 void WrMem(uint16_t addr, uint8_t b)
292 extern bool charbase; // Needed for screen. Extern it in it??
293 //extern uint16_t sr, ur, xr, yr; // Needed for tracelog
294 //extern uint16_t pcr;
295 /* if ((addr>0x40FF) && (addr<0x4390))
297 tr << hex << addr << ":" << (int)b;
298 //for(int i=0; i<32; i++)
300 // if (gram1[0x4400+i]<0x10) tr << "0";
301 // tr << hex << (uint16_t)gram1[0x4400+i] << " ";
308 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
312 if (((addr >= 0x4180) && (addr <= 0x4191)) || (addr == 0x4380))
313 printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, addr);
317 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
319 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
321 banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
322 if (addr > 0x4284 && addr < 0x42A5 && b)
323 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
326 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
328 game_over_switch = 240; // Set game over delay...
330 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
333 charbase = false; // Char banksw1
335 charbase = true; // Char banksw2
336 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
338 if (refresh_++ == 1) // 30 Hz...
340 BlitChar(screen, chr_rom, gram1);
341 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
344 // IRQ Ack (may also be frame go...
345 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
348 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
355 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
357 uint8_t RdMemB(uint16_t addr)
359 // extern uint16_t cpu2.s, cpu2.u, cpu2.x, cpu2.y; // Needed for tracelog
365 b = gram1[addr + 0x4000];
366 if (addr > 0x1FFF && addr < 0x6000)
367 b = gram1[addr - 0x2000];
369 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
374 /* if ((addr>0x3FFF) && (addr<0x4400)) tr << "R-" << hex << pcx << ": "
376 << (int)looking_at_rom
378 << "] XR:" << xr << " YR:" << yr
379 << " SR:" << sr << " UR:" << ur
386 // Write a byte to memory (2nd processor)
388 void WrMemB(uint16_t addr, uint8_t b)
391 extern bool charbase;
392 //extern uint16_t sr, ur, xr, yr; // Needed for tracelog
393 //extern uint16_t pcr;
394 /* if ((addr>0x00FF) && (addr<0x0390))
396 tr << hex << addr << ":" << (int)b;
397 //for(int i=0; i<32; i++)
399 // if (gram1[0x4400+i]<0x10) tr << "0";
400 // tr << hex << (uint16_t)gram1[0x4400+i] << " ";
407 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
411 if (((addr >= 0x0180) && (addr <= 0x0191)) || (addr == 0x0380))
412 printf("WriteMem: CPU #2 writing $%02X to $%04X...\n", b, addr);
416 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
418 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
419 if (addr > 0x0284 && addr < 0x02A5 && b)
420 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
422 banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
425 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
427 game_over_switch = 240; // Set game over delay...
429 if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
432 gram1[addr + 0x4000] = b;
433 if (addr > 0x1FFF && addr < 0x6000)
434 gram1[addr - 0x2000] = b;
440 // IRQ Ack (may also be frame go...)
441 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
444 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
451 // Display bytes in mem in hex
453 void DisplayBytes(uint16_t src, unsigned long dst)
456 WriteLog("%04X: ", src);
459 dst += 0x10000; // That should fix the FFFF bug...
461 for(unsigned long i=src; i<dst; i++)
463 WriteLog("%02X ", (uint8_t)(looking_at_rom ? RdMem(i) : RdMemB(i)));
467 // Pad the leftover spaces...
468 for(unsigned long i=cnt; i<5; i++)
474 uint8_t Fetch(void) { return RdMem(dpc); }
475 uint16_t FetchW(void) { return (uint16_t)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
476 uint8_t FetchB(void) { return RdMemB(dpc); }
477 uint16_t FetchWB(void) { return (uint16_t)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
481 // Decode a 6809 instruction at 'addr'
485 uint8_t (* DFetch)(); // Decode Fetch() pointer...
486 uint16_t (* DFetchW)(); // Decode FetchW() pointer...
487 DFetch = (looking_at_rom ? Fetch : FetchB);
488 DFetchW = (looking_at_rom ? FetchW : FetchWB);
490 /* extern*/ uint16_t pcr, pcrB; // Pull in 'pcr' from '6809.cpp'
491 uint16_t pc_save = pcr, pcB_save = pcrB;
492 pcr = dpc; pcrB = dpc;
493 uint8_t opcode = DFetch(); // Get the opcode ('fetch' cycle)
494 uint8_t opcode2, operand;
496 uint8_t admode = op_mat1[opcode]; // addressing mode
497 char outbuf[80], mnem[6], tmp[30];
499 strcpy(mnem, mnemonics[opcode]); // Copy page 1 opcode
501 if (opcode == 0x10) // Extended opcode?
503 opcode2 = DFetch(); // Then get next byte
504 admode = op_mat2[opcode2]; // And use it as index into 'op_mat2'
505 strcpy(mnem, mnemonics2[opcode2]); // Overwrite mnemonic
508 if (opcode == 0x11) // Same as above...
511 admode = op_mat3[opcode2];
512 strcpy(mnem, mnemonics3[opcode2]); // Overwrite mnemonic
519 sprintf(outbuf, "???");
522 operand = DFetch(); // Get ZP address
523 sprintf(outbuf, "%s $%02X", mnem, operand);
526 loperand = DFetchW(); // Get ABS address
527 sprintf(outbuf, "%s $%04X", mnem, loperand);
531 operand = DFetch(); // Get offset
532 uint16_t tmpc = (looking_at_rom ? pcr : pcrB);
533 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16_t)(int8_t)operand);
536 case 4: // Long Relative
538 loperand = DFetchW(); // Get long offset
539 uint16_t tmpc = (looking_at_rom ? pcr : pcrB);
540 sprintf(outbuf, "%s $%04X", mnem, tmpc+(int16_t)loperand);
544 sprintf(outbuf, "%s ", mnem);
546 case 6: // Txfr/exchg/push/pull
548 operand = DFetch(); // Get txfr/exg/push/pull byte
550 if ((opcode == 0x1E) || (opcode == 0x1F)) // Is it TXF/EXG?
552 sprintf(tmp, "%s,%s", tregs[operand>>4], tregs[operand&0x0F]);
557 if (operand&0x01) strcat(tmp, "CC ");
558 if (operand&0x02) strcat(tmp, "A ");
559 if (operand&0x04) strcat(tmp, "B ");
560 if (operand&0x08) strcat(tmp, "DP ");
561 if (operand&0x10) strcat(tmp, "X ");
562 if (operand&0x20) strcat(tmp, "Y ");
563 if (operand&0x40) (((opcode==0x34)||(opcode==0x35))
564 ? strcat(tmp, "U ") : strcat(tmp, "S "));
565 if (operand&0x80) strcat(tmp, "PC");
567 sprintf(outbuf, "%s %s", mnem, tmp);
570 case 7: // Indexed (the tough one!)
572 operand = DFetch(); // Get IDX byte
573 uint8_t reg = ((operand & 0x60) >> 5), idxind = ((operand & 0x10) >> 4),
574 lo_nyb = (operand & 0x0F), boff;
579 if (!(operand & 0x80)) // Hi bit set? Then decode 4 bit offset
581 sprintf(tmp, "(%d),%s", (idxind ? -(16-lo_nyb) : lo_nyb),
584 else // Add the ($nnnn,R) code dude...
590 case 1: sprintf(tmp, "(,%s++)", iregs[reg]); break;
591 case 3: sprintf(tmp, "(,--%s)", iregs[reg]); break;
592 case 4: sprintf(tmp, "(,%s)", iregs[reg]); break;
593 case 5: sprintf(tmp, "(B,%s)", iregs[reg]); break;
594 case 6: sprintf(tmp, "(A,%s)", iregs[reg]); break;
596 { boff = DFetch(); sprintf(tmp, "($%02X,%s)", boff,
597 iregs[reg]); break; }
599 { woff = DFetchW(); sprintf(tmp, "($%04X,%s)", woff,
600 iregs[reg]); break; }
601 case 11: sprintf(tmp, "(D,%s)", iregs[reg]); break;
603 { boff = DFetch(); sprintf(tmp, "($%02X,PC)", boff); break; }
605 { woff = DFetchW(); sprintf(tmp, "($%04X,PC)", woff); break; }
607 { woff = DFetchW(); sprintf(tmp, "[$%04X]", woff); break; }
608 default: strcpy(tmp, "??");
615 case 0: sprintf(tmp, ",%s+", iregs[reg]); break;
616 case 1: sprintf(tmp, ",%s++", iregs[reg]); break;
617 case 2: sprintf(tmp, ",-%s", iregs[reg]); break;
618 case 3: sprintf(tmp, ",--%s", iregs[reg]); break;
619 case 4: sprintf(tmp, ",%s", iregs[reg]); break;
620 case 5: sprintf(tmp, "(B),%s", iregs[reg]); break;
621 case 6: sprintf(tmp, "(A),%s", iregs[reg]); break;
623 { boff = DFetch(); sprintf(tmp, "($%02X),%s", boff,
624 iregs[reg]); break; }
626 { woff = DFetchW(); sprintf(tmp, "($%04X),%s", woff,
627 iregs[reg]); break; }
628 case 11: sprintf(tmp, "(D),%s", iregs[reg]); break;
630 { boff = DFetch(); sprintf(tmp, "($%02X),PC", boff); break; }
632 { woff = DFetchW(); sprintf(tmp, "($%04X),PC", woff); break; }
633 default: strcpy(tmp, "??");
638 sprintf(outbuf, "%s %s", mnem, tmp);
642 operand = DFetch(); // Get IMM byte
643 sprintf(outbuf, "%s #$%02X", mnem, operand);
645 case 9: // Long Immediate
646 loperand = DFetchW(); // Get IMM word
647 sprintf(outbuf, "%s #$%04X", mnem, loperand);
651 DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB)); // Show bytes
653 WriteLog("\n"); // display opcode & addressing, etc
654 dpc = (looking_at_rom ? pcr : pcrB); // Advance debug PC
656 pcrB = pcB_save; // Restore PCs
661 // Convert hex to dec
663 uint16_t htod(char * str)
666 int len = strlen(str);
668 for(int i=0; i<len; i++)
670 if (str[i] >= '0' && str[i] <= '9')
671 value = (value << 4) | (unsigned)(str[i] - '0');
672 else if (str[i] >= 'a' && str[i] <= 'f')
673 value = (value << 4) | (unsigned)((str[i] - 'a') + 10);
674 else if (str[i] >= 'A' && str[i] <= 'F')
675 value = (value << 4) | (unsigned)((str[i] - 'A') + 10);
683 // Load 32K file into ROM image space
685 bool Load32KImg(char * filename, uint16_t address)
691 ff.open(filename, ios::binary | ios::in);
696 for(long i=0; i<32768; i++)
699 grom[address+i] = ch;
707 FILE * file = fopen(filename, "rb");
712 fread(&grom[address], 1, 0x8000, file);
720 // Generic Load file into image space
721 // (No error checking performed! Responsibility of caller!)
723 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
729 strcpy(path, "./ROMs/");
730 strcat(path, filename);
731 // ff.open(filename, ios::binary | ios::in); // Open 'da file...
732 ff.open(path, ios::binary | ios::in); // Open 'da file...
736 for(uint32_t i=0; i<length; i++) // Read it in...
742 ff.close(); // Close 'da file...
752 bool ReadColorPROMs(void)
757 // extern uint8_t palette[768]; // Screen physical palette
758 extern uint32_t palette[256]; // Screen physical palette
759 extern uint8_t ccolor[256][8]; // Character color PROM values
760 extern uint8_t scolor[128][16]; // Sprite color PROM values
762 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
766 for(int i=0; i<256; i++) // Load char pallete with PROM values
768 for(int j=0; j<8; j++)
771 ccolor[i][j] = (uint8_t)ch;
778 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
782 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
784 for(int j=0; j<16; j++)
787 scolor[i][j] = (uint8_t)ch;
794 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
795 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
797 // If open was successful...
800 // Palette is 12-bit RGB, we stretch it to 24-bit
801 for(int i=0; i<256; i++)
807 r = (uint8_t)c1 & 0x0F;
808 g = (uint8_t)c1 >> 4;
810 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
817 // PROM5 has the following in it (tile address decoder):
818 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
819 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
828 bool UnpackFonts(void)
830 // uint8_t b1, b2, b3;
834 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
835 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
838 return false; // Return if not found...
840 for(long i=0; i<0x40000; i+=64)
842 for(int j=0; j<64; j+=8)
844 f1.get(b1); f1.get(b2); f2.get(b3);
845 b3 ^= 0xFF; // Invert top data...
846 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
847 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
848 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
849 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
850 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
851 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
852 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
853 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
860 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
861 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
863 for(long i=0x40000; i<0x60000; i+=64)
865 for(int j=0; j<64; j+=8)
867 f1.get(b1); f1.get(b2); f2.get(b3);
868 b3 ^= 0xFF; // Invert top data
869 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
870 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
871 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
872 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
873 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
874 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
875 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
876 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
883 return true; // Made it!
888 // Get length of sample from WAV format
890 uint32_t GetWAVLength(fstream & file)
895 file.ignore(16); // Skip header BS
897 for(int i=0; i<2; i++)
899 file.get(ch); len = (int)(uint8_t)ch;
900 file.get(ch); len |= (int)(uint8_t)ch << 8;
901 file.get(ch); len |= (int)(uint8_t)ch << 16;
902 file.get(ch); len |= (int)(uint8_t)ch << 24;
904 file.ignore(len + 4); // Skip intermediate data
907 file.get(ch); len = (int)(uint8_t)ch; // & finally get length of data
908 file.get(ch); len |= (int)(uint8_t)ch << 8;
909 file.get(ch); len |= (int)(uint8_t)ch << 16;
910 file.get(ch); len |= (int)(uint8_t)ch << 24;
917 // Load PSG samples from disk
925 for(int i=0; i<16; i++)
929 psg_adrs[i] = NULL; // Zero out pointer
930 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
932 fp.open(file, ios::binary | ios::in); // Attempt to open it...
936 len = GetWAVLength(fp); // Get WAV data length...
937 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
939 if (psg_adrs[i] != NULL)
941 for(int j=0; j<(signed)len; j++)
944 psg_adrs[i][j] = ch; // & load it in...
948 // cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
949 printf("Found sample file: %s\t[Length: %u]\n", file, len);
959 // Load FM samples from disk
967 for(int i=0; i<14; i++)
971 fm_adrs[i] = NULL; // Zero out pointer
972 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
974 fp.open(file, ios::binary | ios::in); // Attempt to open it...
978 len = GetWAVLength(fp); // Get WAV length...
979 fm_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
981 if (fm_adrs[i] != NULL)
983 for(int j=0; j<(signed)len; j++)
986 fm_adrs[i][j] = ch; // & load it in...
990 // cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
991 printf("Found sample file: %s\t[Length: %u]\n", file, len);
1003 int main(int argc, char * argv[])
1005 InitLog("thunder.log");
1007 extern bool disasm; // From 'V6809.CPP'
1008 extern bool charbase; // From 'SCREEN.CPP'
1012 fstream ff; // Declare fstream without file hooks...
1013 bool brk = false, brk2 = false; // Breakpoint set flag
1014 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
1015 bool running; // CPU running state flag...
1016 bool self_test = false; // Self-test switch
1017 bool scr_type = false; // false=chars, true=pixels
1018 uint16_t debounce = 0; // Key de-bounce counter
1019 uint16_t fire_debounce = 0; // Fire button debounce counter
1020 // bool refresh2 = true; // Default to 60 Hz...
1021 uint8_t x; // General placeholder...
1022 bool active = true; // Program running flag
1024 SDL_Event event; // SDL "event"
1025 extern uint8_t palette[768]; // Screen physical palette
1026 uint32_t ticks, oldTicks;
1028 cout << endl << "THUNDER v"THUNDER_VERSION" ";
1029 cout << "by James Hammons" << endl;
1030 cout << "Serial #20149404 / Prerelease" << endl;
1031 cout << "© 2003, 2014 Underground Software" << endl << endl;
1033 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
1037 cout << "Initializing SDL..." << endl;
1039 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
1041 cout << "Couldn't initialize SDL: " << SDL_GetError() << endl;
1046 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
1048 // keys = SDL_GetKeyState(NULL); // Get the SDL keyboard matrix
1050 gram = gram1; grom = grom1; // Needed only for debugger
1053 for(long i=0; i<0x10000; i++)
1055 gram[i] = 0; grom[i] = 0; // Zero out memory
1056 gram2[i] = 0; grom2[i] = 0;
1059 memset(gram, 0, 0x10000);
1060 memset(grom, 0, 0x10000);
1061 memset(gram2, 0, 0x10000);
1062 memset(grom2, 0, 0x10000);
1065 game_over_switch = 0; // Init game over delay
1066 // 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;
1068 cout << "Loading ROMs..." << endl;
1069 // LoadCMOS(); // Load CMOS at $CC00-$CFFF
1070 if (!ReadColorPROMs()) // Load virtual PROMs
1071 { cout << "Could not open PROM files!" << endl; return -1; }
1073 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
1074 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
1076 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
1077 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
1079 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
1080 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
1082 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
1083 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
1085 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
1086 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
1088 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
1089 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
1091 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
1092 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
1094 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
1095 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
1097 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
1098 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
1100 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
1101 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
1103 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
1104 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
1106 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
1107 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
1109 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
1110 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
1112 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
1113 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
1115 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
1116 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
1118 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
1119 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
1121 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
1122 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
1124 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
1125 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
1127 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
1129 cout << "Could not open font files!" << endl;
1133 // Load samples if they're there...
1137 // Quick 'n' Dirty voice dump (sound 0x0E)
1138 /* uint32_t adc = (voice_rom[26]<<8) | voice_rom[27];
1139 bool doneWitIt = false;
1143 if (voice_rom[adc] < 0x10) tr << "0";
1144 tr << hex << (int)voice_rom[adc] << " ";
1145 if (crh++ > 24) { crh = 0; tr << endl; }
1146 if ((voice_rom[adc] == 0xFF) && (voice_rom[adc-1] != 0x00))
1151 // Set up V6809 execution contexts
1153 memset(&cpu1, 0, sizeof(V6809REGS));
1156 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1158 memset(&cpu2, 0, sizeof(V6809REGS));
1159 cpu2.RdMem = RdMemB;
1160 cpu2.WrMem = WrMemB;
1161 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1163 bool firstTime = true; // kludge...
1165 WriteLog("About to go to the main loop...\n");
1171 firstTime = false; // crappy kludge...
1178 if (lbuff[0] == 'd')
1185 printf("%04X: ", dpc);
1186 uint16_t pc_save = cpu1.pc, pcB_save = cpu2.pc;
1187 cpu1.pc = dpc; cpu2.pc = dpc;
1188 for(int i=0; i<16; i++)
1189 printf("%02X ", (looking_at_rom ? Fetch() : FetchB()));
1191 cpu1.pc = dpc; cpu2.pc = dpc;
1192 for(int i=0; i<16; i++)
1194 uint8_t a = (looking_at_rom ? Fetch() : FetchB());
1195 if (a<10) cout << (char)(a+48);
1196 if ((a>9) && (a<37)) cout << (char)(a+55);
1197 if (a>36) cout << ".";
1200 dpc = (looking_at_rom ? cpu1.pc : cpu2.pc);
1201 cpu1.pc = pc_save; cpu2.pc = pcB_save;
1203 else if (lbuff[0] == 'e')
1210 printf("%04X: ", dpc);
1211 for(int i=0; i<16; i++) printf("%02X ", (uint8_t)gram[dpc++]);
1214 else if (lbuff[0] == 'l')
1221 for(int i=0; i<23; i++)
1224 else if (lbuff[0] == 't')
1235 Execute6809(&cpu1, 1);
1237 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1238 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1239 cout << " iclock=" << cpu1.clock << endl;
1245 Execute6809(&cpu2, 1);
1247 printf("A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1248 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1249 cout << " iclock=" << cpu2.clock << endl;
1252 else if ((lbuff[0] == 'r') || (lbuff[0] == 'c')) // Run/continue...
1254 WriteLog("Executing 'run' command...\n");
1255 uint32_t my_clock = 0;
1256 running = true; // Set running status...
1258 SetRefreshRate(refresh2); // Tell GUI our refresh rate
1259 //for(uint16_t i=0; i<0x8000; i++) gram2[i] = grom3[i]; //Temp
1261 if (lbuff[0] == 'r') // If run, then reset CPUs
1263 WriteLog("Executing secondary 'run' command...\n");
1265 // This is data that is supposed to come from the MCU... So that's why it hangs
1266 gram1[0x4182] = 0xA6; // Temp kludge
1267 gram1[0x4184] = 0xA6;
1268 gram1[0x4183] = 0x00; // More of the same
1269 gram1[0x4185] = 0x00;
1271 banksw1 = 0; // Will this work?
1273 // iclock = 0; // Reset instr clock #1...
1274 InitGUI(); // Reset # of coins
1277 cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
1280 lbuff[0] = 32; cpu1.pc = htod(lbuff);
1282 else cpu1.cc = 0xFF; // Set CC register
1284 cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
1285 cpu2.cc = 0xFF; // Set CC register
1286 while(iclock < 8000) // was 17000, 20000, 5000
1288 Execute6809(&cpu1, 1); Execute6809(&cpu2, 1);
1292 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
1293 // Will *this* help video sync? NO
1294 while (cpu1.clock < 8000) // was 17000, 20000, 5000
1296 Execute6809(&cpu1, 1);
1297 Execute6809(&cpu2, 1);
1302 WriteLog("About to set up screen...\n");
1304 screen = SDL_SetVideoMode(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 8, SDL_SWSURFACE | SDL_DOUBLEBUF);
1307 cout << "Failed to initialize screen!" << endl;
1315 SDL_Color colors[256];
1317 for(int i=0; i<256; i++)
1319 colors[i].r = palette[i*3+0];
1320 colors[i].g = palette[i*3+1];
1321 colors[i].b = palette[i*3+2];
1324 SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
1328 for(int i=0; i<256; i++)
1329 keys[i] = 0; // Clear keyboard buffer...
1332 oldTicks = SDL_GetTicks();
1334 WriteLog("About to set up audio...\n");
1336 // This crap SHOULD be in sound.cpp (not yet created)...
1337 SDL_AudioSpec desired, obtained;
1338 desired.freq = 22050;
1339 desired.format = AUDIO_U8;
1340 desired.channels = 1;
1341 desired.samples = 600;
1342 desired.callback = SoundFunc;
1343 desired.userdata = NULL;
1344 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
1345 if (SDL_OpenAudio(&desired, &obtained) < 0)
1347 cout << "Couldn't open audio: " << SDL_GetError() << endl;
1351 SDL_PauseAudio(0); // Get that audio going!
1354 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
1355 RenderScreenBuffer();
1356 WriteLog("About to enter main loop...\n");
1359 HandleGUIDebounce(); // Debounce GUI keys
1361 if (game_over_switch)
1363 game_over_switch--; // Countdown...
1365 if (game_over_switch == 0)
1366 gram1[0x4380] = 0; // Kill music!
1369 //testing... (works)
1370 //gram1[0x423D] = 1;
1371 //gram1[0x423D] = self_test; // Reset DSW1-1
1372 gram1[0x4268] = 0; // Reset Video test
1373 gram1[0x427A] = 0; gram1[0x427C] = 0;
1374 gram1[0x427B] = 0; gram1[0x427D] = 0;
1375 gram1[0x427E] = 0; gram1[0x427F] = 0;
1376 gram1[0x4280] = 0; gram1[0x4281] = 0;
1377 gram1[0x4276] = 0; gram1[0x426A] = 0;
1378 gram1[0x4278] = 0; gram1[0x426C] = 0;
1379 gram1[0x4262] = 0; gram1[0x4260] = 0;
1380 //gram1[0x4247] = 0;
1382 // SDL key handling...
1384 // SDL_PumpEvents(); // Force key events into the buffer.
1387 while (SDL_PollEvent(&event))
1392 if (event.key.keysym.sym == SDLK_ESCAPE)
1394 else if (event.key.keysym.sym == SDLK_F10)
1395 gram1[0x41A5]++; // Coin? (F10)
1396 else if (event.key.keysym.sym == SDLK_c)
1397 gram1[0x418C]++; // ? (C) Start
1402 if (keys[SDLK_ESCAPE])
1403 running = false; // ESC to exit...
1406 debounce--; // Debounce toggle keys...
1411 self_test = !self_test; // Self-test (F1-toggle)
1412 debounce = 10; // Key debounce value...
1416 gram1[0x4268] = 1; // Video test (F2)
1417 debounce = 10; // Key debounce value...
1421 scr_type = !scr_type; // Toggle screen (F12)
1422 debounce = 10; // Key debounce value...
1426 show_scr = !show_scr; // Toggle bkgrnd (F3)
1431 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1436 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1437 SetRefreshRate(refresh2); // Inform GUI of refresh
1442 debounce = 10; // Key debounce value...
1444 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1446 SpawnSound(USERSOUND, SCAMERA);
1450 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1460 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1461 if (keys[SDLK_RIGHT]) // Right arrow
1464 SelectRight(); // If GUI active...
1467 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1468 gram1[0x427F] = 1; // Stick right
1471 if (keys[SDLK_LEFT])
1474 SelectLeft(); // If GUI active...
1477 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1478 gram1[0x4281] = 1; // Left arrow
1484 SelectUp(); // If GUI active...
1487 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1488 gram1[0x427B] = 1; // Up arrow
1491 if (keys[SDLK_DOWN])
1494 SelectDown(); // If GUI active...
1497 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1498 gram1[0x427D] = 1; // Down arrow
1501 if (keys[SDLK_RETURN]) // Return
1503 uint8_t retval = UserSelectedSomething();
1508 if (retval == REFRESH)
1510 refresh2 = !refresh2;
1511 SetRefreshRate(refresh2);
1516 gram1[0x427A] = 1; // (1)
1519 gram1[0x427C] = 1; // (2)
1522 gram1[0x427E] = 1; // (3)
1525 gram1[0x4280] = 1; // (5)
1527 if (keys[SDLK_q] | keys[29])
1528 gram1[0x4276] = 1; // (Q) Jump
1531 gram1[0x426A] = 1; // (W)
1536 if (keys[SDLK_e] | keys[56]) // (E) Fire
1542 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1550 gram1[0x426C] = 1; // (R)
1553 gram1[0x4262] = 1; // (T)
1556 gram1[0x4260] = 1; // (Y)
1559 gram1[0x41A5]++; // Coin? (F10)
1562 gram1[0x4189]++; // ? (Z) credits l dig
1565 gram1[0x418A]++; // ? (X) credits r dig
1568 gram1[0x418C]++; // ? (C) Start
1571 gram1[0x418D]++; // ? (V)
1574 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1576 // if (keys[SDLK_F8])
1578 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1579 // charbase = false; // Switch chars out...
1581 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1583 if (keys[SDLK_F11]) // (F11)
1585 Execute6809(&cpu1, 10);
1586 Execute6809(&cpu2, 10);
1589 //F12 is used above, but the values are ignored. So we'll do it here too.
1592 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1593 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1596 if (keys[SDLK_d]) // (D) start disassembly
1600 gram1[0x5606] = 0x00;
1603 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1604 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1608 gram1[0x5FF3] = 0x02;
1609 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1617 // We can do this here because we're not executing the cores yet.
1618 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1619 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1620 // while (cpu1.clock < 25000)
1621 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1622 // 25600 cycles/frame
1623 // Setting interleave to 25 and below causes the V6809 core to hang...
1624 // 32 gets to the title screen before hanging...
1625 // 40 works, until it doesn't... :-P
1628 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1629 for(uint32_t i=0; i<640; i++)
1630 // for(uint32_t i=0; i<1280; i++)
1632 // Gay, but what are ya gonna do?
1633 // There's better ways, such as keeping track of when slave writes to master, etc...
1634 Execute6809(&cpu1, 40);
1635 Execute6809(&cpu2, 40);
1637 } // END: enable_cpu
1639 // if (refresh_++ == 1) // 30 Hz...
1642 // BlitWilliamsScreen(gram1); // Display the screen...
1644 // BlitChar(screen, chr_rom, gram1);
1645 // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
1649 //temp, for testing...
1650 BlitChar(screen, chr_rom, gram1);
1652 // Speed throttling happens here...
1653 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1654 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1655 SDL_Delay(1); // Release our timeslice...
1657 oldTicks = SDL_GetTicks();
1658 //cout << "Finished frame..." << endl;
1661 // Stop_audio_output();
1663 // ReleaseKeyboard(); // Release the interrupt...
1664 // RestoreOldMode(); // Restore screen
1665 if (brk && (cpu1.pc == brkpnt))
1666 cout << "CPU 1: Break at " << hex << cpu1.pc << endl;
1667 if (brk2 && (cpu2.pc == brkpnt2))
1668 cout << "CPU 2: Break at " << hex << cpu2.pc << endl;
1670 lbuff[0] = 'q'; // Temp kludge...
1672 else if (lbuff[0] == 'b') // Set/clear breakpoint
1677 brkpnt = htod(lbuff);
1679 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1684 cout << "Breakpoint cleared" << endl;
1687 else if (lbuff[0] == 'a') // Set/clear breakpoint #2
1692 brkpnt2 = htod(lbuff);
1694 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1699 cout << "Breakpoint cleared" << endl;
1702 else if (lbuff[0] == 'i') // Inspect registers
1704 printf("CPU1: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1705 cpu1.a, cpu1.b, cpu1.cc, cpu1.dp, cpu1.x, cpu1.y, cpu1.s, cpu1.u, cpu1.pc);
1706 cout << " iclk=" << dec << cpu1.clock << endl;
1707 printf("CPU2: A=%02X B=%02X CC=%02X DP=%02X X=%04X Y=%04X S=%04X U=%04X PC=%04X",
1708 cpu2.a, cpu2.b, cpu2.cc, cpu2.dp, cpu2.x, cpu2.y, cpu2.s, cpu2.u, cpu2.pc);
1709 cout << " iclk=" << dec << cpu2.clock << endl;
1712 cout << "Breakpoint #1 set at " << hex << brkpnt << dec << endl;
1715 cout << "Breakpoint #2 set at " << hex << brkpnt2 << dec << endl;
1717 else if (strncmp(lbuff, "swap", 4) == 0) // Swap ROMs
1719 looking_at_rom = !looking_at_rom;
1720 cout << "Swapped: Looking at ";
1721 (looking_at_rom ? cout << "ROM #1" : cout << "ROM #2");
1724 else if (strncmp(lbuff, "seek", 4) == 0) // Seek non-zero bytes in RAM
1729 for(int i=0; i<4; i++)
1738 while ((x == 0) && (dpc != 0xFFFF)); // Keep going until something found
1741 printf("%04X: ", dpc); // Show data found...
1743 for(int i=0; i<16; i++)
1744 printf("%02X ", gram1[(uint16_t)(dpc+i)]);
1748 for(int i=0; i<16; i++)
1750 uint8_t a = gram1[dpc++];
1753 cout << (char)(a+48);
1754 if ((a>9) && (a<37))
1755 cout << (char)(a+55);
1762 else if (lbuff[0] == 'v') // View screen
1764 BlitChar(screen, chr_rom, gram1);
1768 if (lbuff[0] == 'q')
1769 active = false; //break; // Quit
1772 SDL_Quit(); // Shut down SDL
1774 for(int i=0; i<16; i++)
1776 delete[] psg_adrs[i]; // Deallocate if loaded
1778 for(int i=0; i<14; i++)
1780 delete[] fm_adrs[i]; // Deallocate if loaded
1788 Hitachi uC runs at 6.144 MHz
1789 YM2151 runs at 3.579580 MHz
1792 Rolling Thunder Memory map
1793 --------------------------
1794 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1795 map is inferred by program behaviour. The customs also handle internally irq
1798 The main CPU memory map is the same in all games because CUS47 is used by all
1799 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1800 replaced by other chips.
1802 All RAM is shared between main and sub CPU, except for sound RAM which is
1803 shared between main and sound CPU; the portion of object RAM that is overlapped
1804 by sound RAM is used exclusively by the sub CPU.
1808 Address Dir Data Name Description
1809 ------------------- --- -------- --------- -----------------------
1810 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1811 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1812 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1813 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1814 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1815 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1816 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1817 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1818 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1819 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1820 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1821 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1822 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1823 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1824 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1825 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1826 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1827 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1828 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1830 [1] Note that this is partially overlapped by sound RAM
1831 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1836 Address Dir Data Name Description
1837 ------------------- --- -------- --------- -----------------------
1838 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1839 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1840 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1841 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1842 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1843 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1844 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1845 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1846 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1847 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1848 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1849 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1850 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1851 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1853 [1] Only used by Rolling Thunder
1858 Address Dir Data Name Description
1859 ------------------- --- -------- --------- -----------------------
1860 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1861 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1862 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1863 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1864 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1865 0010 0--- --01 ---- n.c.
1866 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1867 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1868 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1869 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1870 1011 0--- ---- ---- W unknown (CUS41)
1871 1011 1--- ---- ---- W unknown (CUS41)
1872 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1877 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1878 in rthunder. The two 6809 in this game synchronize using a semaphore at
1879 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1880 and then increments 5606. While it does its quick things (which require
1881 about 40 clock cycles) it expects CPU2 to clear 5607.
1882 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1883 mode. I haven't investigated on the cause.
1885 - There are two watchdogs, one per CPU (or maybe three). Handling them
1886 separately is necessary to allow entering service mode without manually
1887 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1890 - The sprite hardware buffers spriteram: the program writes the sprite list to
1891 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1892 sprite RAM to signal the chip that the list is complete. The chip will copy
1893 the list from 4-9 to 10-15 and use it from there. This has not been verified
1894 on the real hardware, but it is the most logical way of doing it.
1895 Emulating this behaviour and not using an external buffer is important in
1896 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1897 is not written to. If we buffered spriteram to an external buffer, this would
1898 cause dangling sprites because the buffer would not be updated.
1900 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1901 entering a door. The *closed* door is made of tiles, but the *moving* door is
1902 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1903 there is one frame where neither the tile-based closed door nor the
1904 sprite-based moving door is shown, so it flickers. This behavior has been
1905 confirmed on a real PCB.
1909 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1910 but they don't seem to work as expected. During the first few frames they are
1911 written out of order and hooking them up in the usual way causes the MCU to
1912 stop receiving interrupts.