initial hle bios

This commit is contained in:
Anthony Pesch 2017-06-14 23:29:04 -04:00
parent e069ca021e
commit fae06b247c
12 changed files with 1255 additions and 46 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

859
src/guest/bios/syscalls.c Normal file
View File

@ -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;
}

12
src/guest/bios/syscalls.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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:

View File

@ -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

110
src/guest/gdrom/iso.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}