mirror of https://github.com/inolen/redream.git
initial hle bios
This commit is contained in:
parent
e069ca021e
commit
fae06b247c
|
@ -176,6 +176,7 @@ set(REDREAM_SOURCES
|
|||
src/guest/arm7/arm7.c
|
||||
src/guest/bios/bios.c
|
||||
src/guest/bios/flash.c
|
||||
src/guest/bios/syscalls.c
|
||||
src/guest/gdrom/cdi.c
|
||||
src/guest/gdrom/disc.c
|
||||
src/guest/gdrom/gdi.c
|
||||
|
|
|
@ -3,15 +3,21 @@
|
|||
#include "core/math.h"
|
||||
#include "core/option.h"
|
||||
#include "guest/aica/aica.h"
|
||||
#include "guest/bios/bios.h"
|
||||
#include "guest/bios/flash.h"
|
||||
#include "guest/bios/syscalls.h"
|
||||
#include "guest/dreamcast.h"
|
||||
#include "guest/gdrom/gdrom.h"
|
||||
#include "guest/gdrom/iso.h"
|
||||
#include "guest/rom/flash.h"
|
||||
#include "guest/sh4/sh4.h"
|
||||
#include "render/imgui.h"
|
||||
|
||||
DEFINE_OPTION_STRING(region, "america", "System region");
|
||||
DEFINE_OPTION_STRING(language, "english", "System language");
|
||||
DEFINE_OPTION_STRING(broadcast, "ntsc", "System broadcast mode");
|
||||
|
||||
/* system settings */
|
||||
static const char *regions[] = {
|
||||
"japan", "america", "europe",
|
||||
};
|
||||
|
@ -24,8 +30,22 @@ static const char *broadcasts[] = {
|
|||
"ntsc", "pal", "pal_m", "pal_n",
|
||||
};
|
||||
|
||||
struct bios {
|
||||
struct dreamcast *dc;
|
||||
/* address of syscall vectors */
|
||||
enum {
|
||||
VECTOR_SYSINFO = 0x0c0000b0,
|
||||
VECTOR_FONTROM = 0x0c0000b4,
|
||||
VECTOR_FLASHROM = 0x0c0000b8,
|
||||
VECTOR_GDROM = 0x0c0000bc,
|
||||
VECTOR_MENU = 0x0c0000e0,
|
||||
};
|
||||
|
||||
/* address of syscall entrypoints */
|
||||
enum {
|
||||
SYSCALL_SYSINFO = 0x0c003c00,
|
||||
SYSCALL_FONTROM = 0x0c003b80,
|
||||
SYSCALL_FLASHROM = 0x0c003d00,
|
||||
SYSCALL_GDROM = 0x0c001000,
|
||||
SYSCALL_MENU = 0x0c000800,
|
||||
};
|
||||
|
||||
static uint32_t bios_local_time() {
|
||||
|
@ -148,7 +168,7 @@ static void bios_validate_flash(struct bios *bios) {
|
|||
|
||||
/* validate partition 1 (reserved) */
|
||||
{
|
||||
LOG_INFO("bios_validate_flash resetting FLASH_PT_RESERVED");
|
||||
/* LOG_INFO("bios_validate_flash resetting FLASH_PT_RESERVED"); */
|
||||
|
||||
flash_erase_partition(flash, FLASH_PT_RESERVED);
|
||||
}
|
||||
|
@ -184,6 +204,131 @@ static void bios_validate_flash(struct bios *bios) {
|
|||
}
|
||||
}
|
||||
|
||||
static int bios_boot(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct flash *flash = dc->flash;
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
struct sh4 *sh4 = dc->sh4;
|
||||
struct sh4_context *ctx = &sh4->ctx;
|
||||
struct address_space *space = sh4->memory_if->space;
|
||||
|
||||
const uint32_t BOOT1_ADDR = 0x8c008000;
|
||||
const uint32_t BOOT2_ADDR = 0x8c010000;
|
||||
const uint32_t SYSINFO_ADDR = 0x8c000068;
|
||||
const enum gd_secfmt secfmt = SECTOR_ANY;
|
||||
const enum gd_secmask secmask = MASK_DATA;
|
||||
const int secsz = 2048;
|
||||
uint8_t tmp[0x10000];
|
||||
|
||||
LOG_INFO("bios_boot using hle bootstrap");
|
||||
|
||||
/* load ip.bin bootstrap */
|
||||
{
|
||||
int fad = 45150;
|
||||
int n = 16;
|
||||
int r = gdrom_read_sectors(gd, fad, secfmt, secmask, n, tmp, sizeof(tmp));
|
||||
if (!r) {
|
||||
return 0;
|
||||
}
|
||||
as_memcpy_to_guest(space, BOOT1_ADDR, tmp, r);
|
||||
}
|
||||
|
||||
/* load 1st_read.bin into ram */
|
||||
{
|
||||
static const char *bootfile = "1ST_READ.BIN";
|
||||
|
||||
/* read primary volume descriptor */
|
||||
int fad = 45150 + ISO_PVD_SECTOR;
|
||||
int n = 1;
|
||||
int r = gdrom_read_sectors(gd, fad, secfmt, secmask, n, tmp, sizeof(tmp));
|
||||
if (!r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iso_pvd *pvd = (struct iso_pvd *)tmp;
|
||||
CHECK(pvd->type == 1);
|
||||
CHECK(memcmp(pvd->id, "CD001", 5) == 0);
|
||||
CHECK(pvd->version == 1);
|
||||
|
||||
/* check root directory for the bootfile */
|
||||
struct iso_dir *root = &pvd->root_directory_record;
|
||||
int len = align_up(root->size.le, secsz);
|
||||
fad = GDROM_PREGAP + root->extent.le;
|
||||
n = len / secsz;
|
||||
r = gdrom_read_sectors(gd, fad, secfmt, secmask, n, tmp, sizeof(tmp));
|
||||
if (!r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *ptr = tmp;
|
||||
uint8_t *end = tmp + len;
|
||||
|
||||
while (ptr < end) {
|
||||
struct iso_dir *dir = (struct iso_dir *)ptr;
|
||||
const char *filename = (const char *)(ptr + sizeof(*dir));
|
||||
|
||||
if (memcmp(filename, bootfile, strlen(bootfile)) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* dir entries always begin on an even byte */
|
||||
ptr = (uint8_t *)filename + dir->name_len;
|
||||
ptr = (uint8_t *)align_up((intptr_t)ptr, (intptr_t)2);
|
||||
}
|
||||
|
||||
if (ptr == end) {
|
||||
LOG_WARNING("bios_boot failed to find '%s'", bootfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy the bootfile into ram */
|
||||
struct iso_dir *dir = (struct iso_dir *)ptr;
|
||||
fad = GDROM_PREGAP + dir->extent.le;
|
||||
n = align_up(dir->size.le, secsz) / secsz;
|
||||
r = gdrom_copy_sectors(gd, fad, secfmt, secmask, n, space, BOOT2_ADDR);
|
||||
if (!r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_INFO("bios_boot found '%s' at fad=%d size=%d", bootfile, fad,
|
||||
dir->size.le);
|
||||
}
|
||||
|
||||
/* write system info */
|
||||
{
|
||||
uint8_t data[24] = {0};
|
||||
|
||||
/* read system id from 0x0001a056 */
|
||||
flash_read(flash, 0x1a056, &data[0], 8);
|
||||
|
||||
/* read system properties from 0x0001a000 */
|
||||
flash_read(flash, 0x1a000, &data[8], 5);
|
||||
|
||||
/* read system settings */
|
||||
struct flash_syscfg_block syscfg;
|
||||
int r = flash_read_block(flash, FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg);
|
||||
CHECK_EQ(r, 1);
|
||||
|
||||
memcpy(&data[16], &syscfg.time_lo, 8);
|
||||
|
||||
as_memcpy_to_guest(space, SYSINFO_ADDR, data, sizeof(data));
|
||||
}
|
||||
|
||||
/* write out syscall addresses to vectors */
|
||||
{
|
||||
as_write32(space, VECTOR_FONTROM, SYSCALL_FONTROM);
|
||||
as_write32(space, VECTOR_SYSINFO, SYSCALL_SYSINFO);
|
||||
as_write32(space, VECTOR_FLASHROM, SYSCALL_FLASHROM);
|
||||
as_write32(space, VECTOR_GDROM, SYSCALL_GDROM);
|
||||
as_write32(space, VECTOR_MENU, SYSCALL_MENU);
|
||||
}
|
||||
|
||||
/* start executing at license screen code inside of ip.bin */
|
||||
ctx->pc = 0xac008300;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void bios_debug_menu(struct bios *bios) {
|
||||
int changed = 0;
|
||||
|
||||
|
@ -238,11 +383,69 @@ void bios_debug_menu(struct bios *bios) {
|
|||
}
|
||||
}
|
||||
|
||||
int bios_invalid_instr(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
uint32_t pc = ctx->pc & 0x1cffffff;
|
||||
|
||||
if (pc == 0x0) {
|
||||
return bios_boot(bios);
|
||||
}
|
||||
|
||||
int handled = 1;
|
||||
|
||||
switch (pc) {
|
||||
case SYSCALL_FONTROM:
|
||||
bios_fontrom_vector(bios);
|
||||
break;
|
||||
|
||||
case SYSCALL_SYSINFO:
|
||||
bios_sysinfo_vector(bios);
|
||||
break;
|
||||
|
||||
case SYSCALL_FLASHROM:
|
||||
bios_flashrom_vector(bios);
|
||||
break;
|
||||
|
||||
case SYSCALL_GDROM:
|
||||
bios_gdrom_vector(bios);
|
||||
break;
|
||||
|
||||
case SYSCALL_MENU:
|
||||
bios_menu_vector(bios);
|
||||
break;
|
||||
|
||||
default:
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
int bios_init(struct bios *bios) {
|
||||
bios_validate_flash(bios);
|
||||
|
||||
bios_override_flash_settings(bios);
|
||||
|
||||
/* this code enables a "hybrid" hle mode. in this mode, syscalls are patched
|
||||
to trap into their hle handlers, but the real bios can still be ran to
|
||||
test if bugs exist in the syscall emulation or bootstrap emulation */
|
||||
#if 0
|
||||
/* write out invalid instructions at syscall entry points. note, the boot rom
|
||||
does a bootstrap on startup which copies the boot rom into system ram. due
|
||||
to this, the invalid instructions are written to the original rom, not the
|
||||
system ram (or else, they would be overwritten by the bootstrap process) */
|
||||
struct boot *boot = bios->dc->boot;
|
||||
uint16_t invalid = 0x0;
|
||||
|
||||
boot_write(boot, SYSCALL_FONTROM, &invalid, 2);
|
||||
boot_write(boot, SYSCALL_SYSINFO, &invalid, 2);
|
||||
boot_write(boot, SYSCALL_FLASHROM, &invalid, 2);
|
||||
boot_write(boot, SYSCALL_GDROM, &invalid, 2);
|
||||
/*boot_write(boot, SYSCALL_MENU, &invalid, 2);*/
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
#ifndef BIOS_H
|
||||
#define BIOS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct dreamcast;
|
||||
struct bios;
|
||||
|
||||
struct bios {
|
||||
struct dreamcast *dc;
|
||||
|
||||
/* gdrom state */
|
||||
uint32_t status;
|
||||
uint32_t cmd_id;
|
||||
uint32_t cmd_code;
|
||||
uint32_t params[4];
|
||||
uint32_t result[4];
|
||||
};
|
||||
|
||||
struct bios *bios_create(struct dreamcast *dc);
|
||||
void bios_destroy(struct bios *bios);
|
||||
|
||||
int bios_init(struct bios *bios);
|
||||
int bios_invalid_instr(struct bios *bios);
|
||||
|
||||
void bios_debug_menu(struct bios *bios);
|
||||
|
||||
|
|
|
@ -0,0 +1,859 @@
|
|||
#include "guest/bios/syscalls.h"
|
||||
#include "guest/bios/bios.h"
|
||||
#include "guest/bios/flash.h"
|
||||
#include "guest/gdrom/gdrom.h"
|
||||
#include "guest/holly/holly.h"
|
||||
#include "guest/rom/flash.h"
|
||||
#include "guest/sh4/sh4.h"
|
||||
|
||||
#if 0
|
||||
#define LOG_SYSCALL LOG_INFO
|
||||
#else
|
||||
#define LOG_SYSCALL(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* menu syscalls
|
||||
*/
|
||||
void bios_menu_vector(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
|
||||
uint32_t fn = ctx->r[4];
|
||||
|
||||
LOG_SYSCALL("MENU 0x%x", fn);
|
||||
|
||||
/* FIXME returning 0 is totally broken, some games (e.g. Virtua Fighter 3b)
|
||||
seem to use these functions to soft reset the machine or something */
|
||||
|
||||
/* nop, branch to the return address */
|
||||
ctx->pc = ctx->pr;
|
||||
}
|
||||
|
||||
/*
|
||||
* gdrom syscalls
|
||||
*/
|
||||
enum {
|
||||
MISC_INIT = 0,
|
||||
MISC_SETVECTOR = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
GDROM_SEND_COMMAND = 0,
|
||||
GDROM_CHECK_COMMAND = 1,
|
||||
GDROM_MAINLOOP = 2,
|
||||
GDROM_INIT = 3,
|
||||
GDROM_CHECK_DRIVE = 4,
|
||||
GDROM_G1_DMA_END = 5,
|
||||
GDROM_REQ_DMA = 6,
|
||||
GDROM_CHECK_DMA = 7,
|
||||
GDROM_ABORT_COMMAND = 8,
|
||||
GDROM_RESET = 9,
|
||||
GDROM_SECTOR_MODE = 10,
|
||||
};
|
||||
|
||||
enum {
|
||||
GDC_PIOREAD = 16,
|
||||
GDC_DMAREAD = 17,
|
||||
GDC_GETTOC = 18,
|
||||
GDC_GETTOC2 = 19,
|
||||
GDC_PLAY = 20,
|
||||
GDC_PLAY2 = 21,
|
||||
GDC_PAUSE = 22,
|
||||
GDC_RELEASE = 23,
|
||||
GDC_INIT = 24,
|
||||
GDC_SEEK = 27,
|
||||
GDC_READ = 28,
|
||||
GDC_REQ_MODE = 30,
|
||||
GDC_SET_MODE = 31,
|
||||
GDC_STOP = 33,
|
||||
GDC_GET_SCD = 34,
|
||||
GDC_REQ_SES = 35,
|
||||
GDC_REQ_STAT = 36,
|
||||
GDC_GET_VER = 40,
|
||||
};
|
||||
|
||||
enum {
|
||||
GDC_STATUS_NONE = 0,
|
||||
GDC_STATUS_ACTIVE = 1,
|
||||
GDC_STATUS_DONE = 2,
|
||||
GDC_STATUS_ABORT = 3,
|
||||
GDC_STATUS_ERROR = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
GDC_ERROR_OK = 0,
|
||||
GDC_ERROR_NO_DISC = 2,
|
||||
GDC_ERROR_DISC_CHANGE = 6,
|
||||
GDC_ERROR_SYSTEM = 1,
|
||||
};
|
||||
|
||||
static uint32_t bios_gdrom_send_cmd(struct bios *bios, uint32_t cmd_code,
|
||||
uint32_t params) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
struct holly *hl = dc->holly;
|
||||
struct sh4 *sh4 = dc->sh4;
|
||||
struct address_space *space = sh4->memory_if->space;
|
||||
|
||||
if (bios->status != GDC_STATUS_NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t next_id = bios->cmd_id + 1;
|
||||
if (next_id == 0) {
|
||||
next_id = 1;
|
||||
}
|
||||
|
||||
bios->status = GDC_STATUS_ACTIVE;
|
||||
bios->cmd_id = next_id;
|
||||
bios->cmd_code = cmd_code;
|
||||
|
||||
memset(bios->params, 0, sizeof(bios->params));
|
||||
memset(bios->result, 0, sizeof(bios->result));
|
||||
|
||||
if (params) {
|
||||
/* greedily copy 4 params every time and hope this doesn't blow up */
|
||||
as_memcpy_to_host(space, &bios->params, params, sizeof(bios->params));
|
||||
}
|
||||
|
||||
return bios->cmd_id;
|
||||
}
|
||||
|
||||
static void bios_gdrom_mainloop(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
struct holly *hl = dc->holly;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
struct address_space *space = dc->sh4->memory_if->space;
|
||||
|
||||
if (bios->status != GDC_STATUS_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (bios->cmd_code) {
|
||||
case GDC_PIOREAD:
|
||||
case GDC_DMAREAD: {
|
||||
uint32_t fad = bios->params[0];
|
||||
uint32_t n = bios->params[1];
|
||||
uint32_t dst = bios->params[2];
|
||||
uint32_t unknown = bios->params[3];
|
||||
enum gd_secfmt fmt = SECTOR_ANY;
|
||||
enum gd_secmask mask = MASK_DATA;
|
||||
|
||||
LOG_SYSCALL("GDC_DMAREAD fad=0x%x n=0x%x dst=0x%x unknown=0x%x",
|
||||
fad, n, dst, unknown);
|
||||
|
||||
/* dma read functionality changes somehow when this in non-zero */
|
||||
CHECK_EQ(unknown, 0);
|
||||
|
||||
int read = gdrom_copy_sectors(gd, fad, fmt, mask, n, space, dst);
|
||||
|
||||
bios->result[2] = read;
|
||||
/* result[3] seems to signals if data is remaining, calculated by:
|
||||
r = (*0xa05f7018 & 0x88) == 0 ? 0 : 2
|
||||
since dmas are performed instantly, this should always be zero */
|
||||
bios->result[3] = 0;
|
||||
} break;
|
||||
|
||||
case GDC_GETTOC: {
|
||||
LOG_FATAL("GDC_GETTOC");
|
||||
} break;
|
||||
|
||||
case GDC_GETTOC2: {
|
||||
uint32_t area = bios->params[0];
|
||||
uint32_t dst = bios->params[1];
|
||||
|
||||
LOG_SYSCALL("GDC_GETTOC2 0=0x%x 1=0x%x", area, dst);
|
||||
|
||||
uint8_t toc[SPI_TOC_SIZE];
|
||||
gdrom_get_toc(gd, area, toc, sizeof(toc));
|
||||
|
||||
/* byte swap to little endian */
|
||||
uint8_t *bytes = (uint8_t *)toc;
|
||||
for (int i = 0; i < (int)sizeof(toc); i += 4) {
|
||||
*(uint32_t *)(bytes + i) = bswap32(*(uint32_t *)(bytes + i));
|
||||
}
|
||||
|
||||
as_memcpy_to_guest(space, dst, toc, sizeof(toc));
|
||||
|
||||
/* record size transferred */
|
||||
bios->result[2] = sizeof(toc);
|
||||
} break;
|
||||
|
||||
case GDC_PLAY: {
|
||||
LOG_WARNING("unsupported GDC_PLAY");
|
||||
} break;
|
||||
|
||||
case GDC_PLAY2: {
|
||||
LOG_WARNING("unsupported GDC_PLAY2");
|
||||
} break;
|
||||
|
||||
case GDC_PAUSE: {
|
||||
LOG_WARNING("unsupported GDC_PAUSE");
|
||||
/* TODO same as SPI_CD_SEEK with parameter type = pause playback */
|
||||
} break;
|
||||
|
||||
case GDC_RELEASE: {
|
||||
LOG_WARNING("GDC_RELEASE");
|
||||
} break;
|
||||
|
||||
case GDC_INIT: {
|
||||
/* this seems to always immediately follow GDROM_INIT */
|
||||
} break;
|
||||
|
||||
case GDC_SEEK: {
|
||||
LOG_FATAL("GDC_SEEK");
|
||||
} break;
|
||||
|
||||
case GDC_READ: {
|
||||
LOG_FATAL("GDC_READ");
|
||||
} break;
|
||||
|
||||
case GDC_REQ_MODE: {
|
||||
uint32_t dst = bios->params[0];
|
||||
|
||||
LOG_SYSCALL("GDC_REQ_MODE 0x%x", dst);
|
||||
|
||||
struct gd_hw_info info;
|
||||
gdrom_get_drive_mode(gd, &info);
|
||||
|
||||
uint32_t mode[4];
|
||||
mode[0] = info.speed;
|
||||
mode[1] = (info.standby_hi << 8) | info.standby_lo;
|
||||
mode[2] = info.read_flags;
|
||||
mode[3] = info.read_retry;
|
||||
|
||||
as_memcpy_to_guest(space, dst, mode, sizeof(mode));
|
||||
|
||||
/* record size transferred */
|
||||
bios->result[2] = sizeof(mode);
|
||||
} break;
|
||||
|
||||
case GDC_SET_MODE: {
|
||||
uint32_t speed = bios->params[0];
|
||||
uint32_t standby = bios->params[1];
|
||||
uint32_t read_flags = bios->params[2];
|
||||
uint32_t read_retry = bios->params[3];
|
||||
|
||||
LOG_SYSCALL("GDC_SET_MODE 0x%x 0x%x 0x%x 0x%x", speed, standby,
|
||||
read_flags, read_retry);
|
||||
|
||||
struct gd_hw_info info;
|
||||
gdrom_get_drive_mode(gd, &info);
|
||||
|
||||
info.speed = speed;
|
||||
info.standby_hi = (standby & 0xff00) >> 8;
|
||||
info.standby_lo = standby & 0xff;
|
||||
info.read_flags = read_flags;
|
||||
info.read_retry = read_retry;
|
||||
|
||||
gdrom_set_drive_mode(gd, &info);
|
||||
} break;
|
||||
|
||||
case GDC_STOP: {
|
||||
LOG_FATAL("GDC_STOP");
|
||||
} break;
|
||||
|
||||
case GDC_GET_SCD: {
|
||||
uint32_t format = bios->params[0];
|
||||
uint32_t size = bios->params[1];
|
||||
uint32_t dst = bios->params[2];
|
||||
|
||||
LOG_SYSCALL("GDC_GET_SCD 0x%x 0x%x 0x%x", format, size, dst);
|
||||
|
||||
uint8_t scd[SPI_SCD_SIZE];
|
||||
gdrom_get_subcode(gd, format, scd, sizeof(scd));
|
||||
|
||||
CHECK_EQ(scd[3], size);
|
||||
|
||||
as_memcpy_to_guest(space, dst, scd, size);
|
||||
|
||||
/* record size transferred */
|
||||
bios->result[2] = size;
|
||||
} break;
|
||||
|
||||
case GDC_REQ_SES: {
|
||||
LOG_FATAL("GDC_REQ_SES");
|
||||
} break;
|
||||
|
||||
case GDC_REQ_STAT: {
|
||||
LOG_SYSCALL("GDC_REQ_STAT");
|
||||
|
||||
uint8_t status[SPI_STAT_SIZE];
|
||||
gdrom_get_status(gd, status, sizeof(status));
|
||||
|
||||
uint32_t drive_status = status[0];
|
||||
uint32_t repeat_count = status[1] & 0xf;
|
||||
uint32_t scd_track = status[3];
|
||||
uint32_t scd_index = status[4];
|
||||
uint32_t fad = bswap24(*(uint32_t *)&status[5] & 0xffffff);
|
||||
|
||||
bios->result[0] = (repeat_count << 8) | drive_status;
|
||||
bios->result[1] = scd_track;
|
||||
bios->result[2] = fad;
|
||||
bios->result[3] = scd_index;
|
||||
} break;
|
||||
|
||||
case GDC_GET_VER: {
|
||||
uint32_t dst = bios->params[0];
|
||||
|
||||
LOG_SYSCALL("GDC_GET_VER 0x%x", dst);
|
||||
|
||||
const char *version = "GDC Version 1.10 1999-03-31";
|
||||
const int len = strlen(version);
|
||||
|
||||
as_memcpy_to_guest(space, dst, version, len);
|
||||
|
||||
/* record size transferred */
|
||||
bios->result[2] = len;
|
||||
} break;
|
||||
|
||||
default: { LOG_FATAL("unexpected gdrom cmd 0x%x", bios->cmd_code); } break;
|
||||
}
|
||||
|
||||
bios->status = GDC_STATUS_DONE;
|
||||
}
|
||||
|
||||
void bios_gdrom_vector(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
struct holly *hl = dc->holly;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
struct address_space *space = dc->sh4->memory_if->space;
|
||||
|
||||
uint32_t misc = ctx->r[6];
|
||||
uint32_t fn = ctx->r[7];
|
||||
|
||||
if (misc) {
|
||||
switch (fn) {
|
||||
case MISC_INIT: {
|
||||
/*
|
||||
* MISC_INIT
|
||||
*
|
||||
* initializes all the syscall vectors to their default values
|
||||
*/
|
||||
LOG_FATAL("MISC_INIT");
|
||||
} break;
|
||||
|
||||
case MISC_SETVECTOR: {
|
||||
/*
|
||||
* MISC_SETVECTOR
|
||||
*
|
||||
* sets/clears the handler for one of the eight superfunctions for this
|
||||
* vector. setting a handler is only allowed if it not currently set
|
||||
*
|
||||
* r4: superfunction number (0-7)
|
||||
* r5: pointer to handler function, or NULL to clear
|
||||
*
|
||||
* r0: zero if successful, -1 if setting/clearing the handler fails
|
||||
*/
|
||||
LOG_FATAL("MISC_SETVECTOR");
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("unexpected MISC syscall %d", fn);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (fn) {
|
||||
case GDROM_SEND_COMMAND: {
|
||||
/*
|
||||
* GDROM_SEND_COMMAND
|
||||
*
|
||||
* enqueue a command for the gdrom to execute
|
||||
*
|
||||
* r4: command code
|
||||
* r5: pointer to parameter block for the command, can be NULL if the
|
||||
* command does not take parameters
|
||||
*
|
||||
* r0: a request id (>0) if successful, negative error code if failed
|
||||
*/
|
||||
uint32_t cmd_code = ctx->r[4];
|
||||
uint32_t params = ctx->r[5];
|
||||
uint32_t cmd_id = bios_gdrom_send_cmd(bios, cmd_code, params);
|
||||
|
||||
LOG_SYSCALL("GDROM_SEND_COMMAND cmd_code=0x%x params=0x%x cmd_id=0x%x",
|
||||
cmd_code, params, cmd_id);
|
||||
|
||||
ctx->r[0] = cmd_id;
|
||||
} break;
|
||||
|
||||
case GDROM_CHECK_COMMAND: {
|
||||
/*
|
||||
* GDROM_CHECK_COMMAND
|
||||
*
|
||||
* check if an enqueued command has completed
|
||||
*
|
||||
* r4: request id
|
||||
* r5: pointer to four 32 bit integers to receive extended status
|
||||
* information. the first is a generic error code
|
||||
*
|
||||
* r0: 0, no such request active
|
||||
* 1, request is still being processed
|
||||
* 2, request has completed (if queried again, you will get a 0)
|
||||
* 3, request was aborted(?)
|
||||
* -1, request has failed (examine extended status information for
|
||||
* cause of failure)
|
||||
*/
|
||||
uint32_t cmd_id = ctx->r[4];
|
||||
uint32_t status = ctx->r[5];
|
||||
|
||||
LOG_SYSCALL("GDROM_CHECK_COMMAND 0x%x 0x%x", cmd_id, status);
|
||||
|
||||
if (cmd_id == bios->cmd_id) {
|
||||
ctx->r[0] = bios->status;
|
||||
|
||||
if (bios->status == GDC_STATUS_DONE) {
|
||||
as_memcpy_to_guest(space, status, &bios->result,
|
||||
sizeof(bios->result));
|
||||
bios->status = GDC_STATUS_NONE;
|
||||
}
|
||||
} else {
|
||||
ctx->r[0] = GDC_STATUS_NONE;
|
||||
}
|
||||
} break;
|
||||
|
||||
case GDROM_MAINLOOP: {
|
||||
/*
|
||||
* GDROM_MAINLOOP
|
||||
*
|
||||
* in order for enqueued commands to get processed, this function must
|
||||
* be called a few times. it can be called from a periodic interrupt, or
|
||||
* just keep calling it manually until GDROM_CHECK_COMMAND says that
|
||||
* your command has stopped processing
|
||||
*/
|
||||
LOG_SYSCALL("GDROM_MAINLOOP");
|
||||
|
||||
bios_gdrom_mainloop(bios);
|
||||
} break;
|
||||
|
||||
case GDROM_INIT: {
|
||||
/*
|
||||
* GDROM_INIT
|
||||
*
|
||||
* initialize the gdrom subsystem. should be called before any requests
|
||||
* are enqueued
|
||||
*/
|
||||
LOG_SYSCALL("GDROM_INIT");
|
||||
|
||||
bios->status = GDC_STATUS_NONE;
|
||||
} break;
|
||||
|
||||
case GDROM_CHECK_DRIVE: {
|
||||
/*
|
||||
* GDROM_CHECK_DRIVE
|
||||
*
|
||||
* checks the general condition of the drive
|
||||
*
|
||||
* r4: pointer to two 32 bit integers, to receive the drive status. the
|
||||
* first is the current drive status, the second is the type of disc
|
||||
* inserted (if any)
|
||||
*
|
||||
* drive status: 0x00, drive is busy
|
||||
* 0x01, drive is paused
|
||||
* 0x02, drive is in standby
|
||||
* 0x03, drive is playing
|
||||
* 0x04, drive is seeking
|
||||
* 0x05, drive is scanning
|
||||
* 0x06, drive lid is open
|
||||
* 0x07, lid is closed, but there is no disc
|
||||
*
|
||||
* disk format: 0x00, CDDA
|
||||
* 0x10, CDROM
|
||||
* 0x20, CDROM/XA
|
||||
* 0x30, CDI
|
||||
* 0x80, GDROM
|
||||
*
|
||||
* r0: zero if successful, nonzero if failure
|
||||
*/
|
||||
uint32_t result = ctx->r[4];
|
||||
|
||||
LOG_SYSCALL("GDROM_CHECK_DRIVE 0x%x", result);
|
||||
|
||||
uint8_t status[SPI_STAT_SIZE];
|
||||
gdrom_get_status(gd, status, sizeof(status));
|
||||
|
||||
uint32_t cond[2];
|
||||
cond[0] = status[0] & 0xf; /* drive status */
|
||||
cond[1] = status[1] & 0xf0; /* disk format */
|
||||
|
||||
as_memcpy_to_guest(space, result, cond, sizeof(cond));
|
||||
|
||||
/* success */
|
||||
ctx->r[0] = 0;
|
||||
} break;
|
||||
|
||||
case GDROM_G1_DMA_END: {
|
||||
/*
|
||||
* GDROM_G1_DMA_END
|
||||
*
|
||||
* r4: callback
|
||||
* r5: callback param
|
||||
*/
|
||||
uint32_t callback = ctx->r[4];
|
||||
uint32_t param = ctx->r[5];
|
||||
|
||||
LOG_SYSCALL("GDROM_G1_DMA_END 0x%x 0x%x", callback, param);
|
||||
|
||||
holly_clear_interrupt(hl, HOLLY_INT_G1DEINT);
|
||||
|
||||
/* TODO support callbacks */
|
||||
CHECK_EQ(callback, 0);
|
||||
} break;
|
||||
|
||||
case GDROM_REQ_DMA: {
|
||||
/*
|
||||
* GDROM_REQ_DMA
|
||||
*/
|
||||
LOG_FATAL("GDROM_REQ_DMA");
|
||||
} break;
|
||||
|
||||
case GDROM_CHECK_DMA: {
|
||||
/*
|
||||
* GDROM_CHECK_DMA
|
||||
*/
|
||||
/* read SB_GDST to check if DMA is in progress, if so, write out
|
||||
SB_GDLEND to r5 and return 1
|
||||
|
||||
if no dma is in progress, write out amount of data available in
|
||||
DMA buffer and return 0 */
|
||||
LOG_FATAL("GDROM_CHECK_DMA");
|
||||
} break;
|
||||
|
||||
case GDROM_ABORT_COMMAND: {
|
||||
/*
|
||||
* GDROM_ABORT_COMMAND
|
||||
*
|
||||
* tries to abort a previously enqueued command
|
||||
*
|
||||
* r4: request id
|
||||
*
|
||||
* r0: zero if successful, nonzero if failure
|
||||
*/
|
||||
LOG_SYSCALL("GDROM_ABORT_COMMAND");
|
||||
|
||||
/* all commands are performed immediately, there's nothing to cancel */
|
||||
ctx->r[0] = -1;
|
||||
} break;
|
||||
|
||||
case GDROM_RESET: {
|
||||
/*
|
||||
* GDROM_RESET
|
||||
*
|
||||
* resets the drive
|
||||
*/
|
||||
LOG_FATAL("GDROM_RESET");
|
||||
} break;
|
||||
|
||||
case GDROM_SECTOR_MODE: {
|
||||
/*
|
||||
* GDROM_SECTOR_MODE
|
||||
*
|
||||
* sets/gets the sector format for read commands
|
||||
*
|
||||
* r4: pointer to a struct of four 32 bit integers containing new
|
||||
* values or to receive the old values
|
||||
*
|
||||
* field function
|
||||
* 0 if 0 the mode will be set, if 1 it will be queried
|
||||
* 1 ? (always 8192)
|
||||
* 2 1024 = mode 1, 2048 = mode 2, 0 = auto detect
|
||||
* 3 sector size in bytes (normally 2048)
|
||||
*
|
||||
* r0: zero if successful, -1 if failure
|
||||
*/
|
||||
LOG_FATAL("GDROM_SECTOR_MODE");
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("unexpected GDROM syscall %d", fn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* branch to the return address */
|
||||
ctx->pc = ctx->pr;
|
||||
}
|
||||
|
||||
/*
|
||||
* flashrom syscalls
|
||||
*/
|
||||
enum {
|
||||
FLASHROM_INFO = 0,
|
||||
FLASHROM_READ = 1,
|
||||
FLASHROM_PROGRAM = 2,
|
||||
FLASHROM_ERASE = 3,
|
||||
};
|
||||
|
||||
void bios_flashrom_vector(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct flash *flash = dc->flash;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
struct address_space *space = dc->sh4->memory_if->space;
|
||||
|
||||
int fn = ctx->r[7];
|
||||
|
||||
switch (fn) {
|
||||
case FLASHROM_INFO: {
|
||||
/*
|
||||
* FLASHROM_INFO
|
||||
*
|
||||
* queries the extent of a single partition in the system flashrom
|
||||
*
|
||||
* r4: partition number (0-4)
|
||||
* r5: pointer to two 32 bit integers to receive the result. the first
|
||||
* will be the offset of the partition start, in bytes from the start
|
||||
* of the flashrom. the second will be the size of the partition in
|
||||
* bytes
|
||||
*
|
||||
* r0: zero if successful, -1 if no such partition exists
|
||||
*/
|
||||
int part_id = ctx->r[4];
|
||||
uint32_t dst = ctx->r[5];
|
||||
|
||||
LOG_SYSCALL("FLASHROM_INFO 0x%x 0x%x", part_id, dst);
|
||||
|
||||
int offset, size;
|
||||
flash_partition_info(part_id, &offset, &size);
|
||||
|
||||
uint32_t result[2];
|
||||
result[0] = offset;
|
||||
result[1] = size;
|
||||
as_memcpy_to_guest(space, dst, result, sizeof(result));
|
||||
|
||||
ctx->r[0] = 0;
|
||||
} break;
|
||||
|
||||
case FLASHROM_READ: {
|
||||
/*
|
||||
* FLASHROM_READ
|
||||
*
|
||||
* read data from the system flashrom
|
||||
*
|
||||
* r4: read start position, in bytes from the start of the flashrom
|
||||
* r5: pointer to destination buffer
|
||||
* r6: number of bytes to read
|
||||
*
|
||||
* r0: number of read bytes if successful, -1 if read failed
|
||||
*/
|
||||
int offset = ctx->r[4];
|
||||
uint32_t dst = ctx->r[5];
|
||||
int size = ctx->r[6];
|
||||
|
||||
LOG_SYSCALL("FLASHROM_READ 0x%x 0x%x 0x%x", offset, dst, size);
|
||||
|
||||
uint8_t tmp[32];
|
||||
int read = 0;
|
||||
|
||||
while (read < size) {
|
||||
int n = MIN(size - read, (int)sizeof(tmp));
|
||||
flash_read(flash, offset + read, tmp, n);
|
||||
as_memcpy_to_guest(space, dst + read, tmp, n);
|
||||
read += n;
|
||||
}
|
||||
|
||||
ctx->r[0] = read;
|
||||
} break;
|
||||
|
||||
case FLASHROM_PROGRAM: {
|
||||
/*
|
||||
* FLASHROM_PROGRAM
|
||||
*
|
||||
* write data to the system flashrom. important: it is only possible to
|
||||
* overwrite 1's with 0's, 0's can not be written back to 1's. general
|
||||
* overwriting is therefore not possible. only bytes containing all ones
|
||||
* can be written with arbitrary values
|
||||
*
|
||||
* r4: write start position, in bytes from the start of the flashrom
|
||||
* r5: pointer to source buffer
|
||||
* r6: number of bytes to write
|
||||
*
|
||||
* r0: number of written bytes if successful, -1 if write failed
|
||||
*/
|
||||
int offset = ctx->r[4];
|
||||
uint32_t src = ctx->r[5];
|
||||
int size = ctx->r[6];
|
||||
|
||||
LOG_SYSCALL("FLASHROM_PROGRAM 0x%x 0x%x 0x%x", offset, src, size);
|
||||
|
||||
uint8_t tmp[32];
|
||||
int wrote = 0;
|
||||
|
||||
while (wrote < size) {
|
||||
int n = MIN(size - wrote, (int)sizeof(tmp));
|
||||
as_memcpy_to_host(space, tmp, src + wrote, n);
|
||||
flash_program(flash, offset + wrote, tmp, n);
|
||||
wrote += n;
|
||||
}
|
||||
|
||||
ctx->r[0] = wrote;
|
||||
} break;
|
||||
|
||||
case FLASHROM_ERASE: {
|
||||
/*
|
||||
* FLASHROM_ERASE
|
||||
*
|
||||
* return a flashrom partition to all ones, so that it may be rewritten
|
||||
*
|
||||
* r4: offset of the start of the partition you want to delete, in bytes
|
||||
* from the start of the flashrom
|
||||
*
|
||||
* r0: zero if successful, -1 if delete failed
|
||||
*/
|
||||
uint32_t start = ctx->r[4];
|
||||
|
||||
LOG_SYSCALL("FLASHROM_ERASE 0x%x", start);
|
||||
|
||||
int part_id = 0;
|
||||
while (part_id < FLASH_PT_NUM) {
|
||||
int offset, size;
|
||||
flash_partition_info(part_id, &offset, &size);
|
||||
|
||||
if (offset == (int)start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_NE(part_id, FLASH_PT_NUM);
|
||||
|
||||
flash_erase_partition(flash, part_id);
|
||||
|
||||
ctx->r[0] = 0;
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("unexpected FLASHROM syscall %d", fn);
|
||||
break;
|
||||
}
|
||||
|
||||
/* branch to the return address */
|
||||
ctx->pc = ctx->pr;
|
||||
}
|
||||
|
||||
/*
|
||||
* fontrom syscalls
|
||||
*/
|
||||
void bios_fontrom_vector(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
|
||||
int fn = ctx->r[1];
|
||||
|
||||
switch (fn) {
|
||||
case 0: {
|
||||
LOG_SYSCALL("FONTROM_ADDRESS");
|
||||
|
||||
/* FIXME embed a valid font and return the address to it here */
|
||||
ctx->r[0] = 0;
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
LOG_SYSCALL("FONTROM_LOCK");
|
||||
|
||||
/* success, mutex aquired */
|
||||
ctx->r[0] = 0;
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
LOG_SYSCALL("FONTROM_UNLOCK");
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("unknown FONTROM syscall %d", fn);
|
||||
break;
|
||||
}
|
||||
|
||||
/* branch to the return address */
|
||||
ctx->pc = ctx->pr;
|
||||
}
|
||||
|
||||
/*
|
||||
* sysinfo syscalls
|
||||
*/
|
||||
enum {
|
||||
SYSINFO_INIT = 0,
|
||||
SYSINFO_ICON = 2,
|
||||
SYSINFO_ID = 3,
|
||||
};
|
||||
|
||||
void bios_sysinfo_vector(struct bios *bios) {
|
||||
struct dreamcast *dc = bios->dc;
|
||||
struct flash *flash = dc->flash;
|
||||
struct sh4_context *ctx = &dc->sh4->ctx;
|
||||
struct address_space *space = dc->sh4->memory_if->space;
|
||||
const uint32_t SYSINFO_DST = 0x8c000068;
|
||||
|
||||
int fn = ctx->r[7];
|
||||
|
||||
switch (fn) {
|
||||
case SYSINFO_INIT: {
|
||||
/*
|
||||
* SYSINFO_INIT
|
||||
*
|
||||
* prepares the other two SYSINFO calls for use by copying the relevant
|
||||
* data from the system flashrom into 0x8c000068-0x8c00007f. always call
|
||||
* this function before using the other two calls
|
||||
*
|
||||
* 0x8c000068-6f: system_id
|
||||
* 0x8c000070-74: system_props
|
||||
* 0x8c000075-77: padding
|
||||
* 0x8c000078-7f: settings
|
||||
*/
|
||||
LOG_SYSCALL("SYSINFO_INIT");
|
||||
|
||||
uint8_t data[24];
|
||||
|
||||
/* read system_id from 0x0001a056 */
|
||||
flash_read(flash, 0x1a056, &data[0], 8);
|
||||
|
||||
/* read system_props from 0x0001a000 */
|
||||
flash_read(flash, 0x1a000, &data[8], 5);
|
||||
|
||||
/* system settings seem to always be zeroed out */
|
||||
memset(&data[13], 0, 11);
|
||||
|
||||
as_memcpy_to_guest(space, SYSINFO_DST, data, sizeof(data));
|
||||
} break;
|
||||
|
||||
case SYSINFO_ICON: {
|
||||
/*
|
||||
* SYSINFO_ICON
|
||||
*
|
||||
* read an icon from the flashrom. the format those icons are in is not
|
||||
* known. SYSINFO_INIT must have been called first
|
||||
*
|
||||
* r4: icon number (0-9, but only 5-9 seems to really be icons)
|
||||
* r5: destination buffer (704 bytes in size)
|
||||
*
|
||||
* r0: number of read bytes if successful, negative if read failed
|
||||
*/
|
||||
uint32_t icon = ctx->r[4];
|
||||
uint32_t dst = ctx->r[5];
|
||||
|
||||
LOG_SYSCALL("SYSINFO_ICON 0x%x 0x%x", icon, dst);
|
||||
|
||||
ctx->r[0] = 704;
|
||||
} break;
|
||||
|
||||
case SYSINFO_ID: {
|
||||
/*
|
||||
* SYSINFO_ID
|
||||
*
|
||||
* query the unique 64 bit id number of this Dreamcast. SYSINFO_INIT must
|
||||
* have been called first
|
||||
*
|
||||
* r0: a pointer to where the id is stored as 8 contiguous bytes
|
||||
*/
|
||||
LOG_SYSCALL("SYSINFO_ID");
|
||||
|
||||
ctx->r[0] = SYSINFO_DST;
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("unexpected SYSINFO syscall %d", fn);
|
||||
break;
|
||||
}
|
||||
|
||||
/* branch to the return address */
|
||||
ctx->pc = ctx->pr;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
struct bios;
|
||||
|
||||
void bios_fontrom_vector(struct bios *bios);
|
||||
void bios_sysinfo_vector(struct bios *bios);
|
||||
void bios_flashrom_vector(struct bios *bios);
|
||||
void bios_gdrom_vector(struct bios *bios);
|
||||
void bios_menu_vector(struct bios *bios);
|
||||
|
||||
#endif
|
|
@ -70,31 +70,6 @@ void dc_suspend(struct dreamcast *dc) {
|
|||
dc->running = 0;
|
||||
}
|
||||
|
||||
static int dc_load_bin(struct dreamcast *dc, const char *path) {
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
/* load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is loaded to */
|
||||
uint8_t *data = memory_translate(dc->memory, "system ram", 0x00010000);
|
||||
int n = (int)fread(data, sizeof(uint8_t), size, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (n != size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sh4_reset(dc->sh4, 0x0c010000);
|
||||
dc_resume(dc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_load_disc(struct dreamcast *dc, const char *path) {
|
||||
struct disc *disc = disc_create(path);
|
||||
|
||||
|
@ -119,7 +94,7 @@ int dc_load(struct dreamcast *dc, const char *path) {
|
|||
|
||||
LOG_INFO("loading %s", path);
|
||||
|
||||
if (dc_load_disc(dc, path) || dc_load_bin(dc, path)) {
|
||||
if (dc_load_disc(dc, path)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -425,25 +425,51 @@ static void gdrom_event(struct gdrom *gd, enum gd_event ev, int arg) {
|
|||
cb(gd, arg);
|
||||
}
|
||||
|
||||
int gdrom_copy_sectors(struct gdrom *gd, int fad, enum gd_secfmt fmt,
|
||||
enum gd_secmask mask, int num_sectors,
|
||||
struct address_space *space, uint32_t dst) {
|
||||
if (!gd->disc) {
|
||||
LOG_WARNING("gdrom_copy_sectors failed, no disc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read = 0;
|
||||
uint8_t tmp[DISC_MAX_SECTOR_SIZE];
|
||||
|
||||
for (int i = fad; i < fad + num_sectors; i++) {
|
||||
int n = gdrom_read_sectors(gd, i, fmt, mask, 1, tmp, sizeof(tmp));
|
||||
as_memcpy_to_guest(space, dst + read, tmp, n);
|
||||
read += n;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
int gdrom_read_sectors(struct gdrom *gd, int fad, enum gd_secfmt fmt,
|
||||
enum gd_secmask mask, int num_sectors, uint8_t *dst,
|
||||
int dst_size) {
|
||||
int total = 0;
|
||||
if (!gd->disc) {
|
||||
LOG_WARNING("gdrom_read_sectors failed, no disc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read = 0;
|
||||
uint8_t data[DISC_MAX_SECTOR_SIZE];
|
||||
|
||||
LOG_GDROM("gdrom_read_sectors [%d, %d)", fad, fad + num_sectors);
|
||||
|
||||
for (int i = 0; i < num_sectors; i++) {
|
||||
int r = disc_read_sector(gd->disc, fad, fmt, mask, data);
|
||||
memcpy(dst + total, data, r);
|
||||
total += r;
|
||||
fad++;
|
||||
for (int i = fad; i < fad + num_sectors; i++) {
|
||||
int n = disc_read_sector(gd->disc, i, fmt, mask, data);
|
||||
CHECK_LE(read + n, dst_size);
|
||||
memcpy(dst + read, data, n);
|
||||
read += n;
|
||||
}
|
||||
|
||||
return total;
|
||||
return read;
|
||||
}
|
||||
|
||||
void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data, int size) {
|
||||
CHECK_NOTNULL(gd->disc);
|
||||
CHECK_GE(size, SPI_SCD_SIZE);
|
||||
|
||||
/* FIXME implement */
|
||||
|
@ -466,6 +492,7 @@ void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data, int size) {
|
|||
|
||||
void gdrom_get_session(struct gdrom *gd, int session_num, uint8_t *data,
|
||||
int size) {
|
||||
CHECK_NOTNULL(gd->disc);
|
||||
CHECK_GE(size, SPI_SES_SIZE);
|
||||
|
||||
/* session response layout:
|
||||
|
@ -519,6 +546,7 @@ void gdrom_get_session(struct gdrom *gd, int session_num, uint8_t *data,
|
|||
|
||||
void gdrom_get_toc(struct gdrom *gd, enum gd_area area, uint8_t *data,
|
||||
int size) {
|
||||
CHECK_NOTNULL(gd->disc);
|
||||
CHECK_GE(size, SPI_TOC_SIZE);
|
||||
|
||||
/* toc response layout:
|
||||
|
@ -584,6 +612,7 @@ void gdrom_get_toc(struct gdrom *gd, enum gd_area area, uint8_t *data,
|
|||
}
|
||||
|
||||
void gdrom_get_error(struct gdrom *gd, uint8_t *data, int size) {
|
||||
CHECK_NOTNULL(gd->disc);
|
||||
CHECK_GE(size, SPI_ERR_SIZE);
|
||||
|
||||
/* cd status information response layout:
|
||||
|
@ -620,6 +649,7 @@ void gdrom_get_error(struct gdrom *gd, uint8_t *data, int size) {
|
|||
}
|
||||
|
||||
void gdrom_get_status(struct gdrom *gd, uint8_t *data, int size) {
|
||||
CHECK_NOTNULL(gd->disc);
|
||||
CHECK_GE(size, SPI_STAT_SIZE);
|
||||
|
||||
/* cd status information response layout:
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "guest/gdrom/disc.h"
|
||||
#include "guest/gdrom/gdrom_types.h"
|
||||
|
||||
struct address_space;
|
||||
struct dreamcast;
|
||||
struct gdrom;
|
||||
|
||||
|
@ -24,7 +25,9 @@ void gdrom_get_toc(struct gdrom *gd, enum gd_area area_type, uint8_t *data,
|
|||
void gdrom_get_session(struct gdrom *gd, int session, uint8_t *data, int size);
|
||||
void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data, int size);
|
||||
int gdrom_read_sectors(struct gdrom *gd, int fad, enum gd_secfmt fmt,
|
||||
enum gd_secmask mask, int num_sectors, uint8_t *dst,
|
||||
int dst_size);
|
||||
enum gd_secmask mask, int n, uint8_t *dst, int dst_size);
|
||||
int gdrom_copy_sectors(struct gdrom *gd, int fad, enum gd_secfmt fmt,
|
||||
enum gd_secmask mask, int num_sectors,
|
||||
struct address_space *space, uint32_t dst);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
#ifndef ISO_H
|
||||
#define ISO_H
|
||||
|
||||
enum {
|
||||
ISO_PVD_SECTOR = 16,
|
||||
};
|
||||
|
||||
/* iso 9660 file flags */
|
||||
enum {
|
||||
ISO_HIDDEN = 0x1,
|
||||
ISO_DIRECTORY = 0x2,
|
||||
ISO_ASSOCIATED = 0x4,
|
||||
ISO_RECORD = 0x8,
|
||||
ISO_PROTECTION = 0x10,
|
||||
ISO_RESERVED1 = 0x20,
|
||||
ISO_RESERVED2 = 0x40,
|
||||
ISO_MULTIEXTENT = 0x80,
|
||||
};
|
||||
|
||||
/* iso 9660 data types */
|
||||
typedef char achar_t;
|
||||
|
||||
typedef char dchar_t;
|
||||
|
||||
typedef uint8_t iso711_t;
|
||||
|
||||
typedef int8_t iso712_t;
|
||||
|
||||
typedef struct iso723 {
|
||||
uint16_t le;
|
||||
uint16_t be;
|
||||
} iso723_t;
|
||||
|
||||
typedef uint32_t iso731_t;
|
||||
|
||||
typedef uint32_t iso732_t;
|
||||
|
||||
typedef struct iso733 {
|
||||
uint32_t le;
|
||||
uint32_t be;
|
||||
} iso733_t;
|
||||
|
||||
|
||||
/* iso 9660 data structures */
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct iso_ltime {
|
||||
char year[4];
|
||||
char month[2];
|
||||
char day[2];
|
||||
char hour[2];
|
||||
char minute[2];
|
||||
char second[2];
|
||||
char fractions[2];
|
||||
iso712_t gmt_offset;
|
||||
};
|
||||
|
||||
struct iso_dir {
|
||||
iso711_t length;
|
||||
iso711_t xa_length;
|
||||
iso733_t extent;
|
||||
iso733_t size;
|
||||
iso711_t date[7];
|
||||
iso711_t file_flags;
|
||||
iso711_t file_unit_size;
|
||||
iso711_t interleave_gap;
|
||||
iso723_t volume_sequence_number;
|
||||
iso711_t name_len;
|
||||
};
|
||||
|
||||
struct iso_pvd {
|
||||
iso711_t type;
|
||||
unsigned char id[5];
|
||||
iso711_t version;
|
||||
uint8_t unused1;
|
||||
achar_t system_id[32];
|
||||
dchar_t volume_id[32];
|
||||
uint8_t unused2[8];
|
||||
iso733_t volume_space_size;
|
||||
uint8_t unused3[32];
|
||||
iso723_t volume_set_size;
|
||||
iso723_t volume_sequence_number;
|
||||
iso723_t logical_block_size;
|
||||
iso733_t path_table_size;
|
||||
iso731_t type_l_path_table;
|
||||
iso731_t opt_type_l_path_table;
|
||||
iso732_t type_m_path_table;
|
||||
iso732_t opt_type_m_path_table;
|
||||
struct iso_dir root_directory_record;
|
||||
char root_directory_name;
|
||||
dchar_t volume_set_id[128];
|
||||
achar_t publisher_id[128];
|
||||
achar_t preparer_id[128];
|
||||
achar_t application_id[128];
|
||||
dchar_t copyright_file_id[37];
|
||||
dchar_t abstract_file_id[37];
|
||||
dchar_t bibliographic_file_id[37];
|
||||
struct iso_ltime creation_date;
|
||||
struct iso_ltime modification_date;
|
||||
struct iso_ltime expiration_date;
|
||||
struct iso_ltime effective_date;
|
||||
iso711_t file_structure_version;
|
||||
uint8_t unused4;
|
||||
uint8_t application_data[512];
|
||||
uint8_t unused5[653];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
|
@ -73,9 +73,8 @@ static void pvr_reconfigure_spg(struct pvr *pvr) {
|
|||
}
|
||||
|
||||
LOG_INFO(
|
||||
"pvr_reconfigure_spg mode %s, pixel_clock %d, line_clock %d, vcount %d, "
|
||||
"hcount %d"
|
||||
", interlace %d, vbstart %d, vbend %d",
|
||||
"pvr_reconfigure_spg mode=%s pixel_clock=%d line_clock=%d vcount=%d "
|
||||
"hcount=%d interlace=%d vbstart=%d vbend=%d",
|
||||
mode, pixel_clock, pvr->line_clock, pvr->SPG_LOAD->vcount,
|
||||
pvr->SPG_LOAD->hcount, pvr->SPG_CONTROL->interlace,
|
||||
pvr->SPG_VBLANK->vbstart, pvr->SPG_VBLANK->vbend);
|
||||
|
|
|
@ -45,7 +45,6 @@ static int boot_load_rom(struct boot *boot) {
|
|||
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
LOG_WARNING("failed to open boot rom '%s'", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -69,15 +68,16 @@ static int boot_load_rom(struct boot *boot) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
LOG_INFO("boot_load_rom loaded '%s'", filename);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int boot_init(struct device *dev) {
|
||||
struct boot *boot = (struct boot *)dev;
|
||||
|
||||
if (!boot_load_rom(boot)) {
|
||||
return 0;
|
||||
}
|
||||
/* attempt to load the boot rom, if this fails, the bios code will hle it */
|
||||
boot_load_rom(boot);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,10 @@ static void sh4_reg_write(struct sh4 *sh4, uint32_t addr, uint32_t data,
|
|||
static void sh4_invalid_instr(void *data) {
|
||||
struct sh4 *sh4 = data;
|
||||
|
||||
if (bios_invalid_instr(sh4->bios)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sh4_dbg_invalid_instr(sh4)) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue