view jaguar.c @ 1483:001120e91fed nuklear_ui

Skip loading menu ROM if Nuklear UI is enabled. Allow disabling Nuklear UI in favor of old menu ROM both at compile time and in config. Fall back to ROM UI if GL is unavailable
author Michael Pavone <pavone@retrodev.com>
date Sat, 25 Nov 2017 20:43:20 -0800
parents b45d1f64060e
children
line wrap: on
line source

#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include "m68k_core.h"
#include "68kinst.h"
#include "jaguar.h"
#include "util.h"
#include "debug.h"
#include "config.h"
#include "render.h"

//BIOS Area Memory map
// 10 00 00 - 10 04 00 : Video mode/ Memory control registers
// 10 04 00 - 10 08 00 : CLUT
// 10 08 00 - 10 10 00 : Line buffer A
// 10 10 00 - 10 18 00 : Line buffer B
// 10 18 00 - 10 20 00 : Write Line buffer
// 10 21 00 - 10 30 00 : GPU/blitter registers
// 10 30 00 - 10 40 00 : GPU Local RAM (mirrored every 1K?)
// 11 00 00 - 11 00 40 : Timer/Clock registers
// 11 40 00 - 11 40 04 : Joystick Interface
// 11 A1 00 - 11 A1 52 : DSP/DAC/I2S Registers
// 11 B0 00 - 11 D0 00 : DSP Local RAM (8KB)
// 11 D0 00 - 11 E0 00 : Wave table ROM

int headless = 1;
tern_node * config;

void handle_keydown(int keycode, uint8_t scancode)
{
}

void handle_keyup(int keycode, uint8_t scancode)
{
}

void handle_joydown(int joystick, int button)
{
}

void handle_joyup(int joystick, int button)
{
}

void handle_joy_dpad(int joystick, int dpadnum, uint8_t value)
{
}

void handle_mousedown(int mouse, int button)
{
}

void handle_mouseup(int mouse, int button)
{
}

void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay)
{
}

void jag_update_m68k_int(jaguar_context *system)
{
	m68k_context *m68k = system->m68k;
	if (m68k->sync_cycle - m68k->current_cycle > system->max_cycles) {
		m68k->sync_cycle = m68k->current_cycle + system->max_cycles;
	}
	//TODO: Support other interrupt sources
	if (!system->cpu_int_control || (m68k->status & 0x7)) {
		m68k->int_cycle = CYCLE_NEVER;
	} else if(system->cpu_int_control & system->video->cpu_int_pending) {
		m68k->int_cycle = m68k->current_cycle;
		//supposedly all interrupts on the jaguar are "level 0" autovector interrupts
		//which I assume means they're abusing the "spurious interrupt" vector
		m68k->int_num = VECTOR_USER0 - VECTOR_SPURIOUS_INTERRUPT;
	} else {
		m68k->int_cycle = jag_next_vid_interrupt(system->video);
		m68k->int_num = VECTOR_USER0 - VECTOR_SPURIOUS_INTERRUPT;
	}
	
	if (m68k->int_cycle > m68k->current_cycle && m68k->int_pending == INT_PENDING_SR_CHANGE) {
		m68k->int_pending = INT_PENDING_NONE;
	}
	
	m68k->target_cycle = m68k->int_cycle < m68k->sync_cycle ? m68k->int_cycle : m68k->sync_cycle;
	if (m68k->should_return) {
		m68k->target_cycle = m68k->current_cycle;
	} else if (m68k->target_cycle < m68k->current_cycle) {
		//Changes to SR can result in an interrupt cycle that's in the past
		//This can cause issues with the implementation of STOP though
		m68k->target_cycle = m68k->current_cycle;
	}
}

void rom0_write_16(uint32_t address, jaguar_context *system, uint16_t value)
{
	//TODO: Use write_latch and write_pending to turn two 16-bit writes into a 32-bit one
	//Documentation heavily suggests that writes to most registers should be 32-bits wide
	if (address < 0x100000 || address >= 0x120000) {
		//Boot ROM
		fprintf(stderr, "Invalid write to Boot ROM - %X:%X\n", address, value);
		return;
	}
	if (address < 0x103000) {
		if (address < 0x101000) {
			if (address < 0x100400) {
				//Video mode / Memory control registers
				switch(address & 0x3FE)
				{
				case 0:
					if (((value ^ system->memcon1) & 1) || !system->memcon_written) {
						uint16_t **mem_pointers = system->m68k->mem_pointers;
						int rom = value & 1 ? 4 : 1;
						int ram0 = value & 1 ? 0 : 6;
						int ram1 = value & 1 ? 2 : 4;
						mem_pointers[ram0] = mem_pointers[ram0 + 1] = system->dram;
						//these are probably open bus, but mirror DRAM for now
						mem_pointers[ram1] = mem_pointers[ram1 + 1] = system->dram;
						
						mem_pointers[rom] = system->cart;
						mem_pointers[rom + 1] = system->cart + ((0x200000 & (system->cart_size-1)) >> 1);
						mem_pointers[rom + 2] = system->cart + ((0x400000 & (system->cart_size-1)) >> 1);
						system->memcon_written = 1;
						printf("MEMCON1 write - ROMHI: %d\n", value & 1);
						switch (system->memcon1 >> 3 & 3)
						{
						case 0:
							system->rom_cycles = 10;
							break;
						case 1:
							system->rom_cycles = 8;
							break;
						case 2:
							system->rom_cycles = 6;
							break;
						case 3:
							system->rom_cycles = 5;
							break;
						}
						//TODO: invalidate code cache
					}
					system->memcon1 = value;
					break;
				case 2:
					system->memcon2 = value;
					break;
				case 0xE0:
					system->cpu_int_control = value & 0x1F;
					system->video->cpu_int_pending &= ~(value >> 8);
					printf("INT1 write: %X @ %d - int_pending: %X, int_control: %X\n", value, system->m68k->current_cycle, system->video->cpu_int_pending, system->cpu_int_control);
					jag_update_m68k_int(system);
					//TODO: apply mask to int pending fields on other components once they are implemented
					break;
				case 0xE2:
					//no real handling of bus conflicts presently, so this doesn't really need to do anything yet
					printf("INT2 write: %X\n", value);
					break;
				default:
					jag_video_reg_write(system->video, address, value);
					jag_update_m68k_int(system);
					break;
				}
			} else if (address < 0x100800) {
				//CLUT
				address = address >> 1 & 255;
				system->video->clut[address] = value;
			} else {
				//Line buffer A
				address = address >> 1 & 0x3FF;
				if (address < LINEBUFFER_WORDS) {
					system->video->line_buffer_a[address] = value;
				}
			}
		} else if (address < 0x101800) {
			//Line buffer B
			address = address >> 1 & 0x3FF;
			if (address < LINEBUFFER_WORDS) {
				system->video->line_buffer_b[address] = value;
			}
		} else if (address < 0x102100) {
			//Write Line Buffer
			address = address >> 1 & 0x3FF;
			if (address < LINEBUFFER_WORDS) {
				system->video->write_line_buffer[address] = value;
			}
		} else {
			//GPU/Blitter registers
			if (address < 0x102200) {
				fprintf(stderr, "Unhandled write to GPU registers %X: %X\n", address, value);
				if (address == 0x102116 && (value & 1)) {
					FILE *f = fopen("gpu.bin", "wb");
					uint8_t buf[4];
					for (int i = 0; i < GPU_RAM_BYTES/sizeof(uint32_t); i++)
					{
						buf[0] = system->gpu_local[i] >> 24;
						buf[1] = system->gpu_local[i] >> 16;
						buf[2] = system->gpu_local[i] >> 8;
						buf[3] = system->gpu_local[i];
						fwrite(buf, 1, sizeof(buf), f);
					}
					fclose(f);
				}
			} else {
				fprintf(stderr, "Unhandled write to Blitter registers %X: %X\n", address, value);
			}
		}
	} else if (address < 0x11A100) {
		if (address < 0x110000) {
			//GPU Local RAM
				uint32_t offset = address >> 2 & (GPU_RAM_BYTES / sizeof(uint32_t) - 1);
			uint32_t value32 = value;
			if (address & 2) {
				system->gpu_local[offset] &= 0xFFFF0000;
			} else {
				system->gpu_local[offset] &= 0x0000FFFF;
				value32 = value32 << 16;
			}
			system->gpu_local[offset] |= value32;
		} else if (address < 0x114000) {
			//timer clock registers
			fprintf(stderr, "Unhandled write to timer/clock registers - %X:%X\n", address, value);
		} else {
			//joystick interface
			fprintf(stderr, "Unhandled write to joystick interface - %X:%X\n", address, value);
		}
	} else if (address < 0x11B000) {
		//DSP/DAC/I2S Registers
		fprintf(stderr, "Unhandled write to DSP/DAC/I2S registers - %X:%X\n", address, value);
	} else if (address < 0x11D000) {
		//DSP local RAM
		uint32_t offset = address >> 2 & (DSP_RAM_BYTES / sizeof(uint32_t) - 1);
		uint32_t value32 = value;
		if (address & 2) {
			system->dsp_local[offset] &= 0xFFFF0000;
		} else {
			system->dsp_local[offset] &= 0x0000FFFF;
			value32 = value32 << 16;
		}
		system->gpu_local[offset] |= value32;
	} else {
		//Wave table ROM
		fprintf(stderr, "Invalid write to wave table ROM - %X:%X\n", address, value);
	}
}

uint16_t rom0_read_16(uint32_t address, jaguar_context *system)
{
	if (address < 0x100000 || address >= 0x120000) {
		//Boot ROM
		address = address >> 1 & ((system->bios_size >> 1) - 1);
		return system->bios[address];
	}
	if (address < 0x103000) {
		if (address < 0x101000) {
			if (address < 0x100400) {
				//Video mode / Memory control registers
				switch (address & 0x3FE)
				{
				case 0xE0:
					puts("INT1 read");
					//TODO: Bitwise or with cpu_int_pending fields from other components once implemented
					return system->video->cpu_int_pending;
					break;
				default:
					fprintf(stderr, "Unhandled read from video mode/memory control registers - %X\n", address);
				}
			} else if (address < 0x100800) {
				//CLUT
				address = address >> 1 & 255;
				return system->video->clut[address];
			} else {
				//Line buffer A
				address = address >> 1 & 0x3FF;
				if (address < LINEBUFFER_WORDS) {
					return system->video->line_buffer_a[address];
				}
			}
		} else if (address < 0x101800) {
			//Line buffer B
			address = address >> 1 & 0x3FF;
			if (address < LINEBUFFER_WORDS) {
				return system->video->line_buffer_b[address];
			}
		} else if (address < 0x102100) {
			//Write Line Buffer
			address = address >> 1 & 0x3FF;
			if (address < LINEBUFFER_WORDS) {
				return system->video->write_line_buffer[address];
			}
		} else {
			//GPU/Blitter registers
			if (address < 0x102200) {
				fprintf(stderr, "Unhandled read from GPU registers %X\n", address);
			} else {
				fprintf(stderr, "Unhandled read from Blitter registers %X\n", address);
			}
		}
	} else if (address < 0x11A100) {
		if (address < 0x110000) {
			//GPU Local RAM
			uint32_t offset = address >> 2 & (GPU_RAM_BYTES / sizeof(uint32_t) - 1);
			if (address & 2) {
				return system->gpu_local[offset];
			} else {
				return system->gpu_local[offset] >> 16;
			}
		} else if (address < 0x114000) {
			//timer clock registers
			fprintf(stderr, "Unhandled read from timer/clock registers - %X\n", address);
		} else {
			//joystick interface
			fprintf(stderr, "Unhandled read from joystick interface - %X\n", address);
		}
	} else if (address < 0x11B000) {
		//DSP/DAC/I2S Registers
		fprintf(stderr, "Unhandled read from DSP/DAC/I2S registers - %X\n", address);
	} else if (address < 0x11D000) {
		//DSP local RAM
		uint32_t offset = address >> 2 & (DSP_RAM_BYTES / sizeof(uint32_t) - 1);
		if (address & 2) {
				return system->dsp_local[offset];
			} else {
				return system->dsp_local[offset] >> 16;
			}
	} else {
		//Wave table ROM
		fprintf(stderr, "Unhandled read from wave table ROM - %X\n", address);
	}
	return 0xFFFF;
}

uint64_t rom0_read_64(uint32_t address, jaguar_context *system)
{
	address &= 0x1FFFFF;
	uint64_t high = rom0_read_16(address, system);
	uint64_t highmid = rom0_read_16(address+2, system);
	uint64_t lowmid = rom0_read_16(address+4, system);
	uint64_t low = rom0_read_16(address+6, system);
	return high << 48 | highmid << 32 | lowmid << 16 | low;
}

void rom0_write_64(uint32_t address, jaguar_context *system, uint64_t val)
{
	address &= 0x1FFFFF;
	rom0_write_16(address, system, val >> 48);
	rom0_write_16(address+2, system, val >> 32);
	rom0_write_16(address+4, system, val >> 16);
	rom0_write_16(address+6, system, val);
}

uint64_t jag_read_phrase(jaguar_context *system, uint32_t address, uint32_t *cycles)
{
	if (!system->memcon_written) {
		//unsure of timing, but presumably at least 2 32-bit reads 
		//which presumably take a minimum of 1 cycle
		//reality probably depends on the exact area read
		//docs seem to imply some areas only 16-bits wide whereas others are 32-bit
		*cycles += 2;
		return rom0_read_64(address, system);
	}
	uint16_t *src;
	if (system->memcon1 & 1) {
		if (address < 0x800000) {
			src = system->dram + (address >> 1 & (DRAM_WORDS - 1));
			//DRAM is 64-bits wide, but sounds like an access is still at least two cycles
			*cycles += 2;
		} else if (address < 0xE00000) {
			//cart is slow and only 32-bits wide
			*cycles += 2 * (system->rom_cycles);
			src = system->cart + (address >> 1 & (system->cart_size - 1));
		} else {
			*cycles += 2;
			return rom0_read_64(address, system);
		}
	} else if (address > 0x800000) {
		src = system->dram + (address >> 1 & (DRAM_WORDS - 1));
		//DRAM is 64-bits wide, but sounds like an access is still at least two cycles
		*cycles += 2;
	} else if (address > 0x200000) {
		//cart is slow and only 32-bits wide
		*cycles += 2 * (system->rom_cycles);
		src = system->cart + (address >> 1 & (system->cart_size - 1));
	} else {
		*cycles += 2;
		return rom0_read_64(address, system);
	}
	uint64_t high = src[0];
	uint64_t highmid = src[1];
	uint64_t lowmid = src[2];
	uint64_t low = src[3];
	return high << 48 | highmid << 32 | lowmid << 16 | low;
}

uint32_t jag_write_phrase(jaguar_context *system, uint32_t address, uint64_t val)
{
	if (!system->memcon_written) {
		//unsure of timing, but presumably at least 2 32-bit reads 
		//which presumably take a minimum of 1 cycle
		//reality probably depends on the exact area read
		//docs seem to imply some areas only 16-bits wide whereas others are 32-bit
		rom0_write_64(address, system, val);
		return 2;
	}
	uint16_t *dst;
	uint32_t cycles;
	if (system->memcon1 & 1) {
		if (address < 0x800000) {
			dst = system->dram + (address >> 1 & (DRAM_WORDS - 1));
			//DRAM is 64-bits wide, but sounds like an access is still at least two cycles
			cycles = 2;
		} else if (address < 0xE00000) {
			dst = system->cart + (address >> 1 & (system->cart_size - 1));
			//cart is slow and only 32-bits wide
			cycles = 2 * (system->rom_cycles);
		} else {
			rom0_write_64(address, system, val);
			return 2;
		}
	} else if (address > 0x800000) {
		dst = system->dram + (address >> 1 & (DRAM_WORDS - 1));
		//DRAM is 64-bits wide, but sounds like an access is still at least two cycles
		cycles = 2;
	} else if (address > 0x200000) {
		dst = system->cart + (address >> 1 & (system->cart_size - 1));
		//cart is slow and only 32-bits wide
		cycles = 2 * (system->rom_cycles);
	} else {
		rom0_write_64(address, system, val);
		return 2;
	}
	dst[0] = val >> 48;
	dst[1] = val >> 32;
	dst[2] = val >> 16;
	dst[3] = val;
	return cycles;
}

m68k_context * sync_components(m68k_context * context, uint32_t address)
{
	jaguar_context *system = context->system;
	jag_video_run(system->video, context->current_cycle);
	jag_update_m68k_int(system);
	if (context->current_cycle > 0x10000000) {
		context->current_cycle -= 0x10000000;
		system->video->cycles -= 0x10000000;
	}
	if (context->int_ack) {
		context->int_ack = 0;
		//hack until 68K core more properly supports non-autovector interrupts
		context->status |= 1;
	}
	jag_update_m68k_int(system);
	return context;
}


void *rom0_write_m68k(uint32_t address, void *context, uint16_t value)
{
	sync_components(context, 0);
	rom0_write_16(address, ((m68k_context *)context)->system, value);
	return context;
}

uint16_t rom0_read_m68k(uint32_t address, void *context)
{
	sync_components(context, 0);
	return rom0_read_16(address, ((m68k_context *)context)->system);
}

void *rom0_write_m68k_b(uint32_t address, void *context, uint8_t value)
{
	sync_components(context, 0);
	//seems unlikely these areas support byte access
	uint16_t value16 = value;
	value16 |= value16 << 8;
	rom0_write_16(address, ((m68k_context *)context)->system, value16);
	return context;
}

uint8_t rom0_read_m68k_b(uint32_t address, void *context)
{
	sync_components(context, 0);
	uint16_t value = rom0_read_16(address, ((m68k_context *)context)->system);
	if (address & 1) {
		return value;
	}
	return value >> 8;
}

m68k_context *handle_m68k_reset(m68k_context *context)
{
	puts("M68K executed RESET");
	return context;
}

jaguar_context *init_jaguar(uint16_t *bios, uint32_t bios_size, uint16_t *cart, uint32_t cart_size)
{
	jaguar_context *system = calloc(1, sizeof(jaguar_context));
	system->bios = bios;
	system->bios_size = bios_size;
	system->cart = cart;
	system->cart_size = cart_size;
	//TODO: Figure out a better default for this and make it configurable
	system->max_cycles = 3000;

	memmap_chunk *jag_m68k_map = calloc(8, sizeof(memmap_chunk));
	for (uint32_t start = 0, index=0; index < 8; index++, start += 0x200000)
	{
		jag_m68k_map[index].start = start;
		jag_m68k_map[index].end = start + 0x200000;
		jag_m68k_map[index].mask = index ? 0x1FFFFF : 0xFFFFFF;
		jag_m68k_map[index].aux_mask = bios_size - 1;
		jag_m68k_map[index].ptr_index = index;
		jag_m68k_map[index].flags = MMAP_READ | MMAP_WRITE | MMAP_PTR_IDX | MMAP_FUNC_NULL | MMAP_AUX_BUFF | MMAP_CODE;
		jag_m68k_map[index].buffer = bios;
		jag_m68k_map[index].read_16 = rom0_read_m68k;
		jag_m68k_map[index].read_8 = rom0_read_m68k_b;
		jag_m68k_map[index].write_16 = rom0_write_m68k;
		jag_m68k_map[index].write_8 = rom0_write_m68k_b;
	}
	m68k_options *opts = malloc(sizeof(m68k_options));
	init_m68k_opts(opts, jag_m68k_map, 8, 2);
	system->m68k = init_68k_context(opts, handle_m68k_reset);
	system->m68k->sync_cycle = system->max_cycles;
	system->m68k->system = system;
	system->video = jag_video_init();
	system->video->system = system;
	return system;
}

//modified copy of the version in blastem.c
uint16_t *load_rom(char * filename, uint32_t *size)
{
	FILE * f = fopen(filename, "rb");
	if (!f) {
		return 0;
	}
	fseek(f, 0, SEEK_END);
	long filesize = ftell(f);
	fseek(f, 0, SEEK_SET);
	
	*size = nearest_pow2(filesize);
	uint16_t *cart = malloc(*size);
	if (filesize != fread(cart, 1, filesize, f)) {
		fatal_error("Error reading from %s\n", filename);
	}
	filesize = (filesize + 1) & ~1L;
	for (long i = 0; i < filesize; i+=2)
	{
		long index = i >> 1;
		cart[index] = cart[index] >> 8 | cart[index] << 8;
	}
	while (filesize < *size)
	{
		cart[filesize / 2] = 0xFFFF;
		filesize += 2;
	}
	fclose(f);
	return cart;
}

//temporary main function until I clean up blastem.c
int main(int argc, char **argv)
{
	if (argc < 3) {
		fputs("Usage: blastjag BIOS ROM\n", stderr);
		return 1;
	}
	set_exe_str(argv[0]);
	config = load_config(argv[0]);
	uint32_t bios_size;
	uint16_t *bios = load_rom(argv[1], &bios_size);
	if (!bios_size) {
		fatal_error("Failed to read BIOS from %s\n", argv[1]);
	}
	uint32_t cart_size;
	uint16_t *cart = load_rom(argv[2], &cart_size);
	if (!bios_size) {
		fatal_error("Failed to read cart from %s\n", argv[2]);
	}
	jaguar_context *system = init_jaguar(bios, bios_size, cart, cart_size);
	render_init(640, 480, "BlastJag", 0);
	m68k_reset(system->m68k);
	return 0;
}