Mercurial > repos > blastem
comparison blastem.c @ 88:c339559f1d4f
Forgot to add blastem main file earlier
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 26 Dec 2012 21:50:48 -0800 |
parents | |
children | 9757b4c6c33f |
comparison
equal
deleted
inserted
replaced
87:60b5c9e2f4e0 | 88:c339559f1d4f |
---|---|
1 #include "68kinst.h" | |
2 #include "m68k_to_x86.h" | |
3 #include "mem.h" | |
4 #include "vdp.h" | |
5 #include "render.h" | |
6 #include "blastem.h" | |
7 #include <stdio.h> | |
8 #include <stdlib.h> | |
9 | |
10 #define CARTRIDGE_WORDS 0x200000 | |
11 #define RAM_WORDS 32 * 1024 | |
12 #define MCLKS_PER_68K 7 | |
13 //TODO: Figure out the exact value for this | |
14 #define MCLKS_PER_FRAME (MCLKS_LINE*262) | |
15 #define CYCLE_NEVER 0xFFFFFFFF | |
16 | |
17 uint16_t cart[CARTRIDGE_WORDS]; | |
18 uint16_t ram[RAM_WORDS]; | |
19 | |
20 io_port gamepad_1; | |
21 io_port gamepad_2; | |
22 | |
23 #ifndef MIN | |
24 #define MIN(a,b) ((a) < (b) ? (a) : (b)) | |
25 #endif | |
26 | |
27 int load_rom(char * filename) | |
28 { | |
29 FILE * f = fopen(filename, "rb"); | |
30 if (!f) { | |
31 return 0; | |
32 } | |
33 fseek(f, 0, SEEK_END); | |
34 long filesize = ftell(f); | |
35 fseek(f, 0, SEEK_SET); | |
36 fread(cart, 2, MIN(filesize/2, CARTRIDGE_WORDS), f); | |
37 fclose(f); | |
38 for(unsigned short * cur = cart; cur - cart < (filesize/2); ++cur) | |
39 { | |
40 *cur = (*cur >> 8) | (*cur << 8); | |
41 } | |
42 //TODO: Mirror ROM | |
43 return 1; | |
44 } | |
45 | |
46 uint16_t read_dma_value(uint32_t address) | |
47 { | |
48 //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do div by 2 | |
49 if (address < 0x200000) { | |
50 return cart[address]; | |
51 } else if(address >= 0x700000) { | |
52 return ram[address & 0x7FFF]; | |
53 } | |
54 //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area | |
55 return 0; | |
56 } | |
57 | |
58 #define VINT_CYCLE ((MCLKS_LINE * 226)/MCLKS_PER_68K) | |
59 | |
60 m68k_context * sync_components(m68k_context * context) | |
61 { | |
62 //TODO: Handle sync targets smaller than a single frame | |
63 vdp_context * v_context = context->next_context; | |
64 uint32_t mclks = context->current_cycle * MCLKS_PER_68K; | |
65 if (mclks >= MCLKS_PER_FRAME) { | |
66 //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); | |
67 vdp_run_context(v_context, MCLKS_PER_FRAME); | |
68 wait_render_frame(v_context); | |
69 mclks -= MCLKS_PER_FRAME; | |
70 vdp_adjust_cycles(v_context, MCLKS_PER_FRAME); | |
71 io_adjust_cycles(&gamepad_1, context->current_cycle, MCLKS_PER_FRAME/MCLKS_PER_68K); | |
72 io_adjust_cycles(&gamepad_2, context->current_cycle, MCLKS_PER_FRAME/MCLKS_PER_68K); | |
73 context->current_cycle -= MCLKS_PER_FRAME/MCLKS_PER_68K; | |
74 if (mclks) { | |
75 vdp_run_context(v_context, mclks); | |
76 } | |
77 if (v_context->regs[REG_MODE_2] & 0x20 && ((context->status & 0x7) < 6)) { | |
78 if (context->int_cycle > VINT_CYCLE) { | |
79 context->int_cycle = VINT_CYCLE; | |
80 context->int_num = 6; | |
81 if (context->int_cycle < context->sync_cycle) { | |
82 context->target_cycle = context->int_cycle; | |
83 } | |
84 } | |
85 } else { | |
86 context->int_cycle = 0xFFFFFFFF; | |
87 context->target_cycle = context->sync_cycle; | |
88 } | |
89 } else { | |
90 //printf("running VDP for %d cycles\n", mclks - v_context->cycles); | |
91 vdp_run_context(v_context, mclks); | |
92 if (v_context->regs[REG_MODE_2] & 0x20 && ((context->status & 0x7) < 6)) { | |
93 if (context->int_cycle > VINT_CYCLE) { | |
94 context->int_cycle = VINT_CYCLE; | |
95 context->int_num = 6; | |
96 if (context->int_cycle < context->sync_cycle && context->int_cycle < context->current_cycle) { | |
97 context->target_cycle = context->int_cycle; | |
98 } | |
99 } | |
100 if (context->int_cycle <= context->current_cycle) { | |
101 context->int_cycle = CYCLE_NEVER; | |
102 context->target_cycle = context->sync_cycle; | |
103 } | |
104 } else { | |
105 context->int_cycle = CYCLE_NEVER; | |
106 context->target_cycle = context->sync_cycle; | |
107 } | |
108 } | |
109 return context; | |
110 } | |
111 | |
112 m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value) | |
113 { | |
114 //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); | |
115 sync_components(context); | |
116 vdp_context * v_context = context->next_context; | |
117 if (vdp_port < 0x10) { | |
118 if (vdp_port < 4) { | |
119 vdp_data_port_write(v_context, value); | |
120 } else if(vdp_port < 8) { | |
121 int blocked = vdp_control_port_write(v_context, value); | |
122 if (blocked) { | |
123 while(v_context->flags & FLAG_DMA_RUN) { | |
124 vdp_run_dma_done(v_context, MCLKS_PER_FRAME); | |
125 if (v_context->cycles >= MCLKS_PER_FRAME) { | |
126 wait_render_frame(v_context); | |
127 vdp_adjust_cycles(v_context, MCLKS_PER_FRAME); | |
128 io_adjust_cycles(&gamepad_1, v_context->cycles/MCLKS_PER_68K, MCLKS_PER_FRAME/MCLKS_PER_68K); | |
129 io_adjust_cycles(&gamepad_2, v_context->cycles/MCLKS_PER_68K, MCLKS_PER_FRAME/MCLKS_PER_68K); | |
130 } | |
131 } | |
132 context->current_cycle = v_context->cycles / MCLKS_PER_68K; | |
133 } else { | |
134 if (v_context->regs[REG_MODE_2] & 0x20 && ((context->status & 0x7) < 6)) { | |
135 if (context->int_cycle > VINT_CYCLE) { | |
136 context->int_cycle = VINT_CYCLE; | |
137 context->int_num = 6; | |
138 if (context->int_cycle < context->sync_cycle) { | |
139 context->target_cycle = context->int_cycle; | |
140 } | |
141 } | |
142 } else { | |
143 context->int_cycle = 0xFFFFFFFF; | |
144 context->target_cycle = context->sync_cycle; | |
145 } | |
146 } | |
147 } else { | |
148 printf("Illegal write to HV Counter port %X\n", vdp_port); | |
149 exit(1); | |
150 } | |
151 context->current_cycle = v_context->cycles/MCLKS_PER_68K; | |
152 } else if (vdp_port < 0x18) { | |
153 //TODO: Implement PSG | |
154 } else { | |
155 //TODO: Implement undocumented test register(s) | |
156 } | |
157 return context; | |
158 } | |
159 | |
160 m68k_context * vdp_port_read(uint32_t vdp_port, m68k_context * context) | |
161 { | |
162 sync_components(context); | |
163 vdp_context * v_context = context->next_context; | |
164 if (vdp_port < 0x10) { | |
165 if (vdp_port < 4) { | |
166 context->value = vdp_data_port_read(v_context); | |
167 } else if(vdp_port < 8) { | |
168 context->value = vdp_control_port_read(v_context); | |
169 } else { | |
170 //TODO: Implement H/V counter | |
171 context->value = 0; | |
172 } | |
173 context->current_cycle = v_context->cycles/MCLKS_PER_68K; | |
174 } else { | |
175 printf("Illegal read from PSG or test register port %X\n", vdp_port); | |
176 exit(1); | |
177 } | |
178 return context; | |
179 } | |
180 | |
181 #define TH 0x40 | |
182 #define TH_TIMEOUT 8000 | |
183 #define Z80_ACK_DELAY 3 //TODO: Calculate this on the fly based on how synced up the Z80 and 68K clocks are | |
184 | |
185 uint8_t reset = 1; | |
186 uint8_t busreq = 0; | |
187 uint8_t busack = 0; | |
188 uint32_t busack_cycle = CYCLE_NEVER; | |
189 uint8_t new_busack = 0; | |
190 | |
191 void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction) | |
192 { | |
193 uint8_t control = pad->control | 0x80; | |
194 uint8_t th = control & pad->output; | |
195 if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { | |
196 printf("adjust_cycles | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, current_cycle); | |
197 } | |
198 if (current_cycle >= pad->timeout_cycle) { | |
199 pad->th_counter = 0; | |
200 } else { | |
201 pad->timeout_cycle -= deduction; | |
202 } | |
203 if (busack_cycle < CYCLE_NEVER && current_cycle < busack_cycle) { | |
204 busack_cycle -= deduction; | |
205 } | |
206 } | |
207 | |
208 void io_data_write(io_port * pad, m68k_context * context, uint8_t value) | |
209 { | |
210 if (pad->control & TH) { | |
211 //check if TH has changed | |
212 if ((pad->output & TH) ^ (value & TH)) { | |
213 if (context->current_cycle >= pad->timeout_cycle) { | |
214 pad->th_counter = 0; | |
215 } | |
216 if (!(value & TH)) { | |
217 pad->th_counter++; | |
218 } | |
219 pad->timeout_cycle = context->current_cycle + TH_TIMEOUT; | |
220 } | |
221 } | |
222 pad->output = value; | |
223 } | |
224 | |
225 void io_data_read(io_port * pad, m68k_context * context) | |
226 { | |
227 uint8_t control = pad->control | 0x80; | |
228 uint8_t th = control & pad->output; | |
229 uint8_t input; | |
230 if (context->current_cycle >= pad->timeout_cycle) { | |
231 pad->th_counter = 0; | |
232 } | |
233 if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { | |
234 printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, context->current_cycle); | |
235 } | |
236 if (th) { | |
237 if (pad->th_counter == 2) { | |
238 input = pad->input[GAMEPAD_EXTRA]; | |
239 } else { | |
240 input = pad->input[GAMEPAD_TH1]; | |
241 } | |
242 } else { | |
243 if (pad->th_counter == 2) { | |
244 input = pad->input[GAMEPAD_TH0] | 0xF; | |
245 } else if(pad->th_counter == 3) { | |
246 input = pad->input[GAMEPAD_TH0] & 0x30; | |
247 } else { | |
248 input = pad->input[GAMEPAD_TH0]; | |
249 } | |
250 } | |
251 context->value = ((~input) & (~control)) | (pad->output & control); | |
252 /*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { | |
253 printf ("value: %X\n", context->value); | |
254 }*/ | |
255 } | |
256 | |
257 m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) | |
258 { | |
259 if (location < 0x100) { | |
260 switch(location/2) | |
261 { | |
262 case 0x1: | |
263 io_data_write(&gamepad_1, context, value); | |
264 break; | |
265 case 0x2: | |
266 io_data_write(&gamepad_2, context, value); | |
267 break; | |
268 case 0x3://PORT C Data | |
269 break; | |
270 case 0x4: | |
271 gamepad_1.control = value; | |
272 break; | |
273 case 0x5: | |
274 gamepad_2.control = value; | |
275 break; | |
276 } | |
277 } else { | |
278 if (location == 0x1100) { | |
279 if (busack_cycle > context->current_cycle) { | |
280 busack = new_busack; | |
281 busack_cycle = CYCLE_NEVER; | |
282 } | |
283 if (value & 1) { | |
284 busreq = 1; | |
285 if(!reset) { | |
286 busack_cycle = context->current_cycle + Z80_ACK_DELAY; | |
287 new_busack = 1; | |
288 } | |
289 } else { | |
290 busreq = 0; | |
291 busack_cycle = CYCLE_NEVER; | |
292 busack = 0; | |
293 } | |
294 } else if (location == 0x1200) { | |
295 if (value & 1) { | |
296 if (reset && busreq) { | |
297 new_busack = 1; | |
298 busack_cycle = context->current_cycle + Z80_ACK_DELAY; | |
299 } | |
300 reset = 0; | |
301 } else { | |
302 busack = 0; | |
303 reset = 1; | |
304 } | |
305 } | |
306 } | |
307 return context; | |
308 } | |
309 | |
310 m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value) | |
311 { | |
312 if (location < 0x100) { | |
313 switch(location/2) | |
314 { | |
315 case 0x1: | |
316 io_data_write(&gamepad_1, context, value); | |
317 break; | |
318 case 0x2: | |
319 io_data_write(&gamepad_2, context, value); | |
320 break; | |
321 case 0x3://PORT C Data | |
322 break; | |
323 case 0x4: | |
324 gamepad_1.control = value; | |
325 break; | |
326 case 0x5: | |
327 gamepad_2.control = value; | |
328 break; | |
329 } | |
330 } else { | |
331 printf("IO Write of %X to %X\n", value, location); | |
332 if (location == 0x1100) { | |
333 if (busack_cycle > context->current_cycle) { | |
334 busack = new_busack; | |
335 busack_cycle = CYCLE_NEVER; | |
336 } | |
337 if (value & 0x100) { | |
338 busreq = 1; | |
339 if(!reset) { | |
340 busack_cycle = context->current_cycle + Z80_ACK_DELAY; | |
341 new_busack = 1; | |
342 } | |
343 } else { | |
344 busreq = 0; | |
345 busack_cycle = CYCLE_NEVER; | |
346 busack = 0; | |
347 } | |
348 } else if (location == 0x1200) { | |
349 if (value & 0x100) { | |
350 if (reset && busreq) { | |
351 new_busack = 1; | |
352 busack_cycle = context->current_cycle + Z80_ACK_DELAY; | |
353 } | |
354 reset = 0; | |
355 } else { | |
356 busack = 0; | |
357 reset = 1; | |
358 } | |
359 } | |
360 } | |
361 return context; | |
362 } | |
363 | |
364 m68k_context * io_read(uint32_t location, m68k_context * context) | |
365 { | |
366 if (location < 0x100) { | |
367 switch(location/2) | |
368 { | |
369 case 0x0: | |
370 //version bits should be 0 for now since we're not emulating TMSS | |
371 //Not sure about the other bits | |
372 context->value = 0; | |
373 break; | |
374 case 0x1: | |
375 io_data_read(&gamepad_1, context); | |
376 break; | |
377 case 0x2: | |
378 io_data_read(&gamepad_2, context); | |
379 break; | |
380 case 0x3://PORT C Data | |
381 break; | |
382 case 0x4: | |
383 context->value = gamepad_1.control; | |
384 break; | |
385 case 0x5: | |
386 context->value = gamepad_2.control; | |
387 break; | |
388 } | |
389 } else { | |
390 if (location == 0x1100) { | |
391 if (busack_cycle > context->current_cycle) { | |
392 busack = new_busack; | |
393 busack_cycle = CYCLE_NEVER; | |
394 } | |
395 context->value = (!reset) && busack; | |
396 printf("Byte read of BUSREQ returned %d\n", context->value); | |
397 } else if (location == 0x1200) { | |
398 context->value = !reset; | |
399 } else { | |
400 printf("Byte read of unknown IO location: %X\n", location); | |
401 } | |
402 } | |
403 return context; | |
404 } | |
405 | |
406 m68k_context * io_read_w(uint32_t location, m68k_context * context) | |
407 { | |
408 if (location < 0x100) { | |
409 switch(location/2) | |
410 { | |
411 case 0x0: | |
412 //version bits should be 0 for now since we're not emulating TMSS | |
413 //Not sure about the other bits | |
414 context->value = 0; | |
415 break; | |
416 case 0x1: | |
417 io_data_read(&gamepad_1, context); | |
418 break; | |
419 case 0x2: | |
420 io_data_read(&gamepad_2, context); | |
421 break; | |
422 case 0x3://PORT C Data | |
423 break; | |
424 case 0x4: | |
425 context->value = gamepad_1.control; | |
426 break; | |
427 case 0x5: | |
428 context->value = gamepad_2.control; | |
429 break; | |
430 case 0x6: | |
431 //PORT C Control | |
432 context->value = 0; | |
433 break; | |
434 } | |
435 context->value = context->value | (context->value << 8); | |
436 printf("Word read to %X returned %d\n", location, context->value); | |
437 } else { | |
438 if (location == 0x1100) { | |
439 if (busack_cycle > context->current_cycle) { | |
440 busack = new_busack; | |
441 busack_cycle = CYCLE_NEVER; | |
442 } | |
443 context->value = ((!reset) && busack) << 8; | |
444 printf("Word read of BUSREQ returned %d\n", context->value); | |
445 } else if (location == 0x1200) { | |
446 context->value = (!reset) << 8; | |
447 } else { | |
448 printf("Word read of unknown IO location: %X\n", location); | |
449 } | |
450 } | |
451 return context; | |
452 } | |
453 | |
454 int main(int argc, char ** argv) | |
455 { | |
456 if (argc < 2) { | |
457 fputs("Usage: blastem FILENAME\n", stderr); | |
458 return 1; | |
459 } | |
460 if(!load_rom(argv[1])) { | |
461 fprintf(stderr, "Failed to open %s for reading\n", argv[1]); | |
462 return 1; | |
463 } | |
464 int width = 320; | |
465 int height = 240; | |
466 if (argc > 2) { | |
467 width = atoi(argv[2]); | |
468 if (argc > 3) { | |
469 height = atoi(argv[3]); | |
470 } else { | |
471 height = (width/320) * 240; | |
472 } | |
473 } | |
474 render_init(width, height); | |
475 size_t size = 1024 * 1024; | |
476 uint8_t * transbuf = alloc_code(&size); | |
477 | |
478 x86_68k_options opts; | |
479 m68k_context context; | |
480 vdp_context v_context; | |
481 | |
482 init_x86_68k_opts(&opts); | |
483 init_68k_context(&context, opts.native_code_map, &opts); | |
484 init_vdp_context(&v_context); | |
485 context.next_context = &v_context; | |
486 //cartridge ROM | |
487 context.mem_pointers[0] = cart; | |
488 context.target_cycle = context.sync_cycle = MCLKS_PER_FRAME/MCLKS_PER_68K; | |
489 //work RAM | |
490 context.mem_pointers[1] = ram; | |
491 uint32_t address; | |
492 address = cart[0x68/2] << 16 | cart[0x6A/2]; | |
493 uint8_t * end = transbuf + size; | |
494 transbuf = translate_m68k_stream(transbuf, end, address, &context); | |
495 address = cart[0x70/2] << 16 | cart[0x72/2]; | |
496 transbuf = translate_m68k_stream(transbuf, end, address, &context); | |
497 address = cart[0x78/2] << 16 | cart[0x7A/2]; | |
498 transbuf = translate_m68k_stream(transbuf, end, address, &context); | |
499 address = cart[2] << 16 | cart[3]; | |
500 translate_m68k_stream(transbuf, end, address, &context); | |
501 m68k_reset(&context); | |
502 return 0; | |
503 } |