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}