Mercurial > repos > blastem
changeset 2718:8ce5d1a7ef54
Initial work to integrate PAC-less LaserActive emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 16 Jul 2025 12:32:28 -0700 |
parents | 04007ac9ee3b |
children | f817aedf5e53 |
files | Makefile blastem.c genesis.c laseractive.c laseractive.h system.c system.h upd78k2.cpu upd78k2_util.c |
diffstat | 9 files changed, 298 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Wed Jul 16 07:36:01 2025 -0700 +++ b/Makefile Wed Jul 16 12:32:28 2025 -0700 @@ -283,7 +283,8 @@ COREOBJS:=system.o genesis.o vdp.o io.o romdb.o hash.o xband.o realtec.o i2c.o nor.o $(M68KOBJS) \ sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o gen_player.o coleco.o pico_pcm.o ymz263b.o \ - segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o + segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o \ + laseractive.o upd78k2_dis.o upd78k2.o ifdef NOZ80 CFLAGS+=-DNO_Z80 @@ -337,10 +338,10 @@ LIBORDERONLY:=$(LIBOBJDIR) ifdef NEW_CORE ifeq ($(wildcard $(OBJDIR)/*.d),) -ORDERONLY+= m68k.c z80.c +ORDERONLY+= m68k.c z80.c upd78k2.c endif ifeq ($(wildcard $(LIBOBJDIR)/*.d),) -LIBORDERONLY+= m68k.c z80.c +LIBORDERONLY+= m68k.c z80.c upd78k2.c endif endif
--- a/blastem.c Wed Jul 16 07:36:01 2025 -0700 +++ b/blastem.c Wed Jul 16 12:32:28 2025 -0700 @@ -477,6 +477,8 @@ stype = force_stype = SYSTEM_JAGUAR; } else if (!strcmp("media", argv[i])) { stype = force_stype = SYSTEM_MEDIA_PLAYER; + } else if (!strcmp("laser", argv[i])) { + stype = force_stype = SYSTEM_LASERACTIVE; } else { fatal_error("Unrecognized machine type %s\n", argv[i]); } @@ -520,6 +522,7 @@ " pico - Sega Pico\n" " copera - Yamaha Copera\n" " media - Media Player\n" + " laser - Pioneer LaserActive (no PAC)\n" " -f Toggles fullscreen mode\n" " -g Disable OpenGL rendering\n" " -s FILE Load a GST format savestate from FILE\n"
--- a/genesis.c Wed Jul 16 07:36:01 2025 -0700 +++ b/genesis.c Wed Jul 16 12:32:28 2025 -0700 @@ -555,9 +555,8 @@ gen->refresh_counter = gen->refresh_counter % interval; } -#include <limits.h> #define ADJUST_BUFFER (8*MCLKS_LINE*313) -#define MAX_NO_ADJUST (UINT_MAX-ADJUST_BUFFER) +#define MAX_NO_ADJUST (UINT32_MAX-ADJUST_BUFFER) static m68k_context *sync_components(m68k_context * context, uint32_t address) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/laseractive.c Wed Jul 16 12:32:28 2025 -0700 @@ -0,0 +1,240 @@ +#include <limits.h> +#include "laseractive.h" +#include "io.h" +#include "render.h" +#include "render_audio.h" +#include "blastem.h" +#include "util.h" + +static void gamepad_down(system_header *system, uint8_t pad, uint8_t button) +{ + if (pad != 1) { + return; + } + laseractive *la = (laseractive *)system; + switch (button) + { + case BUTTON_A: + la->upd->port_input[7] &= ~0x40; //LD open + break; + case BUTTON_B: + la->upd->port_input[7] &= ~0x80; //CD open + break; + case BUTTON_C: + la->upd->port_input[7] &= ~0x10; //Digital memory + break; + case BUTTON_START: + la->upd->port_input[7] &= ~0x20; //play + break; + } + +} + +static void gamepad_up(system_header *system, uint8_t pad, uint8_t button) +{ + if (pad != 1) { + return; + } + laseractive *la = (laseractive *)system; + switch (button) + { + case BUTTON_A: + la->upd->port_input[7] |= 0x40; //LD open + break; + case BUTTON_B: + la->upd->port_input[7] |= 0x80; //CD open + break; + case BUTTON_C: + la->upd->port_input[7] |= 0x10; //Digital memory + break; + case BUTTON_START: + la->upd->port_input[7] |= 0x20; //play + break; + } +} + +#define ADJUST_BUFFER 12000000 // one second of cycles +#define MAX_NO_ADJUST (UINT32_MAX-ADJUST_BUFFER) + +static void resume_laseractive(system_header *system) +{ + + pixel_t bg_color = render_map_color(0, 0, 255); + pixel_t led_on = render_map_color(0, 255, 0); + pixel_t led_standby = render_map_color(255, 0, 0); + pixel_t led_off = render_map_color(0, 0, 0); + laseractive *la = (laseractive *)system; + audio_source *audio = render_audio_source("laseractive_tmp", 12000000, 250, 2); + static const uint32_t cycles_per_frame = 12000000 / 60; + static const uint32_t cycles_per_audio_chunk = 16 * 250; + uint32_t next_video = la->upd->cycles + cycles_per_frame; + uint32_t next_audio = la->upd->cycles + cycles_per_audio_chunk; + uint32_t next = next_audio < next_video ? next_audio : next_video; + while (!la->header.should_exit) + { + upd78k2_execute(la->upd, next); + while (la->upd->cycles >= next_audio) { + for (int i = 0; i < 16; i++) + { + render_put_stereo_sample(audio, 0, 0); + } + next_audio += cycles_per_audio_chunk; + } + while (la->upd->cycles >= next_video) { + int pitch; + pixel_t *fb = render_get_framebuffer(FRAMEBUFFER_ODD, &pitch); + pixel_t led_state[5] = { + /*power*/la->upd->port_data[0] & 0x08 ? led_on : la->upd->port_data[0] & 0x40 ? led_standby : led_off, + /*play*/la->upd->port_data[0] & 0x10 ? led_on : led_off, + /*memory*/la->upd->port_data[0] & 0x20 ? led_standby : led_off, + /*cd*/la->upd->port_data[6] & 0x02 ? led_on : led_off, + /*ld*/la->upd->port_data[0] & 0x80 ? led_on : led_off + }; + pixel_t *line = fb; + for (int y = 0; y < 224 + 11 - 8; y++) + { + pixel_t *cur = line; + for (int x = 0; x < 320 + 13 + 14; x++) + { + *(cur++) = bg_color; + } + + line += pitch / sizeof(pixel_t); + } + for (int y = 224 + 11 - 8; y < 224 + 11 + 8; y++) + { + pixel_t *cur = line; + for (int x = 0; x < 13; x++) + { + *(cur++) = bg_color; + } + for (int x = 13; x < 13 + 8 * 5; x++) + { + *(cur++) = led_state[(x-13) >> 3]; + } + for (int x = 13 + 8 * 5; x < 320 + 13 + 14; x++) + { + *(cur++) = bg_color; + } + + line += pitch / sizeof(pixel_t); + } + render_framebuffer_updated(FRAMEBUFFER_ODD, 320); + next_video += cycles_per_frame; + } + if (la->upd->cycles > MAX_NO_ADJUST) { + uint32_t deduction = la->upd->cycles - ADJUST_BUFFER; + upd78k2_adjust_cycles(la->upd, deduction); + next_audio -= deduction; + next_video -= deduction; + } + next = next_audio < next_video ? next_audio : next_video; + } + render_free_source(audio); +} + +static void start_laseractive(system_header *system, char *statefile) +{ + laseractive *la = (laseractive *)system; + la->upd->port_input[2] = 0xFF; + la->upd->port_input[3] = 0x20; + la->upd->port_input[7] = 0xF7; + la->upd->pc = la->upd_rom[0] | la->upd_rom[1] << 8; + resume_laseractive(system); +} + +static void free_laseractive(system_header *system) +{ + laseractive *la = (laseractive *)system; + free(la->upd->opts->gen.memmap); + free(la->upd->opts); + free(la->upd); + free(la); +} + +static void request_exit(system_header *system) +{ + //TODO: implement me +} + +static void toggle_debug_view(system_header *system, uint8_t debug_view) +{ +#ifndef IS_LIB +#endif +} + +static void inc_debug_mode(system_header * system) +{ +} + +static void soft_reset(system_header * system) +{ + //TODO: implement me +} + +static void set_speed_percent(system_header * system, uint32_t percent) +{ + //TODO: implement me +} + +laseractive *alloc_laseractive(system_media *media, uint32_t opts) +{ + laseractive *la = calloc(1, sizeof(laseractive)); + la->header.start_context = start_laseractive; + la->header.resume_context = resume_laseractive; + la->header.request_exit = request_exit; + la->header.soft_reset = soft_reset; + la->header.free_context = free_laseractive; + la->header.set_speed_percent = set_speed_percent; + la->header.gamepad_down = gamepad_down; + la->header.gamepad_up = gamepad_up; + la->header.toggle_debug_view = toggle_debug_view; + la->header.inc_debug_mode = inc_debug_mode; + la->header.type = SYSTEM_LASERACTIVE; + la->header.info.name = strdup(media->name); + char *upd_rom_path = tern_find_path_default(config, "system\0laseractive_upd_rom\0", (tern_val){.ptrval = "laseractive_dyw_1322a.bin"}, TVAL_PTR).ptrval; + uint32_t firmware_size; + if (is_absolute_path(upd_rom_path)) { + FILE *f = fopen(upd_rom_path, "rb"); + if (f) { + long to_read = file_size(f); + if (to_read > sizeof(la->upd_rom)) { + to_read = sizeof(la->upd_rom); + } + firmware_size = fread(la->upd_rom, 1, to_read, f); + if (!firmware_size) { + warning("Failed to read from %s\n", upd_rom_path); + } + fclose(f); + } else { + warning("Failed to open %s\n", upd_rom_path); + } + } else { + uint8_t *tmp = read_bundled_file(upd_rom_path, &firmware_size); + if (tmp) { + if (firmware_size > sizeof(la->upd_rom)) { + firmware_size = sizeof(la->upd_rom); + } + memcpy(la->upd_rom, tmp, firmware_size); + free(tmp); + } else { + warning("Failed to open %s\n", upd_rom_path); + } + } + static const memmap_chunk base_upd_map[] = { + { 0x0000, 0xE000, 0xFFFF, .flags = MMAP_READ,}, + { 0xFB00, 0xFD00, 0x1FF, .flags = MMAP_READ | MMAP_WRITE | MMAP_CODE}, + { 0xFD00, 0xFE00, 0xFF, .flags = MMAP_READ | MMAP_WRITE | MMAP_CODE}, + { 0xFF00, 0xFFFF, 0xFF, .read_8 = upd78237_sfr_read, .write_8 = upd78237_sfr_write} + }; + memmap_chunk *upd_map = calloc(4, sizeof(memmap_chunk)); + memcpy(upd_map, base_upd_map, sizeof(base_upd_map)); + upd_map[0].buffer = la->upd_rom; + upd_map[1].buffer = la->upd_pram; + upd_map[2].buffer = la->upd_pram + 512; + upd78k2_options *options = calloc(1, sizeof(upd78k2_options)); + init_upd78k2_opts(options, upd_map, 4); + options->gen.address_mask = options->gen.max_address = 0xFFFF; //expanded memory space is not used + la->upd = init_upd78k2_context(options); + return la; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/laseractive.h Wed Jul 16 12:32:28 2025 -0700 @@ -0,0 +1,16 @@ +#ifndef LASERACTIVE_H_ +#define LASERACTIVE_H_ + +#include "system.h" +#include "upd78k2.h" + +typedef struct { + system_header header; + upd78k2_context *upd; + uint8_t upd_pram[768]; + uint8_t upd_rom[0xE000]; //ROM is actually 64K, but only first 56K are mapped in +} laseractive; + +laseractive *alloc_laseractive(system_media *media, uint32_t opts); + +#endif //LASERACTIVE_H_
--- a/system.c Wed Jul 16 07:36:01 2025 -0700 +++ b/system.c Wed Jul 16 12:32:28 2025 -0700 @@ -9,6 +9,7 @@ #include "paths.h" #include "util.h" #include "cdimage.h" +#include "laseractive.h" #define SMD_HEADER_SIZE 512 #define SMD_MAGIC1 0x03 @@ -405,6 +406,8 @@ case SYSTEM_PICO: case SYSTEM_COPERA: return &(alloc_config_pico(media->buffer, media->size, lock_on, lock_on_size, opts, force_region, stype))->header; + case SYSTEM_LASERACTIVE: + return &(alloc_laseractive(media, opts))->header; default: return NULL; }
--- a/system.h Wed Jul 16 07:36:01 2025 -0700 +++ b/system.h Wed Jul 16 12:32:28 2025 -0700 @@ -23,7 +23,8 @@ SYSTEM_MEDIA_PLAYER, SYSTEM_COLECOVISION, SYSTEM_PICO, - SYSTEM_COPERA + SYSTEM_COPERA, + SYSTEM_LASERACTIVE } system_type; typedef enum {
--- a/upd78k2.cpu Wed Jul 16 07:36:01 2025 -0700 +++ b/upd78k2.cpu Wed Jul 16 12:32:28 2025 -0700 @@ -71,6 +71,7 @@ upd78k2_context *init_upd78k2_context(upd78k2_options *opts); void upd78k2_sync_cycle(upd78k2_context *upd, uint32_t target_cycle); typedef void (upd_io_fun)(upd78k2_context *upd, uint8_t offset); + void upd78k2_adjust_cycles(upd78k2_context *upd, uint32_t deduction); #Prefix bytes # 0000 0001 -> saddr becomes sfr, mem becomes &mem
--- a/upd78k2_util.c Wed Jul 16 07:36:01 2025 -0700 +++ b/upd78k2_util.c Wed Jul 16 12:32:28 2025 -0700 @@ -1,13 +1,18 @@ #include <string.h> +//#define FETCH_DEBUG void upd78k2_read_8(upd78k2_context *upd) { +#ifdef FETCH_DEBUG uint32_t tmp = upd->scratch1; +#endif upd->scratch1 = read_byte(upd->scratch1, (void **)upd->mem_pointers, &upd->opts->gen, upd); +#ifdef FETCH_DEBUG if (tmp == upd->pc) { printf("uPD78K/II fetch %04X: %02X, AX=%02X%02X BC=%02X%02X DE=%02X%02X HL=%02X%02X SP=%04X\n", tmp, upd->scratch1, upd->main[1], upd->main[0], upd->main[3], upd->main[2], upd->main[5], upd->main[4], upd->main[7], upd->main[6], upd->sp); } +#endif //FIXME: cycle count upd->cycles += 2 * upd->opts->gen.clock_divider; } @@ -179,9 +184,11 @@ next_int = cycle; } } +#ifdef FETCH_DEBUG if (next_int != upd->int_cycle) { printf("UPD78K/II int cycle: %u, cur cycle %u\n", next_int, upd->cycles); } +#endif upd->int_cycle = next_int; } @@ -492,3 +499,24 @@ } fatal_error("upd78k2_calc_vector: %X\n", upd->scratch1); } + +void upd78k2_adjust_cycles(upd78k2_context *upd, uint32_t deduction) +{ + upd78k2_update_timer0(upd); + upd78k2_update_timer1(upd); + if (upd->cycles <= deduction) { + upd->cycles = 0; + } else { + upd->cycles -= deduction; + } + if (upd->tm0_cycle <= deduction) { + upd->tm0_cycle = 0; + } else { + upd->tm0_cycle -= deduction; + } + if (upd->tm1_cycle <= deduction) { + upd->tm1_cycle = 0; + } else { + upd->tm1_cycle -= deduction; + } +}