main
xband.c
1#include <stdlib.h>
2#include <stddef.h>
3#include <string.h>
4#include "romdb.h"
5#include "genesis.h"
6#include "tern.h"
7#include "xband.h"
8#include "util.h"
9
10#define BIT_ROM_HI 4
11
12enum {
13 PATCH0_LOW,
14 PATCH0_MID,
15 PATCH0_HI,
16 PATCH1_LOW=4,
17 PATCH1_MID,
18 PATCH1_HI,
19 PATCH2_LOW=8,
20 PATCH2_MID,
21 PATCH2_HI,
22 PATCH3_LOW=12,
23 PATCH3_MID,
24 PATCH3_HI,
25 PATCH4_LOW=16,
26 PATCH4_MID,
27 PATCH4_HI,
28 PATCH5_LOW=20,
29 PATCH5_MID,
30 PATCH5_HI,
31 PATCH6_LOW=24,
32 PATCH6_MID,
33 PATCH6_HI,
34 PATCH7_LOW=28,
35 PATCH7_MID,
36 PATCH7_HI,
37 PATCH8_LOW=32,
38 PATCH8_MID,
39 PATCH8_HI,
40 PATCH9_LOW=36,
41 PATCH9_MID,
42 PATCH9_HI,
43 PATCH10_LOW=40,
44 PATCH10_MID,
45 PATCH10_HI,
46 RANGE0_START_LOW=44,
47 RANGE0_START_MID,
48 RANGE0_START_HI,
49 RANGE1_START=48,
50 RANGE1_START_MID,
51 RANGE1_START_HI,
52 MAGIC_LOW=56,
53 MAGIC_MID,
54 MAGIC_HI,
55 RANGE0_END_LOW=64,
56 RANGE0_END_MID,
57 RANGE0_END_HI,
58 RANGE1_END_LOW=68,
59 RANGE1_END_MID,
60 RANGE1_END_HI,
61 RANGE0_DEST_LOW=80,
62 RANGE0_DEST_HI,
63 RANGE0_MASK,
64 RANGE1_DEST_LOW=84,
65 RANGE1_DEST_HI,
66 RANGE1_MASK,
67
68 MORE_MYSTERY=219,
69 UNKNOWN_REG=221,
70 UNKNOWN_REG2,
71 UNKNOWN_REG3,
72
73};
74
75//#define DO_DEBUG_PRINT
76#ifdef DO_DEBUG_PRINT
77#define dprintf printf
78#define dputs puts
79#else
80#define dprintf
81#define dputs
82#endif
83
84uint8_t xband_detect(uint8_t *rom, uint32_t rom_size)
85{
86 if (rom_size < 0x200) {
87 return 0;
88 }
89
90 //product ID is all NULL
91 for (int i = GAME_ID_OFF; i <= (GAME_ID_OFF + GAME_ID_LEN); i++)
92 {
93 if (rom[i]) {
94 return 0;
95 }
96 }
97
98 if (!memcmp(rom+8, "DAVE", 4)) {
99 //XBAND test roms
100 return 1;
101 }
102
103 //Internal ROM is 512KB, accept larger ones for overdumps and custom firmware
104 if (rom_size < (512*1024)) {
105 return 0;
106 }
107
108 //ROM has no standard header, but does have a jump at $100
109 if (rom[0x100] != 0x4E || rom[0x101] != 0xF9) {
110 return 0;
111 }
112
113
114 return 1;
115}
116
117static xband *get_xband(genesis_context *gen)
118{
119 if (!gen->extra) {
120 gen->extra = gen->m68k->options->gen.memmap[0].buffer;
121 gen->m68k->mem_pointers[2] = (uint16_t *)gen->save_storage;
122 }
123 return gen->extra;
124}
125
126static void update_control(genesis_context *gen, uint8_t value)
127{
128 xband *x = gen->extra;
129 if ((x->control ^ value) & BIT_ROM_HI) {
130 if (value & BIT_ROM_HI) {
131 gen->m68k->mem_pointers[0] = (uint16_t *)gen->save_storage;
132 gen->m68k->mem_pointers[1] = NULL;
133 gen->m68k->mem_pointers[2] = gen->cart;
134 gen->m68k->mem_pointers[3] = x->cart_space - 0x100000;
135 } else {
136 gen->m68k->mem_pointers[0] = x->cart_space;
137 gen->m68k->mem_pointers[1] = x->cart_space;
138 gen->m68k->mem_pointers[2] = (uint16_t *)gen->save_storage;
139 gen->m68k->mem_pointers[3] = NULL;
140 }
141 m68k_invalidate_code_range(gen->m68k, 0, 0x3BC000);
142 }
143 x->control = value;
144}
145
146static void *xband_write_b(uint32_t address, void *context, uint8_t value)
147{
148 m68k_context *m68k = context;
149 genesis_context *gen = m68k->system;
150 xband *x = get_xband(gen);
151 if (address == 0x181) {
152 x->kill = value;
153 dprintf("Write to \"soft\" kill register %X\n", value);
154 } else if (address == 0x183) {
155 update_control(gen, value);
156 dprintf("Write to \"soft\" control register %X\n", value);
157 } else if ((x->control & BIT_ROM_HI && address < 0x200000) || (address >= 0x200000 && !(x->control & BIT_ROM_HI))) {
158 gen->save_storage[(address & 0xFFFF) ^ 1] = value;
159 m68k_handle_code_write(address, m68k);
160 //TODO: handle code at mirror addresses
161 } else {
162 printf("Unhandled write to cartridge area %X: %X\n", address, value);
163 }
164 return context;
165}
166
167static void *xband_write_hi_b(uint32_t address, void *context, uint8_t value)
168{
169 return xband_write_b(address | 0x200000, context, value);
170}
171
172static void *xband_write_w(uint32_t address, void *context, uint16_t value)
173{
174 m68k_context *m68k = context;
175 genesis_context *gen = m68k->system;
176 xband *x = get_xband(gen);
177 if (address == 0x180 || address == 0x182) {
178 return xband_write_b(address | 1, context, value);
179 } else if ((x->control & BIT_ROM_HI && address < 0x200000) || (address >= 0x200000 && !(x->control & BIT_ROM_HI))) {
180 gen->save_storage[address & 0xFFFE] = value;
181 gen->save_storage[(address & 0xFFFE) | 1] = value >> 8;
182 m68k_handle_code_write(address, m68k);
183 //TODO: handle code at mirror addresses
184 return context;
185 }
186 printf("Unhandled write to %X: %X\n", address, value);
187 return context;
188}
189
190static void *xband_write_hi_w(uint32_t address, void *context, uint16_t value)
191{
192 return xband_write_w(address | 0x200000, context, value);
193}
194
195static uint16_t xband_read_w(uint32_t address, void *context)
196{
197 m68k_context *m68k = context;
198 genesis_context *gen = m68k->system;
199 xband *x = get_xband(gen);
200 //TODO: actually do something intelligent here
201 return x->cart_space[address >> 1];
202}
203
204static uint16_t xband_read_hi_w(uint32_t address, void *context)
205{
206 return xband_read_w(address | 0x200000, context);
207}
208
209static uint8_t xband_read_b(uint32_t address, void *context)
210{
211 uint16_t val = xband_read_w(address, context);
212 return address & 1 ? val : val >> 8;
213}
214
215static uint8_t xband_read_hi_b(uint32_t address, void *context)
216{
217 return xband_read_b(address | 0x200000, context);
218}
219
220static void *xband_reg_write_b(uint32_t address, void *context, uint8_t value)
221{
222 m68k_context *m68k = context;
223 genesis_context *gen = m68k->system;
224 if (!(address & 1)) {
225 printf("Ignoring write to even address %X: %X\n", address, value);
226 return context;
227 }
228 xband *x = get_xband(gen);
229 if (address < 0x3BFE00) {
230 uint32_t offset = (address - 0x3BC001) / 2;
231 if (offset < XBAND_REGS) {
232 switch (offset)
233 {
234 case MORE_MYSTERY:
235 case UNKNOWN_REG:
236 dprintf("Write to mysterious reg: %X: %X\n", address, value);
237 value = value & 0x7F;
238 break;
239 case UNKNOWN_REG3:
240 dprintf("Write to mysterious reg: %X: %X\n", address, value);
241 value = value & 0xFE;
242 break;
243 }
244 x->regs[offset] = value;
245 dprintf("Write to register %X(%d): %X\n", address, offset, value);
246 } else {
247 printf("Unhandled register write %X: %X\n", address, value);
248 }
249 } else {
250 if (address == 0x3BFE01) {
251 x->kill = value;
252 dprintf("Write to kill register %X\n", value);
253 } else if (address == 0x3BFE03) {
254 update_control(gen, value);
255 dprintf("Write to control register %X\n", value);
256 } else {
257 printf("Unhandled register write %X: %X\n", address, value);
258 }
259 }
260 return context;
261}
262
263static void *xband_reg_write_w(uint32_t address, void *context, uint16_t value)
264{
265 return xband_reg_write_b(address | 1, context, value);
266}
267
268static uint8_t xband_reg_read_b(uint32_t address, void *context)
269{
270 m68k_context *m68k = context;
271 genesis_context *gen = m68k->system;
272 if (!(address & 1)) {
273 printf("Read from even address %X\n", address);
274 return gen->header.get_open_bus_value(&gen->header) >> 8;
275 }
276 xband *x = get_xband(gen);
277 if (address < 0x3BFE00) {
278 uint32_t offset = (address - 0x3BC001) / 2;
279 if (offset < XBAND_REGS) {
280 dprintf("Regsister read: %X\n", address);
281 return x->regs[offset];
282 } else {
283 printf("Unhandled register read from address %X\n", address);
284 return 0x5D;
285 }
286 } else {
287 if (address == 0x3BFE01) {
288 return x->kill;
289 } else if (address == 0x3BFE03) {
290 return x->control;
291 } else {
292 printf("Unhandled register read from address %X\n", address);
293 return 0x5D;
294 }
295 }
296}
297
298static uint16_t xband_reg_read_w(uint32_t address, void *context)
299{
300 m68k_context *m68k = context;
301 genesis_context *gen = m68k->system;
302 uint16_t value = xband_reg_read_b(address | 1, context);
303 value |= gen->header.get_open_bus_value(&gen->header) & 0xFF00;
304 return value;
305}
306
307void xband_serialize(genesis_context *gen, serialize_buffer *buf)
308{
309 xband *x = get_xband(gen);
310 save_int8(buf, x->kill);
311 save_int8(buf, x->control);
312 save_buffer8(buf, x->regs, XBAND_REGS);
313}
314
315void xband_deserialize(deserialize_buffer *buf, genesis_context *gen)
316{
317 xband *x = get_xband(gen);
318 x->kill = load_int8(buf);
319 update_control(gen, load_int8(buf));
320 for (int i = 0; i < XBAND_REGS; i++)
321 {
322 xband_write_b(0x3BC001 + i*2, gen->m68k, load_int8(buf));
323 }
324}
325
326rom_info xband_configure_rom(tern_node *rom_db, void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, memmap_chunk const *base_map, uint32_t base_chunks)
327{
328 rom_info info;
329 if (lock_on && lock_on_size) {
330 rom_info lock_on_info = configure_rom(rom_db, lock_on, lock_on_size, NULL, 0, base_map, base_chunks);
331 info.name = alloc_concat("XBAND - ", lock_on_info.name);
332 info.regions = lock_on_info.regions;
333 free_rom_info(&lock_on_info);
334 } else {
335 info.name = strdup("XBAND");
336 info.regions = REGION_J|REGION_U|REGION_E;
337 }
338 info.save_size = 64*1024;
339 info.save_buffer = malloc(info.save_size);
340 info.save_mask = info.save_size-1;
341 info.save_type = RAM_FLAG_BOTH;
342 info.port1_override = info.ext_override = info.mouse_mode = NULL;
343 info.port2_override = strdup("xband keyboard");
344 info.eeprom_map = NULL;
345 info.num_eeprom = 0;
346 info.rom = rom;
347 info.rom_size = rom_size;
348 info.is_save_lock_on = 0;
349 xband *x = calloc(sizeof(xband), 1);
350 rom_size = nearest_pow2(rom_size);
351 for (int i = 0; (i + rom_size) <= sizeof(x->cart_space) / 2; i += rom_size)
352 {
353 memcpy(x->cart_space + i/2, rom, rom_size);
354 }
355 if (lock_on && lock_on_size >= 0x200) {
356 memcpy(x->cart_space + 0x80, ((uint16_t *)lock_on) + 0x80, 0x100);
357 }
358 //observed power on values
359 memset(x->regs, 0, sizeof(x->regs));
360 x->regs[0x7C] = 0;
361 x->regs[0x7D] = 0x80;
362 x->regs[0xB4] = 0x7F;
363 x->regs[UNKNOWN_REG2] = 8;
364
365 byteswap_rom(0x400000, x->cart_space);
366
367 info.mapper_type = MAPPER_XBAND;
368 info.map_chunks = base_chunks + 5;
369 info.map = calloc(sizeof(memmap_chunk), info.map_chunks);
370 info.map[0].mask = 0xFFFFFF;
371 info.map[0].aux_mask = 0xFFFFFF;
372 info.map[0].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_AUX_BUFF;
373 info.map[0].start = 0;
374 info.map[0].end = 0x10000;
375 info.map[0].ptr_index = 0;
376 info.map[0].buffer = x->cart_space;
377 info.map[0].write_16 = xband_write_w;
378 info.map[0].write_8 = xband_write_b;
379 info.map[0].read_16 = xband_read_w;
380 info.map[0].read_8 = xband_read_b;
381 info.map[1].mask = 0xFFFFFF;
382 info.map[1].aux_mask = 0xFFFFFF;
383 info.map[1].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_AUX_BUFF;
384 info.map[1].start = 0x10000;
385 info.map[1].end = 0x200000;
386 info.map[1].ptr_index = 1;
387 info.map[1].buffer = x->cart_space;
388 info.map[1].write_16 = xband_write_w;
389 info.map[1].write_8 = xband_write_b;
390 info.map[1].read_16 = xband_read_w;
391 info.map[1].read_8 = xband_read_b;
392 info.map[2].mask = 0xFFFF;
393 info.map[2].aux_mask = 0xFFFF;
394 info.map[2].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL;
395 info.map[2].start = 0x200000;
396 info.map[2].end = 0x210000;
397 info.map[2].ptr_index = 2;
398 info.map[2].buffer = NULL;
399 info.map[2].write_16 = xband_write_hi_w;
400 info.map[2].write_8 = xband_write_hi_b;
401 info.map[2].read_16 = xband_read_hi_w;
402 info.map[2].read_8 = xband_read_hi_b;
403 info.map[3].mask = 0xFFFFFF;
404 info.map[3].aux_mask = 0xFFFFFF;
405 info.map[3].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL;
406 info.map[3].start = 0x210000;
407 info.map[3].end = 0x3BC000;
408 info.map[3].ptr_index = 3;
409 info.map[3].buffer = NULL;
410 info.map[3].write_16 = xband_write_w;
411 info.map[3].write_8 = xband_write_b;
412 info.map[3].read_16 = xband_read_w;
413 info.map[3].read_8 = xband_read_b;
414 info.map[4].mask = 0xFFFFFF;
415 info.map[4].flags = MMAP_READ|MMAP_CODE|MMAP_PTR_IDX|MMAP_FUNC_NULL;
416 info.map[4].start = 0x3BC000;
417 info.map[4].end = 0x3C0000;
418 info.map[4].ptr_index = 4;
419 info.map[4].write_16 = xband_reg_write_w;
420 info.map[4].write_8 = xband_reg_write_b;
421 info.map[4].read_16 = xband_reg_read_w;
422 info.map[4].read_8 = xband_reg_read_b;
423 memcpy(info.map + 5, base_map, base_chunks * sizeof(memmap_chunk));
424
425 return info;
426}