ref: c7663873ef41dabd7beb740615a64fd42effa572
parent: 99be385e3fd8ba853017925aa3c45566be233044
author: allkern <lisandroaalarcon@gmail.com>
date: Thu Dec 14 19:35:14 EST 2023
Project reboot Fixed long-standing bugs Now booting a lot of games Implemented fullscreen mode Implemented vertical mode Improved display area
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -16,8 +16,8 @@
for (int i = 0; i < (size >> 2); i++) {uint32_t sample = psx_spu_get_sample(spu);
- int16_t left = (int16_t)(sample & 0xffff) * 2;
- int16_t right = (int16_t)(sample >> 16) * 2;
+ int16_t left = (int16_t)(sample & 0xffff) * 1.5f;
+ int16_t right = (int16_t)(sample >> 16) * 1.5f;
*(int16_t*)(&buf[(i << 2) + 0]) += left;
*(int16_t*)(&buf[(i << 2) + 2]) += right;
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -21,6 +21,12 @@
return 0;
}
+int screen_get_base_width(psxe_screen_t* screen) {+ int width = psx_get_dmode_width(screen->psx);
+
+ return (width == 256) ? 256 : 320;
+}
+
psxe_screen_t* psxe_screen_create() {return (psxe_screen_t*)malloc(sizeof(psxe_screen_t));
}
@@ -42,9 +48,12 @@
screen->psx = psx;
screen->pad = psx_get_pad(psx);
- SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
+ screen->texture_width = PSX_GPU_FB_WIDTH;
+ screen->texture_height = PSX_GPU_FB_HEIGHT;
+ SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "0");+ SDL_SetRenderDrawColor(screen->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
}
void psxe_screen_reload(psxe_screen_t* screen) {@@ -58,8 +67,13 @@
screen->width = PSX_GPU_FB_WIDTH;
screen->height = PSX_GPU_FB_HEIGHT;
} else {- screen->width = 320;
- screen->height = 240;
+ if (screen->vertical_mode) {+ screen->width = 240;
+ screen->height = screen_get_base_width(screen);
+ } else {+ screen->width = screen_get_base_width(screen);
+ screen->height = 240;
+ }
}
screen->window = SDL_CreateWindow(
@@ -80,7 +94,7 @@
screen->renderer,
screen->format,
SDL_TEXTUREACCESS_STREAMING,
- PSX_GPU_FB_WIDTH, PSX_GPU_FB_HEIGHT
+ screen->texture_width, screen->texture_height
);
// Check for retina displays
@@ -95,6 +109,8 @@
SDL_RenderSetScale(screen->renderer, width_scale, height_scale);
}
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0);
+
screen->open = 1;
}
@@ -107,6 +123,9 @@
psxe_screen_set_scale(screen, screen->saved_scale);
+ screen->texture_width = PSX_GPU_FB_WIDTH;
+ screen->texture_height = PSX_GPU_FB_HEIGHT;
+
psxe_gpu_dmode_event_cb(screen->psx->gpu);
}
@@ -114,8 +133,38 @@
void* display_buf = screen->debug_mode ?
psx_get_vram(screen->psx) : psx_get_display_buffer(screen->psx);
- SDL_UpdateTexture(screen->texture, NULL, display_buf, PSX_GPU_FB_STRIDE);
- SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
+ // Update texture
+ int pitch;
+ uint8_t* ptr = NULL;
+
+ SDL_LockTexture(screen->texture, NULL, &ptr, &pitch);
+
+ for (int y = 0; y < screen->texture_height; y++)
+ memcpy(ptr + (y * pitch), display_buf + (y * PSX_GPU_FB_STRIDE), pitch);
+
+ SDL_UnlockTexture(screen->texture);
+
+ SDL_RenderClear(screen->renderer);
+
+ if (!screen->debug_mode) {+ SDL_Rect dstrect;
+
+ dstrect.x = screen->image_xoff;
+ dstrect.y = screen->image_yoff;
+ dstrect.w = screen->image_width;
+ dstrect.h = screen->image_height;
+
+ SDL_RenderCopyEx(
+ screen->renderer,
+ screen->texture,
+ NULL, &dstrect,
+ screen->vertical_mode ? 270 : 0,
+ NULL, SDL_FLIP_NONE
+ );
+ } else {+ SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
+ }
+
SDL_RenderPresent(screen->renderer);
SDL_Event event;
@@ -137,8 +186,8 @@
case SDLK_F2: {SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(
0,
- screen->width * screen->scale,
- screen->height * screen->scale,
+ screen->width,
+ screen->height,
16,
SDL_PIXELFORMAT_BGR555
);
@@ -154,6 +203,26 @@
SDL_FreeSurface(surface);
} break;
+
+ case SDLK_F3: {+ screen->vertical_mode = !screen->vertical_mode;
+
+ psxe_gpu_dmode_event_cb(screen->psx->gpu);
+ } break;
+
+ case SDLK_F4: {+ screen->fullscreen = !screen->fullscreen;
+
+ SDL_SetWindowFullscreen(
+ screen->window,
+ screen->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0
+ );
+
+ psxe_gpu_dmode_event_cb(screen->psx->gpu);
+
+ SDL_SetRenderDrawColor(screen->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ SDL_RenderClear(screen->renderer);
+ } break;
}
uint16_t mask = screen_get_button(event.key.keysym.sym);
@@ -196,7 +265,7 @@
int texture_height;
screen->format = psx_get_display_format(screen->psx) ?
- SDL_PIXELFORMAT_BGR555 : SDL_PIXELFORMAT_RGB888;
+ SDL_PIXELFORMAT_RGB24 : SDL_PIXELFORMAT_BGR555;
if (screen->debug_mode) {screen->width = PSX_GPU_FB_WIDTH;
@@ -204,32 +273,59 @@
texture_width = PSX_GPU_FB_WIDTH;
texture_height = PSX_GPU_FB_HEIGHT;
} else {- screen->width = 320;
- screen->height = 240;
- texture_width = psx_get_display_width(screen->psx);
- texture_height = psx_get_display_height(screen->psx);
- }
+ if (screen->fullscreen) {+ screen->width = 1920;
+ screen->height = 1080;
- // if (screen->width > 512) {- // screen->scale = 1;
- // } else {- // screen->scale = screen->saved_scale;
- // }
+ if (screen->vertical_mode) {+ screen->image_width = screen->height;
+ screen->image_height = (double)screen->height / psx_get_display_aspect(screen->psx);
+ int off = (screen->image_width - screen->image_height) / 2;
+
+ screen->image_xoff = (screen->width / 2) - (screen->image_width / 2);
+ screen->image_yoff = off;
+ } else {+ screen->image_width = (double)screen->height * psx_get_display_aspect(screen->psx);
+ screen->image_height = screen->height;
+ screen->image_xoff = (screen->width / 2) - (screen->image_width / 2);
+ screen->image_yoff = 0;
+ }
+ } else {+ if (screen->vertical_mode) {+ screen->width = 240 * screen->scale;
+ screen->height = screen_get_base_width(screen) * screen->scale;
+ screen->image_width = screen->height;
+ screen->image_height = screen->width;
+
+ int off = (screen->image_width - screen->image_height) / 2;
+
+ screen->image_xoff = -off;
+ screen->image_yoff = off;
+ } else {+ screen->width = screen_get_base_width(screen) * screen->scale;
+ screen->height = 240 * screen->scale;
+ screen->image_width = screen->width;
+ screen->image_height = screen->height;
+ screen->image_xoff = 0;
+ screen->image_yoff = 0;
+ }
+ }
+
+ screen->texture_width = psx_get_display_width(screen->psx);
+ screen->texture_height = psx_get_display_height(screen->psx);
+ }
+
SDL_DestroyTexture(screen->texture);
screen->texture = SDL_CreateTexture(
screen->renderer,
- SDL_PIXELFORMAT_BGR555,
+ screen->format,
SDL_TEXTUREACCESS_STREAMING,
- texture_width, texture_height
+ screen->texture_width, screen->texture_height
);
- SDL_SetWindowSize(
- screen->window,
- screen->width * screen->scale,
- screen->height * screen->scale
- );
+ SDL_SetWindowSize(screen->window, screen->width, screen->height);
}
void psxe_gpu_vblank_event_cb(psx_gpu_t* gpu) {--- a/frontend/screen.h
+++ b/frontend/screen.h
@@ -18,8 +18,13 @@
unsigned int saved_scale;
unsigned int width, height, scale;
+ unsigned int image_width, image_height;
+ unsigned int image_xoff, image_yoff;
unsigned int format;
+ unsigned int texture_width, texture_height;
+ int fullscreen;
+ int vertical_mode;
int debug_mode;
int open;
} psxe_screen_t;
--- a/psx/cpu.c
+++ b/psx/cpu.c
@@ -700,6 +700,8 @@
}
void psx_cpu_cycle(psx_cpu_t* cpu) {+ cpu->last_cycles = 0;
+
// if ((cpu->pc & 0x3fffffff) == 0x000000a4) {// if (cpu->r[9] == 0x2f)
// goto no_putchar;
@@ -762,6 +764,7 @@
}
cpu->opcode = psx_bus_read32(cpu->bus, cpu->pc);
+ cpu->last_cycles = psx_bus_get_access_cycles(cpu->bus);
cpu->pc = cpu->next_pc;
cpu->next_pc += 4;
@@ -774,7 +777,10 @@
g_psx_cpu_primary_table[OP](cpu);
- cpu->last_cycles = 2 + psx_bus_get_access_cycles(cpu->bus);
+ // Discard instruction access cycles
+ psx_bus_get_access_cycles(cpu->bus);
+
+ cpu->last_cycles = 1;
cpu->total_cycles += cpu->last_cycles;
cpu->r[0] = 0;
@@ -1113,8 +1119,6 @@
uint32_t aligned = psx_bus_read32(cpu->bus, addr & ~0x3);
- cpu->load_v = cpu->r[T];
-
switch (addr & 0x3) {case 0: cpu->load_v = (cpu->load_v & 0x00ffffff) | (aligned << 24); break;
case 1: cpu->load_v = (cpu->load_v & 0x0000ffff) | (aligned << 16); break;
@@ -1174,8 +1178,6 @@
uint32_t addr = cpu->r[S] + IMM16S;
uint32_t aligned = psx_bus_read32(cpu->bus, addr & ~0x3);
-
- cpu->load_v = cpu->r[T];
switch (addr & 0x3) {case 0: cpu->load_v = aligned ; break;
--- a/psx/dev/bios.c
+++ b/psx/dev/bios.c
@@ -14,7 +14,7 @@
bios->io_base = PSX_BIOS_BEGIN;
bios->io_size = PSX_BIOS_SIZE;
- bios->bus_delay = 20;
+ bios->bus_delay = 18;
bios->buf = (uint8_t*)malloc(PSX_BIOS_SIZE);
}
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -24,6 +24,93 @@
#define GETID_RESPONSE_SIZE 8
#define GETID_RESPONSE_END (GETID_RESPONSE_SIZE - 1)
+static const int16_t g_zigzag_table0[] = {+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, -0x0002, 0x000a, -0x0022,
+ 0x0041, -0x0054, 0x0034, 0x0009,
+ -0x010a, 0x0400, -0x0a78, 0x234c,
+ 0x6794, -0x1780, 0x0bcd, -0x0623,
+ 0x0350, -0x016d, 0x006b, 0x000a,
+ -0x0010, 0x0011, -0x0008, 0x0003,
+ -0x0001
+};
+
+static const int16_t g_zigzag_table1[] = {+ 0x0000, 0x0000, 0x0000, -0x0002,
+ 0x0000, 0x0003, -0x0013, 0x003c,
+ -0x004b, 0x00a2, -0x00e3, 0x0132,
+ -0x0043, -0x0267, 0x0c9d, 0x74bb,
+ -0x11b4, 0x09b8, -0x05bf, 0x0372,
+ -0x01a8, 0x00a6, -0x001b, 0x0005,
+ 0x0006, -0x0008, 0x0003, -0x0001,
+ 0x0000
+};
+
+static const int16_t g_zigzag_table2[] = {+ 0x0000, 0x0000, -0x0001, 0x0003,
+ -0x0002, -0x0005, 0x001f, -0x004a,
+ 0x00b3, -0x0192, 0x02b1, -0x039e,
+ 0x04f8, -0x05a6, 0x7939, -0x05a6,
+ 0x04f8, -0x039e, 0x02b1, -0x0192,
+ 0x00b3, -0x004a, 0x001f, -0x0005,
+ -0x0002, 0x0003, -0x0001, 0x0000,
+ 0x0000
+};
+
+static const int16_t g_zigzag_table3[] = {+ 0x0000, -0x0001, 0x0003, -0x0008,
+ 0x0006, 0x0005, -0x001b, 0x00a6,
+ -0x01a8, 0x0372, -0x05bf, 0x09b8,
+ -0x11b4, 0x74bb, 0x0c9d, -0x0267,
+ -0x0043, 0x0132, -0x00e3, 0x00a2,
+ -0x004b, 0x003c, -0x0013, 0x0003,
+ 0x0000, -0x0002, 0x0000, 0x0000,
+ 0x0000
+};
+
+static const int16_t g_zigzag_table4[] = {+ -0x0001, 0x0003, -0x0008, 0x0011,
+ -0x0010, 0x000a, 0x006b, -0x016d,
+ 0x0350, -0x0623, 0x0bcd, -0x1780,
+ 0x6794, 0x234c, -0x0a78, 0x0400,
+ -0x010a, 0x0009, 0x0034, -0x0054,
+ 0x0041, -0x0022, 0x000a, -0x0001,
+ 0x0000, 0x0001, 0x0000, 0x0000,
+ 0x0000
+};
+
+static const int16_t g_zigzag_table5[] = {+ 0x0002, -0x0008, 0x0010, -0x0023,
+ 0x002b, 0x001a, -0x00eb, 0x027b,
+ -0x0548, 0x0afa, -0x16fa, 0x53e0,
+ 0x3c07, -0x1249, 0x080e, -0x0347,
+ 0x015b, -0x0044, -0x0017, 0x0046,
+ -0x0023, 0x0011, -0x0005, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000
+};
+
+static const int16_t g_zigzag_table6[] = {+ -0x0005, 0x0011, -0x0023, 0x0046,
+ -0x0017, -0x0044, 0x015b, -0x0347,
+ 0x080e, -0x1249, 0x3c07, 0x53e0,
+ -0x16fa, 0x0afa, -0x0548, 0x027b,
+ -0x00eb, 0x001a, 0x002b, -0x0023,
+ 0x0010, -0x0008, 0x0002, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000
+};
+
+static const int16_t* g_zigzag_table[] = {+ g_zigzag_table0,
+ g_zigzag_table1,
+ g_zigzag_table2,
+ g_zigzag_table3,
+ g_zigzag_table4,
+ g_zigzag_table5,
+ g_zigzag_table6
+};
+
static const uint8_t g_getid_no_disc[] = {0x08, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
@@ -63,7 +150,9 @@
cdrom->state = CD_STATE_RECV_CMD;
}
void cdrom_cmd_unimplemented(psx_cdrom_t* cdrom) {+ log_set_quiet(0);
log_fatal("Unimplemented CDROM command (%u)", cdrom->command);+ log_set_quiet(1);
exit(1);
}
@@ -91,7 +180,11 @@
case CD_STATE_SEND_RESP1: {SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_MOTOR | (cdrom->disc ? 0 : GETSTAT_TRAYOPEN));
+ RESP_PUSH(
+ GETSTAT_MOTOR |
+ (cdrom->cdda_playing ? GETSTAT_PLAY : 0) |
+ (cdrom->disc ? 0 : GETSTAT_TRAYOPEN)
+ );
if (cdrom->read_ongoing) {cdrom->state = CD_STATE_SEND_RESP2;
@@ -105,6 +198,8 @@
}
}
void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
switch (cdrom->state) { case CD_STATE_RECV_CMD: { if (cdrom->pfifo_index != 3) {@@ -121,15 +216,15 @@
return;
}
- if (!cdrom->read_ongoing) {+ //if (!cdrom->read_ongoing) {cdrom->irq_delay = DELAY_1MS;
cdrom->delayed_command = CDL_SETLOC;
cdrom->state = CD_STATE_SEND_RESP1;
- } else {- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_READN;
- cdrom->state = CD_STATE_SEND_RESP2;
- }
+ // } else {+ // cdrom->irq_delay = DELAY_1MS;
+ // cdrom->delayed_command = CDL_READN;
+ // cdrom->state = CD_STATE_SEND_RESP2;
+ // }
int f = PFIFO_POP;
int s = PFIFO_POP;
@@ -197,6 +292,16 @@
case CD_STATE_RECV_CMD: {int track = 0;
+ if (cdrom->cdda_playing) {+ cdrom->pfifo_index = 0;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_PLAY;
+
+ return;
+ }
+
// Optional track number parameter
if (cdrom->pfifo_index)
track = PFIFO_POP;
@@ -206,7 +311,6 @@
cdrom->delayed_command = CDL_PLAY;
if (track) {-
psx_disc_get_track_addr(cdrom->disc, &cdrom->cdda_msf, track);
msf_to_bcd(&cdrom->cdda_msf);
@@ -281,6 +385,21 @@
msf_from_bcd(&msf);
+ cdrom->xa_msf = msf;
+
+ if (cdrom->mode & MODE_XA_ADPCM) {+ cdrom->xa_playing = 1;
+
+ printf("Play XA-ADPCM encoded song at %02u:%02u:%02x, filter=%u, file=%02x, channel=%02x\n",+ cdrom->xa_msf.m,
+ cdrom->xa_msf.s,
+ cdrom->xa_msf.f,
+ (cdrom->mode & MODE_XA_FILTER) != 0,
+ cdrom->xa_file,
+ cdrom->xa_channel
+ );
+ }
+
int err = psx_disc_seek(cdrom->disc, msf);
if (err) {@@ -469,6 +588,7 @@
case CD_STATE_RECV_CMD: {cdrom->read_ongoing = 0;
cdrom->cdda_playing = 0;
+ cdrom->xa_playing = 0;
cdrom->irq_delay = DELAY_1MS;
cdrom->state = CD_STATE_SEND_RESP1;
@@ -580,7 +700,8 @@
return;
}
- cdrom->pfifo_index = 0;
+ cdrom->xa_file = PFIFO_POP;
+ cdrom->xa_channel = PFIFO_POP;
cdrom->irq_delay = DELAY_1MS;
cdrom->delayed_command = CDL_SETFILTER;
@@ -630,6 +751,30 @@
cdrom->delayed_command = CDL_NONE;
SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_PLAY);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_getparam(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_GETPARAM;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ cdrom->delayed_command = CDL_NONE;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(cdrom->xa_channel);
+ RESP_PUSH(cdrom->xa_file);
+ RESP_PUSH(0x00);
+ RESP_PUSH(cdrom->mode);
RESP_PUSH(GETSTAT_MOTOR);
cdrom->state = CD_STATE_RECV_CMD;
@@ -636,7 +781,30 @@
} break;
}
}
-void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) { log_fatal("getlocl: Unimplemented"); exit(1); }+void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) {+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_GETLOCL;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x01);
+ RESP_PUSH(0xff);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) { switch (cdrom->state) { case CD_STATE_RECV_CMD: {@@ -661,6 +829,33 @@
} break;
}
}
+void cdrom_cmd_setsession(psx_cdrom_t* cdrom) {+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_SETSESSION;
+ cdrom->state = CD_STATE_SEND_RESP1;
+
+ PFIFO_POP;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_READ);
+
+ cdrom->delayed_command = CDL_SETSESSION;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, IFR_INT2);
+ RESP_PUSH(GETSTAT_READ);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
void cdrom_cmd_gettn(psx_cdrom_t* cdrom) {cdrom->delayed_command = CDL_NONE;
@@ -890,10 +1085,10 @@
cdrom->delayed_command = CDL_NONE;
SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(0x01);
- RESP_PUSH(0x95);
- RESP_PUSH(0x13);
- RESP_PUSH(0x03);
+ RESP_PUSH(0xc0);
+ RESP_PUSH(0x19);
+ RESP_PUSH(0x09);
+ RESP_PUSH(0x94);
cdrom->state = CD_STATE_RECV_CMD;
} break;
@@ -988,6 +1183,21 @@
msf_from_bcd(&msf);
+ cdrom->xa_msf = msf;
+
+ if (cdrom->mode & MODE_XA_ADPCM) {+ cdrom->xa_playing = 1;
+
+ printf("Play XA-ADPCM encoded song at %02u:%02u:%02x, filter=%u, file=%02x, channel=%02x\n",+ cdrom->xa_msf.m,
+ cdrom->xa_msf.s,
+ cdrom->xa_msf.f,
+ (cdrom->mode & MODE_XA_FILTER) != 0,
+ cdrom->xa_file,
+ cdrom->xa_channel
+ );
+ }
+
int err = psx_disc_seek(cdrom->disc, msf);
if (err) {@@ -1001,35 +1211,7 @@
return;
}
-
- psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
- msf_t sector_msf;
-
- sector_msf.m = cdrom->dfifo[0x0c];
- sector_msf.s = cdrom->dfifo[0x0d];
- sector_msf.f = cdrom->dfifo[0x0e];
-
- int correct_msf = (cdrom->seek_mm == sector_msf.m) &&
- (cdrom->seek_ss == sector_msf.s) &&
- (cdrom->seek_ff == sector_msf.f);
- // Most probably audio sector:
- // Purposefully constructed audio data could
- // circumvent this detection code, but it will work
- // for most intents and purposes
- if (!correct_msf) {- log_fatal("CdlReadS: Audio read");-
- cdrom->irq_delay = DELAY_1MS * 600;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_SEEK;
- cdrom->error_flags = GETSTAT_SEEKERROR;
-
- return;
- }
-
int double_speed = cdrom->mode & MODE_SPEED;
cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
@@ -1056,19 +1238,6 @@
psx_disc_seek(cdrom->disc, msf);
psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
- if (cdrom->mode & MODE_XA_ADPCM) {- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
-
- return;
- }
-
- if (cdrom->dfifo[0x12] & 0x20) {- log_fatal("Unimplemented XA Form2 Sector");-
- // exit(1);
- }
-
cdrom->seek_ff++;
if ((cdrom->seek_ff & 0xF) == 10) { cdrom->seek_ff += 0x10; cdrom->seek_ff &= 0xF0; }@@ -1109,10 +1278,10 @@
"CdlUnmute",
"CdlSetfilter",
"CdlSetmode",
- "CdlUnimplemented",
+ "CdlGetparam",
"CdlGetlocl",
"CdlGetlocp",
- "CdlUnimplemented",
+ "CdlSetsession",
"CdlGettn",
"CdlGettd",
"CdlSeekl",
@@ -1144,10 +1313,10 @@
cdrom_cmd_unmute,
cdrom_cmd_setfilter,
cdrom_cmd_setmode,
- cdrom_cmd_unimplemented,
+ cdrom_cmd_getparam,
cdrom_cmd_getlocl,
cdrom_cmd_getlocp,
- cdrom_cmd_unimplemented,
+ cdrom_cmd_setsession,
cdrom_cmd_gettn,
cdrom_cmd_gettd,
cdrom_cmd_seekl,
@@ -1178,7 +1347,7 @@
if (cdrom->rfifo_index == 0)
SET_BITS(status, STAT_RSLRRDY_MASK, 0);
-
+
return data;
}
@@ -1186,19 +1355,32 @@
if (!cdrom->dfifo_full)
return 0;
+ // int data_sector_size = cdrom->dfifo[0x12] & MODE_SECTOR_SIZE;
int sector_size_bit = cdrom->mode & MODE_SECTOR_SIZE;
+ uint8_t m = cdrom->dfifo[0x0c];
+ uint8_t s = cdrom->dfifo[0x0d];
+ uint8_t f = cdrom->dfifo[0x0e];
+
uint32_t sector_size = sector_size_bit ? 0x924 : 0x800;
uint32_t offset = sector_size_bit ? 12 : 24;
+ // if ((m == 0) && (s == 2) && (f == 0x18)) {+ // if ((cdrom->dfifo_index & 0xf) == 0)
+ // printf("\n");+
+ // printf("%02x ", cdrom->dfifo[offset + (cdrom->dfifo_index)]);+ // }
+
if (cdrom->dfifo_index < sector_size) {SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
- return cdrom->dfifo[offset + (cdrom->dfifo_index++)];
- } else {- SET_BITS(status, STAT_DRQSTS_MASK, 0);
+ uint8_t data = cdrom->dfifo[offset + (cdrom->dfifo_index++)];
- cdrom->dfifo_full = 0;
+ if (cdrom->dfifo_index >= sector_size)
+ SET_BITS(status, STAT_DRQSTS_MASK, 0);
+
+ return data;
}
return 0x00;
@@ -1217,19 +1399,19 @@
}
void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t value) {- log_set_quiet(0);
- log_fatal("%s(%02x) %u params=[%02x, %02x, %02x, %02x, %02x, %02x]",- g_psx_cdrom_command_names[value],
- value,
- cdrom->pfifo_index,
- cdrom->pfifo[0],
- cdrom->pfifo[1],
- cdrom->pfifo[2],
- cdrom->pfifo[3],
- cdrom->pfifo[4],
- cdrom->pfifo[5]
- );
- log_set_quiet(1);
+ // log_set_quiet(0);
+ // log_fatal("%s(%02x) %u params=[%02x, %02x, %02x, %02x, %02x, %02x]",+ // g_psx_cdrom_command_names[value],
+ // value,
+ // cdrom->pfifo_index,
+ // cdrom->pfifo[0],
+ // cdrom->pfifo[1],
+ // cdrom->pfifo[2],
+ // cdrom->pfifo[3],
+ // cdrom->pfifo[4],
+ // cdrom->pfifo[5]
+ // );
+ // log_set_quiet(1);
cdrom->command = value;
cdrom->state = CD_STATE_RECV_CMD;
@@ -1250,12 +1432,10 @@
SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
cdrom->dfifo_full = 1;
- cdrom->dfifo_index = 0;
} else {SET_BITS(status, STAT_DRQSTS_MASK, 0);
cdrom->dfifo_full = 0;
- cdrom->dfifo_index = 0;
}
}
@@ -1344,6 +1524,13 @@
return (psx_cdrom_t*)malloc(sizeof(psx_cdrom_t));
}
+#define XA_STEREO_SAMPLES 2016
+#define XA_MONO_SAMPLES 4032
+#define XA_DECODED_SAMPLES 37632
+#define XA_RINGBUF_SIZE 32
+#define XA_STEREO_RESAMPLE_SIZE 2352
+#define XA_MONO_RESAMPLE_SIZE 4704
+
void psx_cdrom_init(psx_cdrom_t* cdrom, psx_ic_t* ic) {memset(cdrom, 0, sizeof(psx_cdrom_t));
@@ -1354,17 +1541,44 @@
cdrom->status = STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK | STAT_RSLRRDY_MASK;
cdrom->dfifo = malloc(CD_SECTOR_SIZE);
cdrom->cdda_buf = malloc(CD_SECTOR_SIZE);
+
+ // Initialize XA state
+ cdrom->xa_sector_buf = malloc(CD_SECTOR_SIZE);
+ cdrom->xa_left_buf = malloc(XA_STEREO_SAMPLES * sizeof(int16_t));
+ cdrom->xa_right_buf = malloc(XA_STEREO_SAMPLES * sizeof(int16_t));
+ cdrom->xa_mono_buf = malloc(XA_MONO_SAMPLES * sizeof(int16_t));
+ cdrom->xa_decoded_buf = malloc(XA_DECODED_SAMPLES * sizeof(int16_t));
+ cdrom->xa_left_ring_buf = malloc(XA_RINGBUF_SIZE * sizeof(int16_t));
+ cdrom->xa_right_ring_buf = malloc(XA_RINGBUF_SIZE * sizeof(int16_t));
+ cdrom->xa_stereo_resample_buf = malloc(XA_STEREO_RESAMPLE_SIZE * sizeof(int16_t));
+ cdrom->xa_mono_resample_buf = malloc(XA_MONO_RESAMPLE_SIZE * sizeof(int16_t));
+ cdrom->xa_step = 6;
+
+ memset(cdrom->xa_left_buf, 0, XA_STEREO_SAMPLES * sizeof(int16_t));
+ memset(cdrom->xa_right_buf, 0, XA_STEREO_SAMPLES * sizeof(int16_t));
+ memset(cdrom->xa_mono_buf, 0, XA_MONO_SAMPLES * sizeof(int16_t));
+ memset(cdrom->xa_decoded_buf, 0, XA_DECODED_SAMPLES * sizeof(int16_t));
+ memset(cdrom->xa_left_ring_buf, 0, XA_RINGBUF_SIZE * sizeof(int16_t));
+ memset(cdrom->xa_right_ring_buf, 0, XA_RINGBUF_SIZE * sizeof(int16_t));
+ memset(cdrom->xa_stereo_resample_buf, 0, XA_STEREO_RESAMPLE_SIZE * sizeof(int16_t));
+ memset(cdrom->xa_mono_resample_buf, 0, XA_MONO_RESAMPLE_SIZE * sizeof(int16_t));
}
uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t offset) {+ log_set_quiet(0);
log_fatal("Unhandled 32-bit CDROM read at offset %08x", offset);+ // exit(1);
+
return 0x0;
}
uint16_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t offset) {+ log_set_quiet(0);
log_fatal("Unhandled 16-bit CDROM read at offset %08x", offset);+ // exit(1);
+
return 0x0;
}
@@ -1371,21 +1585,34 @@
uint8_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t offset) {uint8_t data = g_psx_cdrom_read_table[(STAT_INDEX << 2) | offset](cdrom);
+ if (((STAT_INDEX << 2) | offset) == 2)
+ return data;
+
+ // log_set_quiet(0);
// log_fatal("%s (read %02x)", g_psx_cdrom_read_names_table[(STAT_INDEX << 2) | offset], data);+ // log_set_quiet(1);
return data;
}
void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t offset, uint32_t value) {+ log_set_quiet(0);
log_fatal("Unhandled 32-bit CDROM write at offset %08x (%08x)", offset, value);+
+ // exit(1);
}
void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t offset, uint16_t value) {+ log_set_quiet(0);
log_fatal("Unhandled 16-bit CDROM write at offset %08x (%04x)", offset, value);+
+ // exit(1);
}
void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t offset, uint8_t value) {+ // log_set_quiet(0);
// log_fatal("%s (write %02x)", g_psx_cdrom_write_names_table[(STAT_INDEX << 2) | offset], value);+ // log_set_quiet(1);
g_psx_cdrom_write_table[(STAT_INDEX << 2) | offset](cdrom, value);
}
@@ -1405,13 +1632,13 @@
// g_psx_cdrom_command_names[cdrom->delayed_command],
// cdrom->delayed_command
// );
- // log_set_quiet(1);
+ log_set_quiet(1);
g_psx_cdrom_command_table[cdrom->delayed_command](cdrom);
}
// log_set_quiet(0);
// log_fatal("CDROM INT%u", cdrom->ifr & 0x7);- // log_set_quiet(1);
+ log_set_quiet(1);
}
}
}
@@ -1529,7 +1756,242 @@
}
}
+static const int g_spu_pos_adpcm_table[] = {+ 0, +60, +115, +98, +122
+};
+
+static const int g_spu_neg_adpcm_table[] = {+ 0, 0, -52, -55, -60
+};
+
+void cdrom_decode_xa_block(psx_cdrom_t* cdrom, int idx, int blk, int nib, int16_t* buf, int16_t* h) {+ int shift = 12 - (cdrom->xa_sector_buf[idx + 4 + blk * 2 + nib] & 0x0F);
+ int filter = (cdrom->xa_sector_buf[idx + 4 + blk * 2 + nib] & 0x30) >> 4;
+
+ int32_t f0 = g_spu_pos_adpcm_table[filter];
+ int32_t f1 = g_spu_neg_adpcm_table[filter];
+
+ for (int j = 0; j < 28; j++) {+ uint16_t n = (cdrom->xa_sector_buf[idx + 16 + blk + j * 4] >> (nib * 4)) & 0x0f;
+
+ int16_t t = (int16_t)(n << 12) >> 12;
+ int16_t s = (t << shift) + (((h[0] * f0) + (h[1] * f1) + 32) / 64);
+
+ s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
+
+ h[1] = h[0];
+ h[0] = s;
+
+ buf[j] = s;
+ }
+}
+
+int16_t cdrom_xa_interpolate(psx_cdrom_t* cdrom, int table, int channel) {+ int16_t* ringbuf = channel ? cdrom->xa_right_ring_buf : cdrom->xa_left_ring_buf;
+
+ int sum = 0;
+
+ for (int i = 0; i < 29; i++) {+ int16_t* zigzag = g_zigzag_table[table];
+
+ sum += (ringbuf[(cdrom->xa_ringbuf_pos - i) & 0x1f] * zigzag[i]) / 0x8000;
+ }
+
+ return (sum > INT16_MAX) ? INT16_MAX : ((sum < INT16_MIN) ? INT16_MIN : sum);
+}
+
+int16_t* cdrom_resample_xa_sector(psx_cdrom_t* cdrom, int16_t* buf, int stereo, int f18khz, int channel) {+ int16_t* ringbuf = channel ? cdrom->xa_right_ring_buf : cdrom->xa_left_ring_buf;
+ int16_t* resample_buf = stereo ? cdrom->xa_stereo_resample_buf : cdrom->xa_mono_resample_buf;
+
+ int sample_count = stereo ? XA_STEREO_SAMPLES : XA_MONO_SAMPLES;
+
+ for (int i = 0; i < sample_count; i++) {+ ringbuf[cdrom->xa_ringbuf_pos++ & 0x1f] = buf[i];
+
+ cdrom->xa_step--;
+
+ if (!cdrom->xa_step) {+ cdrom->xa_step = 6;
+
+ for (int table = 0; table < 7; table++) {+ int16_t sample = cdrom_xa_interpolate(cdrom, table, channel);
+
+ *resample_buf++ = sample;
+
+ if (f18khz)
+ *resample_buf++ = sample;
+ }
+ }
+ }
+
+ return resample_buf;
+}
+
+void cdrom_decode_xa_sector(psx_cdrom_t* cdrom, void* buf) {+ int16_t* ptr = (int16_t*)buf;
+
+ cdrom->xa_coding = cdrom->xa_sector_buf[0x13];
+
+ int src = 24;
+
+ int16_t left[28];
+ int16_t right[28];
+ int16_t left_h[2] = { 0, 0 };+ int16_t right_h[2] = { 0, 0 };+
+ int stereo = (cdrom->xa_coding & 1) == 1;
+ int f18khz = ((cdrom->xa_coding >> 2) & 1) == 1;
+
+ int16_t* left_ptr = cdrom->xa_left_buf;
+ int16_t* right_ptr = cdrom->xa_right_buf;
+ int16_t* mono_ptr = cdrom->xa_mono_buf;
+
+ for (int i = 0; i < 12; i++) {+ for (int blk = 0; blk < 4; blk++) {+ if (stereo) {+ cdrom_decode_xa_block(cdrom, src, blk, 0, left, left_h);
+ cdrom_decode_xa_block(cdrom, src, blk, 1, right, right_h);
+
+ for (int i = 0; i < 28; i++) {+ *left_ptr++ = left[i];
+ *right_ptr++ = right[i];
+ }
+ } else {+ cdrom_decode_xa_block(cdrom, src, blk, 0, left, left_h);
+
+ for (int i = 0; i < 28; i++)
+ *mono_ptr++ = left[i];
+
+ cdrom_decode_xa_block(cdrom, src, blk, 1, left, left_h);
+
+ for (int i = 0; i < 28; i++)
+ *mono_ptr++ = left[i];
+ }
+ }
+
+ src += 128;
+ }
+
+ if (stereo) {+ for (int i = 0; i < XA_STEREO_SAMPLES; i++) {+ *ptr++ = cdrom->xa_left_buf[i];
+ *ptr++ = cdrom->xa_right_buf[i];
+ }
+ } else {+ for (int i = 0; i < XA_MONO_SAMPLES; i++) {+ *ptr++ = cdrom->xa_mono_buf[i];
+ *ptr++ = cdrom->xa_mono_buf[i];
+ }
+ }
+
+ // if (stereo) {+ // int16_t* resample_buf;
+
+ // resample_buf = cdrom_resample_xa_sector(cdrom, cdrom->xa_left_buf, 1, f18khz, 0);
+
+ // for (int i = 0, j = 0; i < XA_STEREO_RESAMPLE_SIZE; i++) {+ // int16_t sample = resample_buf[i];
+
+ // cdrom->xa_decoded_buf[j] = sample;
+
+ // j += 2;
+ // }
+
+ // resample_buf = cdrom_resample_xa_sector(cdrom, cdrom->xa_right_buf, 1, f18khz, 1);
+
+ // for (int i = 0, j = 1; i < XA_STEREO_RESAMPLE_SIZE; i++) {+ // int16_t sample = resample_buf[i];
+
+ // cdrom->xa_decoded_buf[j] = sample;
+
+ // j += 2;
+ // }
+
+ // cdrom->xa_remaining_samples = XA_STEREO_RESAMPLE_SIZE;
+ // } else {+ // int16_t* resample_buf;
+
+ // resample_buf = cdrom_resample_xa_sector(cdrom, cdrom->xa_mono_buf, 0, f18khz, 0);
+
+ // for (int i = 0, j = 0; i < XA_MONO_RESAMPLE_SIZE; i++) {+ // int16_t sample = resample_buf[i];
+
+ // cdrom->xa_decoded_buf[j++] = sample;
+ // cdrom->xa_decoded_buf[j++] = sample;
+ // }
+
+ // cdrom->xa_remaining_samples = XA_MONO_RESAMPLE_SIZE;
+ // }
+}
+
+void cdrom_fetch_xa_sector(psx_cdrom_t* cdrom) {+ while (true) {+ psx_disc_seek(cdrom->disc, cdrom->xa_msf);
+ psx_disc_read_sector(cdrom->disc, cdrom->xa_sector_buf);
+
+ msf_add_f(&cdrom->xa_msf, 1);
+
+ // Check RT and Audio bit
+ if ((cdrom->xa_sector_buf[0x12] & 0x44) != 0x44)
+ continue;
+
+ // If we get here it means this is a real-time audio sector.
+ // If the XA filter is disabled, we're done
+ if (!(cdrom->mode & MODE_XA_FILTER))
+ return;
+
+ // Else check XA file/channel
+ int file_eq = cdrom->xa_sector_buf[0x10] == cdrom->xa_file;
+ int channel_eq = cdrom->xa_sector_buf[0x11] == cdrom->xa_channel;
+
+ // If they are equal to our filter values, we're done
+ // else keep searching
+ if (file_eq && channel_eq)
+ return;
+ }
+}
+
void psx_cdrom_get_cdda_samples(psx_cdrom_t* cdrom, void* buf, int size, psx_spu_t* spu) {+ memset(buf, 0, size);
+
+ if (!cdrom->disc)
+ return;
+
+ if (cdrom->xa_playing) {+ return;
+
+ if (!cdrom->xa_remaining_samples) {+ cdrom_fetch_xa_sector(cdrom);
+
+ cdrom->xa_remaining_samples = 4;
+ }
+
+ cdrom_decode_xa_sector(cdrom, buf);
+
+ --cdrom->xa_remaining_samples;
+
+ // int16_t* ptr = (int16_t*)buf;
+
+ // memset(buf, 0, size);
+
+ // if (!cdrom->xa_remaining_samples) {+ // cdrom->xa_sample_idx = 0;
+
+ // cdrom_fetch_xa_sector(cdrom);
+ // cdrom_decode_xa_sector(cdrom, buf);
+ // }
+
+ // // for (int i = 0; i < (size >> 2); i++) {+ // // *ptr++ = cdrom->xa_decoded_buf[cdrom->xa_sample_idx++];
+ // // *ptr++ = cdrom->xa_decoded_buf[cdrom->xa_sample_idx++];
+ // // }
+
+ // cdrom->xa_remaining_samples -= size >> 2;
+
+ return;
+ }
+
if (!cdrom->cdda_playing) {memset(buf, 0, size);
@@ -1536,9 +1998,6 @@
return;
}
- if (!cdrom->disc)
- return;
-
// Convert seek to I
msf_t msf = cdrom->cdda_msf;
@@ -1545,9 +2004,8 @@
msf_from_bcd(&msf);
// Seek to that address and read sector
- if (psx_disc_seek(cdrom->disc, msf)) {+ if (psx_disc_seek(cdrom->disc, msf))
cdrom->cdda_playing = 0;
- }
psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
@@ -1583,7 +2041,7 @@
current.m = 0;
msf_adjust(¤t);
- msf_to_bcd(¤t);
+ //msf_to_bcd(¤t);
RESP_PUSH(0);
RESP_PUSH(0);
--- a/psx/dev/cdrom.h
+++ b/psx/dev/cdrom.h
@@ -205,6 +205,26 @@
int cdda_sectors_played;
int cdda_track;
+ // XA-ADPCM
+ uint8_t* xa_sector_buf;
+ msf_t xa_msf;
+ int xa_playing;
+ uint8_t xa_file;
+ uint8_t xa_channel;
+ uint8_t xa_coding;
+ int16_t* xa_left_buf;
+ int16_t* xa_right_buf;
+ int16_t* xa_mono_buf;
+ int16_t* xa_decoded_buf;
+ int16_t* xa_left_ring_buf;
+ int16_t* xa_right_ring_buf;
+ int16_t* xa_stereo_resample_buf;
+ int16_t* xa_mono_resample_buf;
+ uint32_t xa_sample_idx;
+ uint32_t xa_remaining_samples;
+ uint32_t xa_step;
+ uint32_t xa_ringbuf_pos;
+
const char* path;
psx_disc_t* disc;
@@ -283,8 +303,10 @@
void cdrom_cmd_unmute(psx_cdrom_t*);
void cdrom_cmd_setfilter(psx_cdrom_t*);
void cdrom_cmd_setmode(psx_cdrom_t*);
+void cdrom_cmd_getparam(psx_cdrom_t*);
void cdrom_cmd_getlocl(psx_cdrom_t*);
void cdrom_cmd_getlocp(psx_cdrom_t*);
+void cdrom_cmd_setsession(psx_cdrom_t*);
void cdrom_cmd_gettn(psx_cdrom_t*);
void cdrom_cmd_gettd(psx_cdrom_t*);
void cdrom_cmd_seekl(psx_cdrom_t*);
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -5,6 +5,7 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
+#include <ctype.h>
psx_dma_t* psx_dma_create() {return (psx_dma_t*)malloc(sizeof(psx_dma_t));
@@ -115,18 +116,15 @@
switch (offset) { case 0x70: log_error("DMA control write %08x", value); dma->dpcr = value; break; case 0x74: {- // IRQ signal is read-only
- value &= ~DICR_IRQSI;
-
- // Reset flags
- dma->dicr &= ~(value & DICR_FLAGS);
-
- // Write other fields
+ uint32_t ack = value & DICR_FLAGS;
uint32_t flags = dma->dicr & DICR_FLAGS;
- dma->dicr &= ~DICR_FLAGS;
- dma->dicr |= value & (~DICR_FLAGS);
+ flags &= (~ack);
+ flags &= DICR_FLAGS;
+
+ dma->dicr &= 0x80000000;
dma->dicr |= flags;
+ dma->dicr |= value & 0xffffff;
} break;
default: {@@ -199,7 +197,7 @@
dma->mdec_out_irq_delay = size;
- dma->mdec_out.chcr &= ~(CHCR_BUSY_MASK | CHCR_TRIG_MASK);
+ dma->mdec_out.chcr = 0;
dma->mdec_out.bcr = 0;
}
@@ -223,7 +221,8 @@
addr = hdr & 0xffffff;
- if (addr == 0xffffff) break;
+ if (addr == 0xffffff)
+ break;
hdr = psx_bus_read32(dma->bus, addr);
size = hdr >> 24;
@@ -293,7 +292,8 @@
void psx_dma_do_cdrom(psx_dma_t* dma) {if (!CHCR_BUSY(cdrom))
return;
-
+
+ // log_set_quiet(0);
// log_fatal("CDROM DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x",// dma->cdrom.madr,
// CHCR_TDIR(cdrom) ? "to device" : "to RAM",
@@ -308,6 +308,7 @@
// (dma->dicr >> 23) & 1,
// (dma->dicr >> 24) & 0x7f
// );
+ // log_set_quiet(1);
uint32_t size = BCR_SIZE(cdrom);
@@ -319,6 +320,8 @@
dma->cdrom_irq_delay = size * 24;
+ uint32_t base_addr = dma->cdrom.madr;
+
if (!CHCR_TDIR(cdrom)) { for (int i = 0; i < size; i++) {uint32_t data = 0;
@@ -335,10 +338,27 @@
} else { log_fatal("Invalid CDROM DMA transfer direction");}
+
+ // size *= 4;
+
+ // for (int i = 0; i < size; i += 16) {+ // for (int k = 0; k < 16; k++) {+ // printf("%02x ", psx_bus_read8(dma->bus, base_addr + i + k));+ // }
+
+ // printf("| ");+
+ // for (int k = 0; k < 16; k++) {+ // char c = psx_bus_read8(dma->bus, base_addr + i + k);
+
+ // printf("%c ", isgraph(c) ? c : '.');+ // }
+
+ // printf("\n");+ // }
// Clear BCR and CHCR trigger and busy bits
dma->cdrom.chcr = 0;
- //dma->otc.chcr &= ~(CHCR_BUSY_MASK | CHCR_TRIG_MASK);
dma->cdrom.bcr = 0;
}
@@ -361,7 +381,7 @@
// (dma->dicr >> 23) & 1,
// (dma->dicr >> 24) & 0x7f
// );
- // log_set_quiet(1);
+ log_set_quiet(1);
uint32_t size = BCR_SIZE(spu);
uint32_t blocks = BCR_BCNT(spu);
@@ -424,7 +444,8 @@
uint32_t size = BCR_SIZE(otc);
- if (!size) size = 0x10000;
+ if (!size)
+ size = 0x10000;
for (int i = size; i > 0; i--) {uint32_t addr = (i != 1) ? (dma->otc.madr - 4) : 0xffffff;
@@ -444,7 +465,7 @@
void psx_dma_update(psx_dma_t* dma, int cyc) { if (dma->cdrom_irq_delay) {- dma->cdrom_irq_delay -= cyc;
+ dma->cdrom_irq_delay = 0;
if ((dma->dicr & DICR_DMA3EN) && !dma->cdrom_irq_delay)
dma->dicr |= DICR_DMA3FL;
@@ -459,7 +480,7 @@
}
if (dma->gpu_irq_delay) {- dma->gpu_irq_delay -= cyc;
+ dma->gpu_irq_delay = 0;
if (!dma->gpu_irq_delay)
if (dma->dicr & DICR_DMA2EN)
@@ -467,7 +488,7 @@
}
if (dma->otc_irq_delay) {- dma->otc_irq_delay -= cyc;
+ dma->otc_irq_delay = 0;
if (!dma->otc_irq_delay)
if (dma->dicr & DICR_DMA6EN)
@@ -475,7 +496,7 @@
}
if (dma->mdec_in_irq_delay) {- dma->mdec_in_irq_delay -= cyc;
+ dma->mdec_in_irq_delay = 0;
if (!dma->mdec_in_irq_delay)
if (dma->dicr & DICR_DMA0EN)
@@ -483,7 +504,7 @@
}
if (dma->mdec_out_irq_delay) {- dma->mdec_out_irq_delay -= cyc;
+ dma->mdec_out_irq_delay = 0;
if (!dma->mdec_out_irq_delay)
if (dma->dicr & DICR_DMA1EN)
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -26,18 +26,16 @@
// #define BGR555(c) gpu_to_bgr555(c)
-#define VRAM(x, y) gpu->vram[(x) + ((y) * 1024)]
-
int min3(int a, int b, int c) {- int m = a < b ? a : b;
+ int m = a <= b ? a : b;
- return m < c ? m : c;
+ return m <= c ? m : c;
}
int max3(int a, int b, int c) {- int m = a > b ? a : b;
+ int m = a >= b ? a : b;
- return m > c ? m : c;
+ return m >= c ? m : c;
}
psx_gpu_t* psx_gpu_create() {@@ -53,6 +51,9 @@
gpu->vram = (uint16_t*)malloc(PSX_GPU_VRAM_SIZE);
gpu->state = GPU_STATE_RECV_CMD;
+ // Default window size, this is not normally needed
+ gpu->display_mode = 1;
+
gpu->ic = ic;
}
@@ -104,7 +105,7 @@
return data;
} break;
- case 0x04: return gpu->gpustat | 0x1e000000;
+ case 0x04: return gpu->gpustat | 0x1c000000;
}
log_warn("Unhandled 32-bit GPU read at offset %08x", offset);@@ -125,11 +126,11 @@
}
int min(int x0, int x1) {- return (x0 < x1) ? x0 : x1;
+ return (x0 <= x1) ? x0 : x1;
}
int max(int x0, int x1) {- return (x0 > x1) ? x0 : x1;
+ return (x0 >= x1) ? x0 : x1;
}
#define EDGE(a, b, c) ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x))
@@ -143,23 +144,25 @@
switch (depth) {// 4-bit
case 0: {- uint16_t texel = VRAM(tpx + (tx >> 2), tpy + ty);
- uint16_t index = (texel >> ((tx & 0x3) << 2)) & 0xf;
+ uint16_t texel = gpu->vram[(tpx + (tx >> 2)) + ((tpy + ty) * 1024)];
- return VRAM(clutx + index, cluty);
+ int index = (texel >> ((tx & 0x3) << 2)) & 0xf;
+
+ return gpu->vram[(clutx + index) + (cluty * 1024)];
} break;
// 8-bit
case 1: {- uint16_t texel = VRAM(tpx + (tx >> 1), tpy + ty);
- uint16_t index = (texel >> ((tx & 0x1) << 3)) & 0xff;
+ uint16_t texel = gpu->vram[(tpx + (tx >> 1)) + ((tpy + ty) * 1024)];
- return VRAM(clutx + index, cluty);
+ int index = (texel >> ((tx & 0x1) << 3)) & 0xff;
+
+ return gpu->vram[(clutx + index) + (cluty * 1024)];
} break;
// 15-bit
default: {- return VRAM(tpx + tx, tpy + ty);
+ return gpu->vram[(tpx + tx) + ((tpy + ty) * 1024)];
} break;
}
}
@@ -199,11 +202,17 @@
c.x += gpu->off_x;
c.y += gpu->off_y;
- int xmin = min3(a.x, b.x, c.x);
- int ymin = min3(a.y, b.y, c.y);
- int xmax = max3(a.x, b.x, c.x);
- int ymax = max3(a.y, b.y, c.y);
+ int xmin = max(min3(a.x, b.x, c.x), gpu->draw_x1);
+ int ymin = max(min3(a.y, b.y, c.y), gpu->draw_y1);
+ int xmax = min(max3(a.x, b.x, c.x), gpu->draw_x2);
+ int ymax = min(max3(a.y, b.y, c.y), gpu->draw_y2);
+ // Hack
+ if (!(data.attrib & PA_TEXTURED)) {+ ++xmax;
+ ++ymax;
+ }
+
float area = EDGE(a, b, c);
for (int y = ymin; y < ymax; y++) {@@ -217,12 +226,6 @@
if ((z0 < 0) || (z1 < 0) || (z2 < 0))
continue;
-
- int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
- (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
-
- if (!bc)
- continue;
uint16_t color = 0;
uint32_t mod = 0;
@@ -298,12 +301,13 @@
int cb = ((color >> 10) & 0x1f) << 3;
if (transp) {- uint16_t back = VRAM(x, y);
+ uint16_t back = gpu->vram[x + (y * 1024)];
int br = ((back >> 0 ) & 0x1f) << 3;
int bg = ((back >> 5 ) & 0x1f) << 3;
int bb = ((back >> 10) & 0x1f) << 3;
+ // Do we use transp or gpustat here?
switch (transp_mode) { case 0: {cr = (0.5f * br) + (0.5f * cr);
@@ -336,7 +340,7 @@
color = BGR555(rgb);
}
- VRAM(x, y) = color;
+ gpu->vram[x + (y * 1024)] = color;
}
}
}
@@ -418,7 +422,7 @@
int cb = ((color >> 10) & 0x1f) << 3;
if (transp) {- uint16_t back = VRAM(x, y);
+ uint16_t back = gpu->vram[x + (y * 1024)];
int br = ((back >> 0 ) & 0x1f) << 3;
int bg = ((back >> 5 ) & 0x1f) << 3;
@@ -456,7 +460,7 @@
color = BGR555(rgb);
}
- VRAM(x, y) = color;
+ gpu->vram[x + (y * 1024)] = color;
skip:
@@ -481,8 +485,8 @@
int y = y0;
for (int x = x0; x < x1; x++) {- if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
- VRAM(x, y) = color;
+ if ((x < 1024) && (y < 512) && (x >= 0) && (y >= 0))
+ gpu->vram[x + (y * 1024)] = color;
if (d > 0) {y += yi;
@@ -505,9 +509,8 @@
int x = x0;
for (int y = y0; y < y1; y++) {- if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
- VRAM(x, y) = color;
-
+ if ((x < 1024) && (y < 512) && (x >= 0) && (y >= 0))
+ gpu->vram[x + (y * 1024)] = color;
if (d > 0) {x = x + xi;
d += (2 * (dx - dy));
@@ -534,6 +537,11 @@
}
void gpu_render_flat_line(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, uint32_t color) {+ v0.x += gpu->off_x;
+ v0.y += gpu->off_y;
+ v1.x += gpu->off_x;
+ v1.y += gpu->off_y;
+
plotLine(gpu, v0.x, v0.y, v1.x, v1.y, color);
}
@@ -548,9 +556,11 @@
int xmax = min(xmin + w, gpu->draw_x2);
int ymax = min(ymin + h, gpu->draw_y2);
- for (uint32_t y = ymin; y < ymax; y++)
- for (uint32_t x = xmin; x < xmax; x++)
- VRAM(x, y) = color;
+ for (uint32_t y = ymin; y < ymax; y++) {+ for (uint32_t x = xmin; x < xmax; x++) {+ gpu->vram[x + (y * 1024)] = color;
+ }
+ }
}
void gpu_render_textured_rectangle(psx_gpu_t* gpu, vertex_t v, uint32_t w, uint32_t h, uint16_t clutx, uint16_t cluty, uint32_t color) {@@ -814,6 +824,16 @@
int vertices = 3 + quad;
gpu->cmd_args_remaining = (fields_per_vertex * vertices) - shaded;
+
+ // log_set_quiet(0);
+ // log_fatal("Poly: GP0(%02x) shaded=%u, quad=%u, textured=%u, argc=%u, fpv=%u, vertc=%u",+ // gpu->buf[0] >> 24,
+ // shaded, quad, textured,
+ // gpu->cmd_args_remaining,
+ // fields_per_vertex,
+ // vertices
+ // );
+ log_set_quiet(1);
} break;
case GPU_STATE_RECV_ARGS: {@@ -859,6 +879,15 @@
gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly);
gpu_render_triangle(gpu, poly.v[1], poly.v[2], poly.v[3], poly);
} else {+ // log_set_quiet(0);
+ // log_fatal("v0=(%3u, %3u, %06x) v1=(%3u, %3u, %06x) v2=(%3u, %3u, %06x) co=%u vo=%u tco=%u tpo=%u",+ // poly.v[0].x, poly.v[0].y, poly.v[0].c,
+ // poly.v[1].x, poly.v[1].y, poly.v[1].c,
+ // poly.v[2].x, poly.v[2].y, poly.v[2].c,
+ // color_offset, vert_offset,
+ // texc_offset, texp_offset
+ // );
+ log_set_quiet(1);
gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly);
}
@@ -868,34 +897,10 @@
}
}
-void gpu_copy(psx_gpu_t* gpu) {+void gpu_cmd_a0(psx_gpu_t* gpu) { switch (gpu->state) { case GPU_STATE_RECV_CMD: {gpu->state = GPU_STATE_RECV_ARGS;
- gpu->cmd_args_remaining = 3;
- } break;
-
- case GPU_STATE_RECV_ARGS: {- if (!gpu->cmd_args_remaining) {- uint32_t srcx = gpu->buf[1] & 0xffff;
- uint32_t srcy = gpu->buf[1] >> 16;
- uint32_t dstx = gpu->buf[2] & 0xffff;
- uint32_t dsty = gpu->buf[2] >> 16;
- uint32_t xsiz = gpu->buf[3] & 0xffff;
- uint32_t ysiz = gpu->buf[3] >> 16;
-
- for (int y = 0; y < ysiz; y++)
- for (int x = 0; x < xsiz; x++)
- VRAM(dstx + x, dsty + y) = VRAM(srcx + x, srcy + y);
- }
- } break;
- }
-}
-
-void gpu_recv(psx_gpu_t* gpu) {- switch (gpu->state) {- case GPU_STATE_RECV_CMD: {- gpu->state = GPU_STATE_RECV_ARGS;
gpu->cmd_args_remaining = 2;
} break;
@@ -923,7 +928,7 @@
// To-do: This is segfaulting for some reason
// Fix GPU edge cases in general
- VRAM(xpos, ypos) = gpu->recv_data & 0xffff;
+ gpu->vram[xpos + (ypos * 1024)] = gpu->recv_data & 0xffff;
++gpu->xcnt;
@@ -938,7 +943,7 @@
xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
}
- VRAM(xpos, ypos) = gpu->recv_data >> 16;
+ gpu->vram[xpos + (ypos * 1024)] = gpu->recv_data >> 16;
++gpu->xcnt;
@@ -961,34 +966,6 @@
}
}
-void gpu_send(psx_gpu_t* gpu) {- switch (gpu->state) {- case GPU_STATE_RECV_CMD: {- gpu->state = GPU_STATE_RECV_ARGS;
- gpu->cmd_args_remaining = 2;
- } break;
-
- case GPU_STATE_RECV_ARGS: {- if (!gpu->cmd_args_remaining) {- gpu->c0_xcnt = 0;
- gpu->c0_ycnt = 0;
- uint32_t c0_xpos = gpu->buf[1] & 0xffff;
- uint32_t c0_ypos = gpu->buf[1] >> 16;
- gpu->c0_xsiz = gpu->buf[2] & 0xffff;
- gpu->c0_ysiz = gpu->buf[2] >> 16;
- c0_xpos = c0_xpos & 0x3ff;
- c0_ypos = c0_ypos & 0x1ff;
- gpu->c0_xsiz = ((gpu->c0_xsiz - 1) & 0x3ff) + 1;
- gpu->c0_ysiz = ((gpu->c0_ysiz - 1) & 0x1ff) + 1;
- gpu->c0_tsiz = ((gpu->c0_xsiz * gpu->c0_ysiz) + 1) & 0xfffffffe;
- gpu->c0_addr = c0_xpos + (c0_ypos * 1024);
-
- gpu->state = GPU_STATE_RECV_CMD;
- }
- } break;
- }
-}
-
// Monochrome Opaque Quadrilateral
void gpu_cmd_28(psx_gpu_t* gpu) { switch (gpu->state) {@@ -1438,6 +1415,34 @@
}
}
+void gpu_cmd_c0(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->c0_xcnt = 0;
+ gpu->c0_ycnt = 0;
+ uint32_t c0_xpos = gpu->buf[1] & 0xffff;
+ uint32_t c0_ypos = gpu->buf[1] >> 16;
+ gpu->c0_xsiz = gpu->buf[2] & 0xffff;
+ gpu->c0_ysiz = gpu->buf[2] >> 16;
+ c0_xpos = c0_xpos & 0x3ff;
+ c0_ypos = c0_ypos & 0x1ff;
+ gpu->c0_xsiz = ((gpu->c0_xsiz - 1) & 0x3ff) + 1;
+ gpu->c0_ysiz = ((gpu->c0_ysiz - 1) & 0x1ff) + 1;
+ gpu->c0_tsiz = ((gpu->c0_xsiz * gpu->c0_ysiz) + 1) & 0xfffffffe;
+ gpu->c0_addr = c0_xpos + (c0_ypos * 1024);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
void gpu_cmd_02(psx_gpu_t* gpu) { switch (gpu->state) { case GPU_STATE_RECV_CMD: {@@ -1464,8 +1469,8 @@
for (uint32_t y = gpu->v0.y; y < (gpu->v0.y + gpu->ysiz); y++) { for (uint32_t x = gpu->v0.x; x < (gpu->v0.x + gpu->xsiz); x++) {- if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
- VRAM(x, y) = color;
+ if ((x < 1024) && (y < 512) && (x >= 0) && (y >= 0))
+ gpu->vram[x + (y * 1024)] = color;
}
}
@@ -1495,7 +1500,10 @@
for (int y = 0; y < ysiz; y++) { for (int x = 0; x < xsiz; x++) {- if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
+ int dstb = ((dstx + x) < 1024) && ((dsty + y) < 512);
+ int srcb = ((srcx + x) < 1024) && ((srcy + y) < 512);
+
+ if (dstb && srcb)
gpu->vram[(dstx + x) + (dsty + y) * 1024] = gpu->vram[(srcx + x) + (srcy + y) * 1024];
}
}
@@ -1509,53 +1517,56 @@
void psx_gpu_update_cmd(psx_gpu_t* gpu) {int type = (gpu->buf[0] >> 29) & 7;
- switch (type) {- case 1: gpu_poly(gpu); return;
- case 3: gpu_rect(gpu); return;
- case 4: gpu_copy(gpu); return;
- case 5: gpu_recv(gpu); return;
- case 6: gpu_send(gpu); return;
- default: break;
+ if (type == 3) {+ gpu_rect(gpu);
+
+ return;
}
+ if (type == 1) {+ gpu_poly(gpu);
+
+ return;
+ }
+
switch (gpu->buf[0] >> 24) {case 0x00: /* nop */ break;
case 0x01: /* Cache clear */ break;
case 0x02: gpu_cmd_02(gpu); break;
- // case 0x24: gpu_cmd_24(gpu); break;
- // case 0x25: gpu_cmd_24(gpu); break;
- // case 0x26: gpu_cmd_24(gpu); break;
- // case 0x27: gpu_cmd_24(gpu); break;
- // case 0x28: gpu_cmd_28(gpu); break;
- // case 0x2a: gpu_cmd_28(gpu); break;
- // case 0x2c: gpu_cmd_2d(gpu); break;
- // case 0x2d: gpu_cmd_2d(gpu); break;
- // case 0x2e: gpu_cmd_2d(gpu); break;
- // case 0x2f: gpu_cmd_2d(gpu); break;
- // case 0x30: gpu_cmd_30(gpu); break;
- // case 0x32: gpu_cmd_30(gpu); break;
- // case 0x38: gpu_cmd_38(gpu); break;
- // case 0x3c: gpu_cmd_3c(gpu); break;
- // case 0x3e: gpu_cmd_3c(gpu); break;
+ case 0x24: gpu_cmd_24(gpu); break;
+ case 0x25: gpu_cmd_24(gpu); break;
+ case 0x26: gpu_cmd_24(gpu); break;
+ case 0x27: gpu_cmd_24(gpu); break;
+ case 0x28: gpu_cmd_28(gpu); break;
+ case 0x2a: gpu_cmd_28(gpu); break;
+ case 0x2c: gpu_cmd_2d(gpu); break;
+ case 0x2d: gpu_cmd_2d(gpu); break;
+ case 0x2e: gpu_cmd_2d(gpu); break;
+ case 0x2f: gpu_cmd_2d(gpu); break;
+ case 0x30: gpu_cmd_30(gpu); break;
+ case 0x32: gpu_cmd_30(gpu); break;
+ case 0x38: gpu_cmd_38(gpu); break;
+ case 0x3c: gpu_cmd_3c(gpu); break;
+ case 0x3e: gpu_cmd_3c(gpu); break;
case 0x40: gpu_cmd_40(gpu); break;
- // case 0x60: gpu_cmd_60(gpu); break;
- // case 0x62: gpu_cmd_60(gpu); break;
- // case 0x64: gpu_cmd_64(gpu); break;
- // case 0x65: gpu_cmd_64(gpu); break;
- // case 0x66: gpu_cmd_64(gpu); break;
- // case 0x67: gpu_cmd_64(gpu); break;
- // case 0x68: gpu_cmd_68(gpu); break;
- // case 0x74: gpu_cmd_74(gpu); break;
- // case 0x75: gpu_cmd_74(gpu); break;
- // case 0x76: gpu_cmd_74(gpu); break;
- // case 0x77: gpu_cmd_74(gpu); break;
- // case 0x7c: gpu_cmd_7c(gpu); break;
- // case 0x7d: gpu_cmd_7c(gpu); break;
- // case 0x7e: gpu_cmd_7c(gpu); break;
- // case 0x7f: gpu_cmd_7c(gpu); break;
- // case 0x80: gpu_cmd_80(gpu); break;
- // case 0xa0: gpu_cmd_a0(gpu); break;
- // case 0xc0: gpu_cmd_c0(gpu); break;
+ case 0x60: gpu_cmd_60(gpu); break;
+ case 0x62: gpu_cmd_60(gpu); break;
+ case 0x64: gpu_cmd_64(gpu); break;
+ case 0x65: gpu_cmd_64(gpu); break;
+ case 0x66: gpu_cmd_64(gpu); break;
+ case 0x67: gpu_cmd_64(gpu); break;
+ case 0x68: gpu_cmd_68(gpu); break;
+ case 0x74: gpu_cmd_74(gpu); break;
+ case 0x75: gpu_cmd_74(gpu); break;
+ case 0x76: gpu_cmd_74(gpu); break;
+ case 0x77: gpu_cmd_74(gpu); break;
+ case 0x7c: gpu_cmd_7c(gpu); break;
+ case 0x7d: gpu_cmd_7c(gpu); break;
+ case 0x7e: gpu_cmd_7c(gpu); break;
+ case 0x7f: gpu_cmd_7c(gpu); break;
+ case 0x80: gpu_cmd_80(gpu); break;
+ case 0xa0: gpu_cmd_a0(gpu); break;
+ case 0xc0: gpu_cmd_c0(gpu); break;
case 0xe1: {gpu->gpustat &= 0xfffff800;
gpu->gpustat |= gpu->buf[0] & 0x7ff;
@@ -1628,33 +1639,6 @@
uint8_t cmd = value >> 24;
switch (cmd) {- case 0x00: {- gpu->gpustat = 0x14802000;
-
- /*
- GP1(01h) ;clear fifo
- GP1(02h) ;ack irq (0)
- GP1(03h) ;display off (1)
- GP1(04h) ;dma off (0)
- GP1(05h) ;display address (0)
- GP1(06h) ;display x1,x2 (x1=200h, x2=200h+256*10)
- GP1(07h) ;display y1,y2 (y1=010h, y2=010h+240)
- GP1(08h) ;display mode 320x200 NTSC (0)
- GP0(E1h..E6h) ;rendering attributes (0)
- */
-
- gpu->disp_x1 = 0x200;
- gpu->disp_x2 = 0xc00;
- gpu->disp_y1 = 0x010;
- gpu->disp_y2 = 0x100;
- gpu->display_mode = 0;
-
- gpu->disp_x = 0;
- gpu->disp_y = 0;
-
- if (gpu->event_cb_table[GPU_EVENT_DMODE])
- gpu->event_cb_table[GPU_EVENT_DMODE](gpu);
- } break;
case 0x04: {} break;
case 0x05: {@@ -1730,7 +1714,7 @@
} else if (gpu->line == GPU_SCANS_PER_FRAME_NTSC) {if (gpu->event_cb_table[GPU_EVENT_VBLANK_END])
gpu->event_cb_table[GPU_EVENT_VBLANK_END](gpu);
-
+
// psx_ic_irq(gpu->ic, IC_SPU);
gpu->line = 0;
@@ -1761,7 +1745,7 @@
}
void* psx_gpu_get_display_buffer(psx_gpu_t* gpu) {- return gpu->vram + (gpu->disp_x + (gpu->disp_y * 1024));
+ return gpu->vram; // + (gpu->disp_x + (gpu->disp_y * 1024));
}
void psx_gpu_destroy(psx_gpu_t* gpu) {--- a/psx/dev/ic.c
+++ b/psx/dev/ic.c
@@ -28,7 +28,7 @@
case 0x04: return ic->mask;
}
- log_warn("Unhandled 32-bit IC read at offset %08x", offset);+ log_fatal("Unhandled 32-bit IC read at offset %08x", offset);return 0x0;
}
@@ -39,14 +39,18 @@
case 0x04: return ic->mask;
}
- log_warn("Unhandled 16-bit IC read at offset %08x", offset);+ log_fatal("Unhandled 16-bit IC read at offset %08x", offset);+ exit(0);
+
return 0x0;
}
uint8_t psx_ic_read8(psx_ic_t* ic, uint32_t offset) {- log_warn("Unhandled 8-bit IC read at offset %08x", offset);+ log_fatal("Unhandled 8-bit IC read at offset %08x", offset);+ exit(0);
+
return 0x0;
}
@@ -56,7 +60,7 @@
case 0x04: ic->mask = value; break;
default: {- log_warn("Unhandled 32-bit IC write at offset %08x (%08x)", offset, value);+ log_fatal("Unhandled 32-bit IC write at offset %08x (%08x)", offset, value);} break;
}
@@ -68,11 +72,11 @@
void psx_ic_write16(psx_ic_t* ic, uint32_t offset, uint16_t value) { switch (offset) {- case 0x00: ic->stat &= value; break;
- case 0x04: ic->mask = value; break;
+ case 0x00: ic->stat &= (uint32_t)value; break;
+ case 0x04: ic->mask &= 0xffff0000; ic->mask |= (uint32_t)value; break;
default: {- log_warn("Unhandled 16-bit IC write at offset %08x (%08x)", offset, value);+
} break;
}
@@ -83,7 +87,9 @@
}
void psx_ic_write8(psx_ic_t* ic, uint32_t offset, uint8_t value) {- log_warn("Unhandled 8-bit IC write at offset %08x (%02x)", offset, value);+ log_fatal("Unhandled 8-bit IC write at offset %08x (%02x)", offset, value);+
+ exit(1);
}
void psx_ic_irq(psx_ic_t* ic, int id) {--- a/psx/dev/mcd.c
+++ b/psx/dev/mcd.c
@@ -29,6 +29,8 @@
}
uint8_t psx_mcd_read(psx_mcd_t* mcd) {+ return 0xff;
+
switch (mcd->state) {case MCD_STATE_TX_HIZ: mcd->tx_data = 0xff; break;
case MCD_STATE_TX_FLG: mcd->tx_data = mcd->flag; mcd->flag = 0x00; break;
@@ -45,7 +47,7 @@
// log_set_quiet(0);
// log_fatal("mcd read %02x", mcd->tx_data);- // log_set_quiet(1);
+ log_set_quiet(1);
return mcd->tx_data;
} break;
@@ -73,7 +75,7 @@
// log_set_quiet(0);
// log_fatal("mcd read %02x", data);- // log_set_quiet(1);
+ log_set_quiet(1);
return data;
} break;
@@ -84,7 +86,7 @@
// log_set_quiet(0);
// log_fatal("mcd read %02x", 'G');- // log_set_quiet(1);
+ log_set_quiet(1);
return 'G';
} break;
@@ -106,7 +108,7 @@
// log_set_quiet(0);
// log_fatal("mcd read %02x", mcd->rx_data);- // log_set_quiet(1);
+ log_set_quiet(1);
return mcd->rx_data;
} break;
@@ -119,7 +121,7 @@
// log_set_quiet(0);
// log_fatal("mcd read %02x", 'G');- // log_set_quiet(1);
+ log_set_quiet(1);
return 'G';
} break;
@@ -130,12 +132,14 @@
// log_set_quiet(0);
// log_fatal("mcd read %02x", mcd->tx_data);- // log_set_quiet(1);
+ log_set_quiet(1);
return mcd->tx_data;
}
void psx_mcd_write(psx_mcd_t* mcd, uint8_t data) {+ return;
+
// log_set_quiet(0);
// log_fatal("mcd write %02x", data);// log_set_quiet(1);
--- a/psx/dev/mdec.c
+++ b/psx/dev/mdec.c
@@ -162,6 +162,8 @@
void mdec_nop(psx_mdec_t* mdec) { /* Do nothing */ } void mdec_decode_macroblock(psx_mdec_t* mdec) {+ return;
+
if (mdec->output_depth < 2) {rl_decode_block(mdec->yblk, mdec->input, mdec->y_quant_table, mdec->scale_table);
@@ -277,17 +279,19 @@
if (mdec->output_words_remaining) {--mdec->output_words_remaining;
- log_set_quiet(0);
- log_fatal("output read %08x", 0);- log_set_quiet(1);
+ // log_set_quiet(0);
+ // log_fatal("output read %08x", 0);+ // log_set_quiet(1);
- return ((uint32_t*)mdec->output)[mdec->output_index++];
+ return 0xaaaaaaaa;
+
+ // return ((uint32_t*)mdec->output)[mdec->output_index++];
} else {mdec->output_empty = 1;
mdec->output_index = 0;
mdec->output_request = 0;
- return 0xaabbccdd;
+ return 0xaaaaaaaa;
}
} break;
case 4: {@@ -313,14 +317,10 @@
uint16_t psx_mdec_read16(psx_mdec_t* mdec, uint32_t offset) { log_fatal("Unhandled 16-bit MDEC read offset=%u", offset);-
- exit(1);
}
uint8_t psx_mdec_read8(psx_mdec_t* mdec, uint32_t offset) { log_fatal("Unhandled 8-bit MDEC read offset=%u", offset);-
- exit(1);
}
void psx_mdec_write32(psx_mdec_t* mdec, uint32_t offset, uint32_t value) {@@ -338,7 +338,7 @@
mdec->busy = 0;
mdec->output_request = mdec->enable_dma1;
- g_mdec_cmd_table[mdec->cmd >> 29](mdec);
+ // g_mdec_cmd_table[mdec->cmd >> 29](mdec);
free(mdec->input);
}
@@ -356,7 +356,7 @@
mdec->input_full = 0;
mdec->busy = 1;
- log_set_quiet(0);
+ //log_set_quiet(0);
switch (mdec->cmd >> 29) { case MDEC_CMD_NOP: {mdec->busy = 0;
@@ -393,7 +393,7 @@
);
} break;
}
- log_set_quiet(1);
+ //log_set_quiet(1);
if (mdec->words_remaining) {mdec->input_request = mdec->enable_dma0;
@@ -431,14 +431,10 @@
void psx_mdec_write16(psx_mdec_t* mdec, uint32_t offset, uint16_t value) { log_fatal("Unhandled 16-bit MDEC write offset=%u, value=%04x", offset, value);-
- exit(1);
}
void psx_mdec_write8(psx_mdec_t* mdec, uint32_t offset, uint8_t value) { log_fatal("Unhandled 8-bit MDEC write offset=%u, value=%02x", offset, value);-
- exit(1);
}
void psx_mdec_destroy(psx_mdec_t* mdec) {--- /dev/null
+++ b/psx/dev/old/cdrom.c
@@ -1,0 +1,1610 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cdrom.h"
+#include "../log.h"
+#include "../msf.h"
+
+/*
+ Drive Status 1st Response 2nd Response
+ Door Open INT5(11h,80h) N/A
+ Spin-up INT5(01h,80h) N/A
+ Detect busy INT5(03h,80h) N/A
+ No Disk INT3(stat) INT5(08h,40h, 00h,00h, 00h,00h,00h,00h)
+ Audio Disk INT3(stat) INT5(0Ah,90h, 00h,00h, 00h,00h,00h,00h)
+ Unlicensed:Mode1 INT3(stat) INT5(0Ah,80h, 00h,00h, 00h,00h,00h,00h)
+ Unlicensed:Mode2 INT3(stat) INT5(0Ah,80h, 20h,00h, 00h,00h,00h,00h)
+ Unlicensed:Mode2+Audio INT3(stat) INT5(0Ah,90h, 20h,00h, 00h,00h,00h,00h)
+ Debug/Yaroze:Mode2 INT3(stat) INT2(02h,00h, 20h,00h, 20h,20h,20h,20h)
+ Licensed:Mode2 INT3(stat) INT2(02h,00h, 20h,00h, 53h,43h,45h,4xh)
+ Modchip:Audio/Mode1 INT3(stat) INT2(02h,00h, 00h,00h, 53h,43h,45h,4xh)
+*/
+
+#define GETID_RESPONSE_SIZE 8
+#define GETID_RESPONSE_END (GETID_RESPONSE_SIZE - 1)
+
+static const uint8_t g_getid_no_disc[] = {+ 0x08, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t g_getid_audio[] = {+ 0x0a, 0x90, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t g_getid_unlicensed[] = {+ 0x0a, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t g_getid_licensed[] = {+ 0x02, 0x00, 0x20, 0x00,
+ 'S' , 'C' , 'E' , 'A'
+};
+
+#define RESP_PUSH(data) { \+ cdrom->rfifo[cdrom->rfifo_index++] = data; \
+ cdrom->rfifo_index &= 15; \
+ SET_BITS(status, STAT_RSLRRDY_MASK, STAT_RSLRRDY_MASK); }
+
+#define PFIFO_POP (cdrom->pfifo[--cdrom->pfifo_index])
+
+#define VALID_BCD(v) (((v & 0xf) <= 9) && ((v & 0xf0) <= 0x90))
+
+void cdrom_cmd_error(psx_cdrom_t* cdrom) {+ SET_BITS(ifr, IFR_INT, IFR_INT5);
+ RESP_PUSH(cdrom->error);
+ RESP_PUSH(GETSTAT_MOTOR | cdrom->error_flags);
+
+ cdrom->pfifo_index = 0;
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+}
+void cdrom_cmd_unimplemented(psx_cdrom_t* cdrom) {+ log_fatal("Unimplemented CDROM command (%u)", cdrom->command);+
+ exit(1);
+}
+void cdrom_cmd_getstat(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index) {+ log_fatal("CdlGetStat: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_GETSTAT;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR | (cdrom->disc ? 0 : GETSTAT_TRAYOPEN));
+
+ if (cdrom->read_ongoing) {+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_READN;
+ cdrom->irq_delay = DELAY_1MS;
+ } else {+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index != 3) {+ log_fatal("CdlSetloc: Expected exactly 3 parameters, got %u instead",+ cdrom->pfifo_index
+ );
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ if (!cdrom->read_ongoing) {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_SETLOC;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } else {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_READN;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ }
+
+ int f = PFIFO_POP;
+ int s = PFIFO_POP;
+ int m = PFIFO_POP;
+
+ if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_INVSUBF;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->seek_ff = f;
+ cdrom->seek_ss = s;
+ cdrom->seek_mm = m;
+
+ cdrom->seek_pending = 1;
+
+ log_fatal("setloc: %02x:%02x:%02x",+ cdrom->seek_mm,
+ cdrom->seek_ss,
+ cdrom->seek_ff
+ );
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ cdrom->delayed_command = CDL_NONE;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+
+ // Read ongoing
+ case CD_STATE_SEND_RESP2: {+ int f = PFIFO_POP;
+ int s = PFIFO_POP;
+ int m = PFIFO_POP;
+
+ if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {+ cdrom->read_ongoing = false;
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_INVSUBF;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->seek_ff = f;
+ cdrom->seek_ss = s;
+ cdrom->seek_mm = m;
+ } break;
+ }
+}
+void cdrom_cmd_play(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ int track = 0;
+
+ // Optional track number parameter
+ if (cdrom->pfifo_index)
+ track = PFIFO_POP;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_PLAY;
+
+ if (track) {+
+ psx_disc_get_track_addr(cdrom->disc, &cdrom->cdda_msf, track);
+
+ msf_to_bcd(&cdrom->cdda_msf);
+
+ cdrom->cdda_track = track;
+ cdrom->seek_mm = cdrom->cdda_msf.m;
+ cdrom->seek_ss = cdrom->cdda_msf.s;
+ cdrom->seek_ff = cdrom->cdda_msf.f;
+
+ cdrom->seek_pending = 1;
+ }
+
+ if (cdrom->seek_pending) {+ cdrom->seek_pending = 0;
+
+ cdrom->cdda_msf.m = cdrom->seek_mm;
+ cdrom->cdda_msf.s = cdrom->seek_ss;
+ cdrom->cdda_msf.f = cdrom->seek_ff;
+
+ // Convert seek to I
+ msf_t msf = cdrom->cdda_msf;
+
+ msf_from_bcd(&msf);
+
+ // Seek to that address and read sector
+ psx_disc_seek(cdrom->disc, msf);
+ psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
+
+ // Increment sector
+ msf_add_f(&msf, 1);
+ msf_to_bcd(&msf);
+
+ cdrom->cdda_msf = msf;
+ cdrom->cdda_sector_offset = 0;
+ }
+
+ cdrom->cdda_playing = 1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_PLAY);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_readn(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+ cdrom->read_ongoing = 1;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ log_fatal("CdlReadN: CD_STATE_RECV_CMD");+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_READN;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ log_fatal("CdlReadN: CD_STATE_SEND_RESP1");+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ // msf_t msf;
+
+ // msf.m = cdrom->seek_mm;
+ // msf.s = cdrom->seek_ss;
+ // msf.f = cdrom->seek_ff;
+
+ // msf_from_bcd(&msf);
+
+ // int err = psx_disc_seek(cdrom->disc, msf);
+
+ // if (err) {+ // log_set_quiet(0);
+ // log_fatal("CdlReadN: Out of bounds seek");+ // log_set_quiet(1);
+
+ // cdrom->irq_delay = DELAY_1MS * 600;
+ // cdrom->delayed_command = CDL_ERROR;
+ // cdrom->state = CD_STATE_ERROR;
+ // cdrom->error = ERR_INVSUBF;
+ // cdrom->error_flags = GETSTAT_SEEKERROR;
+
+ // return;
+ // }
+
+ // psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
+
+ // msf_t sector_msf;
+
+ // sector_msf.m = cdrom->dfifo[0x0c];
+ // sector_msf.s = cdrom->dfifo[0x0d];
+ // sector_msf.f = cdrom->dfifo[0x0e];
+
+ // int correct_msf = (cdrom->seek_mm == sector_msf.m) &&
+ // (cdrom->seek_ss == sector_msf.s) &&
+ // (cdrom->seek_ff == sector_msf.f);
+
+ // Most probably audio sector:
+ // Purposefully constructed audio data could
+ // circumvent this detection code, but it will work
+ // for most intents and purposes
+ // if (!correct_msf) {+ // log_set_quiet(0);
+ // log_fatal("CdlReadN: Audio read");+ // log_set_quiet(1);
+
+ // cdrom->irq_delay = DELAY_1MS * 600;
+ // cdrom->delayed_command = CDL_ERROR;
+ // cdrom->state = CD_STATE_ERROR;
+ // cdrom->error = ERR_SEEK;
+ // cdrom->error_flags = GETSTAT_SEEKERROR;
+
+ // return;
+ // }
+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_READN;
+
+ // if (cdrom->spin_delay) {+ // cdrom->irq_delay += cdrom->spin_delay;
+ // cdrom->spin_delay = 0;
+ // }
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ log_fatal("CdlReadN: CD_STATE_SEND_RESP2");+
+ msf_t msf;
+
+ msf.m = cdrom->seek_mm;
+ msf.s = cdrom->seek_ss;
+ msf.f = cdrom->seek_ff;
+
+ msf_from_bcd(&msf);
+
+ psx_disc_seek(cdrom->disc, msf);
+ psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
+ cdrom->dfifo_index = 0;
+ cdrom->dfifo_full = 1;
+
+ // printf("Sector header: msf=%02x:%02x:%02x, mode=%02x, subheader=%02x,%02x,%02x,%02x\n",+ // cdrom->dfifo[0x0c],
+ // cdrom->dfifo[0x0d],
+ // cdrom->dfifo[0x0e],
+ // cdrom->dfifo[0x0f],
+ // cdrom->dfifo[0x10],
+ // cdrom->dfifo[0x11],
+ // cdrom->dfifo[0x12],
+ // cdrom->dfifo[0x13]
+ // );
+
+ if (cdrom->dfifo[0x12] & 0x20) {+ log_fatal("Unimplemented XA Form2 Sector");+
+ // exit(1);
+ }
+
+ cdrom->seek_ff++;
+
+ if ((cdrom->seek_ff & 0xF) == 10) { cdrom->seek_ff += 0x10; cdrom->seek_ff &= 0xF0; }+ if (cdrom->seek_ff == 0x75) { cdrom->seek_ss++; cdrom->seek_ff = 0; }+ if ((cdrom->seek_ss & 0xF) == 10) { cdrom->seek_ss += 0x10; cdrom->seek_ss &= 0xF0; }+ if (cdrom->seek_ss == 0x60) { cdrom->seek_mm++; cdrom->seek_ss = 0; }+ if ((cdrom->seek_mm & 0xF) == 10) { cdrom->seek_mm += 0x10; cdrom->seek_mm &= 0xF0; }+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_READN;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT1);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+ } break;
+ }
+}
+void cdrom_cmd_motoron(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->read_ongoing = 0;
+ cdrom->cdda_playing = 0;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_MOTORON;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_MOTORON;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, 2);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_stop(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->read_ongoing = 0;
+ cdrom->cdda_playing = 0;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_STOP;
+ cdrom->seek_ff = 0;
+ cdrom->seek_ss = 0;
+ cdrom->seek_mm = 0;
+ cdrom->cdda_msf.m = 0;
+ cdrom->cdda_msf.s = 0;
+ cdrom->cdda_msf.f = 0;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_STOP;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, 2);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_pause(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->read_ongoing = 0;
+ cdrom->cdda_playing = 0;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_PAUSE;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_PAUSE;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, 2);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_init(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_INIT;
+ cdrom->read_ongoing = 0;
+ cdrom->mode = 0;
+ cdrom->dfifo_index = 0;
+ cdrom->dfifo_full = 0;
+ cdrom->pfifo_index = 0;
+ cdrom->rfifo_index = 0;
+ cdrom->seek_mm = 0;
+ cdrom->seek_ss = 0;
+ cdrom->seek_ff = 0;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(cdrom->stat);
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_INIT;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, 2);
+ RESP_PUSH(cdrom->stat);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_unmute(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index) {+ log_fatal("CdlUnmute: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_DEMUTE;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(cdrom->stat);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_setfilter(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index != 2) {+ log_fatal("CdlSetfilter: Expected exactly 2 parameter");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->pfifo_index = 0;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_SETFILTER;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ cdrom->delayed_command = CDL_NONE;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(cdrom->stat);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_setmode(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index != 1) {+ log_fatal("CdlSetmode: Expected exactly 1 parameter");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ int prev_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->mode = PFIFO_POP;
+
+ if ((cdrom->mode & MODE_SPEED) != prev_speed)
+ cdrom->spin_delay = DELAY_1MS * 650;
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_SETMODE;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ cdrom->delayed_command = CDL_NONE;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) { log_fatal("getlocl: Unimplemented"); exit(1); }+void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) {+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_GETLOCP;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x00);
+ RESP_PUSH(0x01);
+ RESP_PUSH(0xff);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_gettn(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index) {+ log_fatal("CdlGetTN: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_GETTN;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ int tn;
+
+ psx_disc_get_track_count(cdrom->disc, &tn);
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(tn);
+ RESP_PUSH(0x01);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_gettd(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index != 1) {+ log_fatal("CdlGetTD: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->gettd_track = PFIFO_POP;
+
+ int err = psx_disc_get_track_addr(cdrom->disc, NULL, cdrom->gettd_track);
+
+ if (err) {+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_GETTD;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ msf_t td;
+
+ psx_disc_get_track_addr(cdrom->disc, &td, cdrom->gettd_track);
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(ITOB(td.s));
+ RESP_PUSH(ITOB(td.m));
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->delayed_command = CDL_NONE;
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_seekl(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index) {+ log_fatal("CdlSeekL: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_SEEKL;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_SEEKL;
+
+ msf_t msf;
+
+ msf.m = cdrom->seek_mm;
+ msf.s = cdrom->seek_ss;
+ msf.f = cdrom->seek_ff;
+
+ msf_from_bcd(&msf);
+
+ psx_disc_seek(cdrom->disc, msf);
+
+ cdrom->seek_pending = 0;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, 2);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_seekp(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index) {+ log_fatal("CdlSeekP: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_SEEKP;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_SEEKP;
+
+ msf_t msf;
+
+ msf.m = cdrom->seek_mm;
+ msf.s = cdrom->seek_ss;
+ msf.f = cdrom->seek_ff;
+
+ msf_from_bcd(&msf);
+
+ psx_disc_seek(cdrom->disc, msf);
+
+ cdrom->seek_pending = 0;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ SET_BITS(ifr, IFR_INT, 2);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_test(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index != 1) {+ log_fatal("CdlTest: Expected exactly 1 parameter");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ if (PFIFO_POP != 0x20) {+ log_fatal("CdlTest: Unhandled subcommand");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_INVSUBF;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_TEST;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ cdrom->delayed_command = CDL_NONE;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(0x01);
+ RESP_PUSH(0x95);
+ RESP_PUSH(0x13);
+ RESP_PUSH(0x03);
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ } break;
+ }
+}
+void cdrom_cmd_getid(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ if (cdrom->pfifo_index) {+ log_fatal("CdlGetID: Expected exactly 0 parameters");+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_PCOUNT;
+ cdrom->error_flags = GETSTAT_ERROR;
+
+ return;
+ }
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_GETID;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ SET_BITS(ifr, IFR_INT, 3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_GETID;
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ if (cdrom->disc) {+ SET_BITS(ifr, IFR_INT, 2);
+
+ switch (cdrom->cd_type) {+ case CDT_LICENSED: {+ for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
+ RESP_PUSH(g_getid_licensed[GETID_RESPONSE_END - i]);
+ } break;
+
+ case CDT_AUDIO: {+ for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
+ RESP_PUSH(g_getid_audio[GETID_RESPONSE_END - i]);
+ } break;
+
+ case CDT_UNKNOWN: {+ for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
+ RESP_PUSH(g_getid_unlicensed[GETID_RESPONSE_END - i]);
+ } break;
+ }
+ } else {+ SET_BITS(ifr, IFR_INT, 5);
+
+ for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
+ RESP_PUSH(g_getid_no_disc[GETID_RESPONSE_END - i]);
+ }
+
+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+ } break;
+ }
+}
+void cdrom_cmd_reads(psx_cdrom_t* cdrom) {+ cdrom->delayed_command = CDL_NONE;
+ cdrom->read_ongoing = 1;
+
+ switch (cdrom->state) {+ case CD_STATE_RECV_CMD: {+ log_fatal("CdlReadS: CD_STATE_RECV_CMD");+ cdrom->irq_delay = DELAY_1MS;
+ cdrom->state = CD_STATE_SEND_RESP1;
+ cdrom->delayed_command = CDL_READS;
+ } break;
+
+ case CD_STATE_SEND_RESP1: {+ log_fatal("CdlReadS: CD_STATE_SEND_RESP1");+
+ SET_BITS(ifr, IFR_INT, IFR_INT3);
+ RESP_PUSH(GETSTAT_MOTOR);
+
+ msf_t msf;
+
+ msf.m = cdrom->seek_mm;
+ msf.s = cdrom->seek_ss;
+ msf.f = cdrom->seek_ff;
+
+ msf_from_bcd(&msf);
+
+ int err = psx_disc_seek(cdrom->disc, msf);
+
+ if (err) {+ log_fatal("CdlReadS: Out of bounds seek");+
+ cdrom->irq_delay = DELAY_1MS * 600;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_INVSUBF;
+ cdrom->error_flags = GETSTAT_SEEKERROR;
+
+ return;
+ }
+
+ psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
+
+ msf_t sector_msf;
+
+ sector_msf.m = cdrom->dfifo[0x0c];
+ sector_msf.s = cdrom->dfifo[0x0d];
+ sector_msf.f = cdrom->dfifo[0x0e];
+
+ int correct_msf = (cdrom->seek_mm == sector_msf.m) &&
+ (cdrom->seek_ss == sector_msf.s) &&
+ (cdrom->seek_ff == sector_msf.f);
+
+ // Most probably audio sector:
+ // Purposefully constructed audio data could
+ // circumvent this detection code, but it will work
+ // for most intents and purposes
+ if (!correct_msf) {+ log_fatal("CdlReadS: Audio read");+
+ cdrom->irq_delay = DELAY_1MS * 600;
+ cdrom->delayed_command = CDL_ERROR;
+ cdrom->state = CD_STATE_ERROR;
+ cdrom->error = ERR_SEEK;
+ cdrom->error_flags = GETSTAT_SEEKERROR;
+
+ return;
+ }
+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_READS;
+
+ if (cdrom->spin_delay) {+ cdrom->irq_delay += cdrom->spin_delay;
+ cdrom->spin_delay = 0;
+ }
+ } break;
+
+ case CD_STATE_SEND_RESP2: {+ log_fatal("CdlReadS: CD_STATE_SEND_RESP2");+
+ msf_t msf;
+
+ msf.m = cdrom->seek_mm;
+ msf.s = cdrom->seek_ss;
+ msf.f = cdrom->seek_ff;
+
+ msf_from_bcd(&msf);
+
+ psx_disc_seek(cdrom->disc, msf);
+ psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
+
+ if (cdrom->mode & MODE_XA_ADPCM) {+ cdrom->state = CD_STATE_RECV_CMD;
+ cdrom->delayed_command = CDL_NONE;
+
+ return;
+ }
+
+ if (cdrom->dfifo[0x12] & 0x20) {+ log_fatal("Unimplemented XA Form2 Sector");+
+ // exit(1);
+ }
+
+ cdrom->seek_ff++;
+
+ if ((cdrom->seek_ff & 0xF) == 10) { cdrom->seek_ff += 0x10; cdrom->seek_ff &= 0xF0; }+ if (cdrom->seek_ff == 0x75) { cdrom->seek_ss++; cdrom->seek_ff = 0; }+ if ((cdrom->seek_ss & 0xF) == 10) { cdrom->seek_ss += 0x10; cdrom->seek_ss &= 0xF0; }+ if (cdrom->seek_ss == 0x60) { cdrom->seek_mm++; cdrom->seek_ss = 0; }+ if ((cdrom->seek_mm & 0xF) == 10) { cdrom->seek_mm += 0x10; cdrom->seek_mm &= 0xF0; }+
+ int double_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
+ cdrom->state = CD_STATE_SEND_RESP2;
+ cdrom->delayed_command = CDL_READS;
+ cdrom->dfifo_index = 0;
+
+ SET_BITS(ifr, IFR_INT, IFR_INT1);
+ RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+ } break;
+ }
+}
+void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) { log_fatal("readtoc: Unimplemented"); exit(1); }+
+typedef void (*cdrom_cmd_t)(psx_cdrom_t*);
+
+const char* g_psx_cdrom_command_names[] = {+ "CdlUnimplemented",
+ "CdlGetstat",
+ "CdlSetloc",
+ "CdlPlay",
+ "CdlUnimplemented",
+ "CdlUnimplemented",
+ "CdlReadn",
+ "CdlMotoron",
+ "CdlStop",
+ "CdlPause",
+ "CdlInit",
+ "CdlUnimplemented",
+ "CdlUnmute",
+ "CdlSetfilter",
+ "CdlSetmode",
+ "CdlUnimplemented",
+ "CdlGetlocl",
+ "CdlGetlocp",
+ "CdlUnimplemented",
+ "CdlGettn",
+ "CdlGettd",
+ "CdlSeekl",
+ "CdlSeekp",
+ "CdlUnimplemented",
+ "CdlUnimplemented",
+ "CdlTest",
+ "CdlGetid",
+ "CdlReads",
+ "CdlUnimplemented",
+ "CdlUnimplemented",
+ "CdlReadtoc",
+ "CdlUnimplemented"
+};
+
+cdrom_cmd_t g_psx_cdrom_command_table[] = {+ cdrom_cmd_unimplemented,
+ cdrom_cmd_getstat,
+ cdrom_cmd_setloc,
+ cdrom_cmd_play,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_readn,
+ cdrom_cmd_motoron,
+ cdrom_cmd_stop,
+ cdrom_cmd_pause,
+ cdrom_cmd_init,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_unmute,
+ cdrom_cmd_setfilter,
+ cdrom_cmd_setmode,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_getlocl,
+ cdrom_cmd_getlocp,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_gettn,
+ cdrom_cmd_gettd,
+ cdrom_cmd_seekl,
+ cdrom_cmd_seekp,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_test,
+ cdrom_cmd_getid,
+ cdrom_cmd_reads,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_unimplemented,
+ cdrom_cmd_readtoc,
+
+ // Actually an unimplemented command, we use this
+ // index for CD error handling
+ cdrom_cmd_error
+};
+
+typedef uint8_t (*psx_cdrom_read_function_t)(psx_cdrom_t*);
+typedef void (*psx_cdrom_write_function_t)(psx_cdrom_t*, uint8_t);
+
+uint8_t cdrom_read_status(psx_cdrom_t* cdrom) {+ return cdrom->status;
+}
+
+uint8_t cdrom_read_rfifo(psx_cdrom_t* cdrom) {+ uint8_t data = cdrom->rfifo[--cdrom->rfifo_index];
+
+ if (cdrom->rfifo_index == 0)
+ SET_BITS(status, STAT_RSLRRDY_MASK, 0);
+
+ return data;
+}
+
+uint8_t cdrom_read_dfifo(psx_cdrom_t* cdrom) {+ if (!cdrom->dfifo_full)
+ return 0;
+
+ int sector_size_bit = cdrom->mode & MODE_SECTOR_SIZE;
+
+ uint32_t sector_size = sector_size_bit ? 0x924 : 0x800;
+ uint32_t offset = sector_size_bit ? 12 : 24;
+
+ if (cdrom->dfifo_index < sector_size) {+ SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
+
+ return cdrom->dfifo[offset + (cdrom->dfifo_index++)];
+ } else {+ SET_BITS(status, STAT_DRQSTS_MASK, 0);
+
+ cdrom->dfifo_full = 0;
+ }
+
+ return 0x00;
+}
+
+uint8_t cdrom_read_ier(psx_cdrom_t* cdrom) {+ return cdrom->ier;
+}
+
+uint8_t cdrom_read_ifr(psx_cdrom_t* cdrom) {+ return 0xe0 | cdrom->ifr;
+}
+
+void cdrom_write_status(psx_cdrom_t* cdrom, uint8_t value) {+ SET_BITS(status, STAT_INDEX_MASK, value);
+}
+
+void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t value) {+ log_set_quiet(0);
+ log_fatal("%s(%02x) %u params=[%02x, %02x, %02x, %02x, %02x, %02x]",+ g_psx_cdrom_command_names[value],
+ value,
+ cdrom->pfifo_index,
+ cdrom->pfifo[0],
+ cdrom->pfifo[1],
+ cdrom->pfifo[2],
+ cdrom->pfifo[3],
+ cdrom->pfifo[4],
+ cdrom->pfifo[5]
+ );
+ log_set_quiet(1);
+
+ cdrom->command = value;
+ cdrom->state = CD_STATE_RECV_CMD;
+
+ g_psx_cdrom_command_table[value](cdrom);
+}
+
+void cdrom_write_pfifo(psx_cdrom_t* cdrom, uint8_t value) {+ cdrom->pfifo[(cdrom->pfifo_index++) & 0xf] = value;
+
+ SET_BITS(status, STAT_PRMWRDY_MASK, (cdrom->pfifo_index & 0x10) ? 0x0 : 0xff);
+
+ cdrom->pfifo_index &= 0x1f;
+}
+
+void cdrom_write_req(psx_cdrom_t* cdrom, uint8_t value) {+ if (value & REQ_BFRD) {+ SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
+
+ cdrom->dfifo_full = 1;
+ cdrom->dfifo_index = 0;
+ } else {+ SET_BITS(status, STAT_DRQSTS_MASK, 0);
+
+ cdrom->dfifo_full = 0;
+ cdrom->dfifo_index = 0;
+ }
+}
+
+void cdrom_write_smdout(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Sound map data out unimplemented");+}
+
+void cdrom_write_ier(psx_cdrom_t* cdrom, uint8_t value) {+ cdrom->ier = value;
+}
+
+void cdrom_write_ifr(psx_cdrom_t* cdrom, uint8_t value) {+ cdrom->ifr &= ~(value & 0x1f);
+
+ // if (value & 0x7) {+ // log_set_quiet(0);
+ // log_fatal("Acknowledge %02x", value & 0x7);+ // log_set_quiet(1);
+ // }
+
+ // Clear Parameter FIFO
+ if (value & 0x40) {+ cdrom->pfifo_index = 0;
+
+ SET_BITS(
+ status,
+ (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK),
+ (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK)
+ );
+ }
+}
+
+void cdrom_write_sminfo(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Sound map coding info unimplemented");+}
+
+void cdrom_write_lcdlspuv(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Volume registers unimplemented");+}
+
+void cdrom_write_rcdrspuv(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Volume registers unimplemented");+}
+
+void cdrom_write_rcdlspuv(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Volume registers unimplemented");+}
+
+void cdrom_write_lcdrspuv(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Volume registers unimplemented");+}
+
+void cdrom_write_volume(psx_cdrom_t* cdrom, uint8_t value) {+ log_fatal("Volume registers unimplemented");+}
+
+psx_cdrom_read_function_t g_psx_cdrom_read_table[] = {+ cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ier,
+ cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ifr,
+ cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ier,
+ cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ifr
+};
+
+psx_cdrom_write_function_t g_psx_cdrom_write_table[] = {+ cdrom_write_status, cdrom_write_cmd , cdrom_write_pfifo , cdrom_write_req ,
+ cdrom_write_status, cdrom_write_smdout , cdrom_write_ier , cdrom_write_ifr ,
+ cdrom_write_status, cdrom_write_sminfo , cdrom_write_lcdlspuv, cdrom_write_lcdrspuv,
+ cdrom_write_status, cdrom_write_rcdrspuv, cdrom_write_rcdlspuv, cdrom_write_volume
+};
+
+const char* g_psx_cdrom_read_names_table[] = {+ "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ier",
+ "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ifr",
+ "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ier",
+ "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ifr"
+};
+
+const char* g_psx_cdrom_write_names_table[] = {+ "cdrom_write_status", "cdrom_write_cmd" , "cdrom_write_pfifo" , "cdrom_write_req" ,
+ "cdrom_write_status", "cdrom_write_smdout" , "cdrom_write_ier" , "cdrom_write_ifr" ,
+ "cdrom_write_status", "cdrom_write_sminfo" , "cdrom_write_lcdlspuv", "cdrom_write_lcdrspuv",
+ "cdrom_write_status", "cdrom_write_rcdrspuv", "cdrom_write_rcdlspuv", "cdrom_write_volume"
+};
+
+psx_cdrom_t* psx_cdrom_create() {+ return (psx_cdrom_t*)malloc(sizeof(psx_cdrom_t));
+}
+
+void psx_cdrom_init(psx_cdrom_t* cdrom, psx_ic_t* ic) {+ memset(cdrom, 0, sizeof(psx_cdrom_t));
+
+ cdrom->io_base = PSX_CDROM_BEGIN;
+ cdrom->io_size = PSX_CDROM_SIZE;
+
+ cdrom->ic = ic;
+ cdrom->status = STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK | STAT_RSLRRDY_MASK;
+ cdrom->dfifo = malloc(CD_SECTOR_SIZE);
+ cdrom->cdda_buf = malloc(CD_SECTOR_SIZE);
+}
+
+uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t offset) {+ log_fatal("Unhandled 32-bit CDROM read at offset %08x", offset);+
+ return 0x0;
+}
+
+uint16_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t offset) {+ log_fatal("Unhandled 16-bit CDROM read at offset %08x", offset);+
+ return 0x0;
+}
+
+uint8_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t offset) {+ uint8_t data = g_psx_cdrom_read_table[(STAT_INDEX << 2) | offset](cdrom);
+
+ // log_fatal("%s (read %02x)", g_psx_cdrom_read_names_table[(STAT_INDEX << 2) | offset], data);+
+ return data;
+}
+
+void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t offset, uint32_t value) {+ log_fatal("Unhandled 32-bit CDROM write at offset %08x (%08x)", offset, value);+}
+
+void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t offset, uint16_t value) {+ log_fatal("Unhandled 16-bit CDROM write at offset %08x (%04x)", offset, value);+}
+
+void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t offset, uint8_t value) {+ // log_fatal("%s (write %02x)", g_psx_cdrom_write_names_table[(STAT_INDEX << 2) | offset], value);+
+ g_psx_cdrom_write_table[(STAT_INDEX << 2) | offset](cdrom, value);
+}
+
+void psx_cdrom_update(psx_cdrom_t* cdrom, int cyc) {+ if (cdrom->irq_delay) {+ cdrom->irq_delay -= cyc;
+
+ if (cdrom->irq_delay <= 0) {+ psx_ic_irq(cdrom->ic, IC_CDROM);
+
+ cdrom->irq_delay = 0;
+
+ if (cdrom->delayed_command) {+ // log_set_quiet(0);
+ // log_fatal("%s(%02x) (Delayed)",+ // g_psx_cdrom_command_names[cdrom->delayed_command],
+ // cdrom->delayed_command
+ // );
+ log_set_quiet(1);
+ g_psx_cdrom_command_table[cdrom->delayed_command](cdrom);
+ }
+
+ // log_set_quiet(0);
+ // log_fatal("CDROM INT%u", cdrom->ifr & 0x7);+ log_set_quiet(1);
+ }
+ }
+}
+
+const char* g_psx_cdrom_extensions[] = {+ "cue",
+ "bin",
+ 0
+};
+
+enum {+ CD_EXT_CUE,
+ CD_EXT_BIN,
+ CD_EXT_UNSUPPORTED
+};
+
+int cdrom_get_extension(const char* path) {+ const char* ptr = &path[strlen(path) - 1];
+ int i = 0;
+
+ while ((*ptr != '.') && (ptr != path))
+ ptr--;
+
+ if (ptr == path)
+ return CD_EXT_UNSUPPORTED;
+
+ while (g_psx_cdrom_extensions[i]) {+ if (!strcmp(ptr + 1, g_psx_cdrom_extensions[i]))
+ return i;
+
+ ++i;
+ }
+
+ return CD_EXT_UNSUPPORTED;
+}
+
+void cdrom_check_cd_type(psx_cdrom_t* cdrom) {+ char buf[CD_SECTOR_SIZE];
+
+ // Seek to Primary Volume Descriptor
+ msf_t pvd = { 0, 2, 16 };+
+ // If the disc is smaller than 16 sectors
+ // then it can't be a PlayStation game.
+ // Audio discs should also have ISO volume
+ // descriptors, so it's probably something else
+ // entirely.
+ if (psx_disc_seek(cdrom->disc, pvd)) {+ cdrom->cd_type = CDT_UNKNOWN;
+
+ return;
+ }
+
+ psx_disc_read_sector(cdrom->disc, buf);
+
+ // Check for the "PLAYSTATION" string at PVD offset 20h
+
+ // Patch 20 byte so comparison is done correctly
+ buf[0x2b] = 0;
+
+ if (strncmp(&buf[0x20], "PLAYSTATION", 12)) {+ cdrom->cd_type = CDT_AUDIO;
+
+ return;
+ }
+
+ cdrom->cd_type = CDT_LICENSED;
+}
+
+void psx_cdrom_open(psx_cdrom_t* cdrom, const char* path) {+ cdrom->disc = psx_disc_create();
+
+ int ext = cdrom_get_extension(path);
+ int error = 0;
+
+ switch (ext) {+ case CD_EXT_CUE: {+ psxd_cue_t* cue = psxd_cue_create();
+
+ psxd_cue_init_disc(cue, cdrom->disc);
+ psxd_cue_init(cue);
+ error = psxd_cue_load(cue, path);
+
+ if (error)
+ break;
+
+ cdrom_check_cd_type(cdrom);
+ } break;
+
+ case CD_EXT_BIN: {+ psxd_bin_t* bin = psxd_bin_create();
+
+ psxd_bin_init_disc(bin, cdrom->disc);
+ psxd_bin_init(bin);
+
+ error = psxd_bin_load(bin, path);
+
+ if (error)
+ break;
+
+ cdrom_check_cd_type(cdrom);
+ } break;
+
+ case CD_EXT_UNSUPPORTED: {+ log_fatal("Unsupported disc format");+
+ cdrom->cd_type = CDT_UNKNOWN;
+ } break;
+ }
+
+ if (error) {+ log_fatal("Error loading file \'%s\'", path);+
+ exit(1);
+ }
+}
+
+void psx_cdrom_get_cdda_samples(psx_cdrom_t* cdrom, void* buf, int size, psx_spu_t* spu) {+ if (!cdrom->cdda_playing) {+ memset(buf, 0, size);
+
+ return;
+ }
+
+ if (!cdrom->disc)
+ return;
+
+ // Convert seek to I
+ msf_t msf = cdrom->cdda_msf;
+
+ msf_from_bcd(&msf);
+
+ // Seek to that address and read sector
+ if (psx_disc_seek(cdrom->disc, msf)) {+ cdrom->cdda_playing = 0;
+ }
+
+ psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
+
+ ++cdrom->cdda_sectors_played;
+
+ // Increment sector
+ msf_add_f(&msf, 1);
+ msf_to_bcd(&msf);
+
+ // Assign to CDDA MSF
+ cdrom->cdda_msf = msf;
+
+ memcpy(buf, cdrom->cdda_buf, size);
+
+ psx_spu_update_cdda_buffer(spu, cdrom->cdda_buf);
+
+ // Handle report IRQ
+ if (cdrom->cdda_sectors_played == CD_SECTORS_PS) {+ if (cdrom->mode & MODE_REPORT) {+ SET_BITS(ifr, IFR_INT, 1);
+
+ msf_t track, current = cdrom->cdda_msf;
+
+ msf_from_bcd(¤t);
+
+ psx_disc_get_track_addr(cdrom->disc, &track, cdrom->cdda_track);
+
+ unsigned int track_s = (track.m * 60) + track.s;
+ unsigned int current_s = (current.m * 60) + current.s;
+ unsigned int diff = current_s - track_s;
+
+ current.s = diff;
+ current.m = 0;
+
+ msf_adjust(¤t);
+ msf_to_bcd(¤t);
+
+ RESP_PUSH(0);
+ RESP_PUSH(0);
+ RESP_PUSH(cdrom->cdda_msf.f);
+ RESP_PUSH(current.s | 0x80);
+ RESP_PUSH(current.m);
+ RESP_PUSH(0);
+ RESP_PUSH(cdrom->cdda_track);
+ RESP_PUSH(GETSTAT_PLAY);
+
+ psx_ic_irq(cdrom->ic, IC_CDROM);
+ }
+
+ cdrom->cdda_sectors_played = 0;
+ }
+}
+
+void psx_cdrom_destroy(psx_cdrom_t* cdrom) {+ if (cdrom->disc)
+ psx_disc_destroy(cdrom->disc);
+
+ free(cdrom);
+}
\ No newline at end of file
--- a/psx/dev/pad.c
+++ b/psx/dev/pad.c
@@ -101,7 +101,7 @@
uint32_t pad_handle_stat_read(psx_pad_t* pad) {// log_set_quiet(0);
// log_fatal("pad stat read");- // log_set_quiet(1);
+ log_set_quiet(1);
return 0x07;
psx_input_t* joy = pad->joy_slot[(pad->ctrl >> 13) & 1];
--- /dev/null
+++ b/psx/dev/rework/gpu.c
@@ -1,0 +1,1773 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "gpu.h"
+#include "../log.h"
+
+int g_psx_gpu_dither_kernel[] = {+ -4, +0, -3, +1,
+ +2, -2, +3, -1,
+ -3, +1, -4, +0,
+ +3, -1, +2, -2,
+};
+
+uint16_t gpu_to_bgr555(uint32_t color) {+ return ((color & 0x0000f8) >> 3) |
+ ((color & 0x00f800) >> 6) |
+ ((color & 0xf80000) >> 9);
+}
+
+#define BGR555(c) \
+ (((c & 0x0000f8) >> 3) | \
+ ((c & 0x00f800) >> 6) | \
+ ((c & 0xf80000) >> 9))
+
+// #define BGR555(c) gpu_to_bgr555(c)
+
+#define VRAM(x, y) gpu->vram[(x) + ((y) * 1024)]
+
+int min3(int a, int b, int c) {+ int m = a < b ? a : b;
+
+ return m < c ? m : c;
+}
+
+int max3(int a, int b, int c) {+ int m = a > b ? a : b;
+
+ return m > c ? m : c;
+}
+
+psx_gpu_t* psx_gpu_create() {+ return (psx_gpu_t*)malloc(sizeof(psx_gpu_t));
+}
+
+void psx_gpu_init(psx_gpu_t* gpu, psx_ic_t* ic) {+ memset(gpu, 0, sizeof(psx_gpu_t));
+
+ gpu->io_base = PSX_GPU_BEGIN;
+ gpu->io_size = PSX_GPU_SIZE;
+
+ gpu->vram = (uint16_t*)malloc(PSX_GPU_VRAM_SIZE);
+ gpu->state = GPU_STATE_RECV_CMD;
+
+ gpu->ic = ic;
+}
+
+uint32_t psx_gpu_read32(psx_gpu_t* gpu, uint32_t offset) {+ switch (offset) {+ case 0x00: {+ uint32_t data = 0x0;
+
+ if (gpu->c0_tsiz) {+ data |= gpu->vram[gpu->c0_addr + (gpu->c0_xcnt + (gpu->c0_ycnt * 1024))];
+
+ gpu->c0_xcnt += 1;
+
+ if (gpu->c0_xcnt == gpu->c0_xsiz) {+ gpu->c0_ycnt += 1;
+ gpu->c0_xcnt = 0;
+ }
+
+ data |= gpu->vram[gpu->c0_addr + (gpu->c0_xcnt + (gpu->c0_ycnt * 1024))] << 16;
+
+ gpu->c0_xcnt += 1;
+
+ if (gpu->c0_xcnt == gpu->c0_xsiz) {+ gpu->c0_ycnt += 1;
+ gpu->c0_xcnt = 0;
+ }
+
+ gpu->c0_tsiz -= 2;
+ }
+
+ if (gpu->gp1_10h_req) {+ switch (gpu->gp1_10h_req & 7) {+ case 2: {+ data = ((gpu->texw_oy / 8) << 15) | ((gpu->texw_ox / 8) << 10) | ((gpu->texw_my / 8) << 5) | (gpu->texw_mx / 8);
+ } break;
+ case 3: {+ data = (gpu->draw_y1 << 10) | gpu->draw_x1;
+ } break;
+ case 4: {+ data = (gpu->draw_y2 << 10) | gpu->draw_x2;
+ } break;
+ case 5: {+ data = (gpu->off_y << 10) | gpu->off_x;
+ } break;
+ }
+
+ gpu->gp1_10h_req = 0;
+ }
+
+ return data;
+ } break;
+ case 0x04: return gpu->gpustat | 0x1e000000;
+ }
+
+ log_warn("Unhandled 32-bit GPU read at offset %08x", offset);+
+ return 0x0;
+}
+
+uint16_t psx_gpu_read16(psx_gpu_t* gpu, uint32_t offset) {+ log_fatal("Unhandled 16-bit GPU read at offset %08x", offset);+}
+
+uint8_t psx_gpu_read8(psx_gpu_t* gpu, uint32_t offset) {+ log_fatal("Unhandled 8-bit GPU read at offset %08x", offset);+}
+
+int min(int x0, int x1) {+ return (x0 < x1) ? x0 : x1;
+}
+
+int max(int x0, int x1) {+ return (x0 > x1) ? x0 : x1;
+}
+
+#define EDGE(a, b, c) ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x))
+
+uint16_t gpu_fetch_texel(psx_gpu_t* gpu, uint16_t tx, uint16_t ty, uint32_t tpx, uint32_t tpy, uint16_t clutx, uint16_t cluty, int depth) {+ tx = (tx & ~gpu->texw_mx) | (gpu->texw_ox & gpu->texw_mx);
+ ty = (ty & ~gpu->texw_my) | (gpu->texw_oy & gpu->texw_my);
+ tx &= 0xff;
+ ty &= 0xff;
+
+ switch (depth) {+ // 4-bit
+ case 0: {+ uint16_t texel = VRAM(tpx + (tx >> 2), tpy + ty);
+ uint16_t index = (texel >> ((tx & 0x3) << 2)) & 0xf;
+
+ return VRAM(clutx + index, cluty);
+ } break;
+
+ // 8-bit
+ case 1: {+ uint16_t texel = VRAM(tpx + (tx >> 1), tpy + ty);
+ uint16_t index = (texel >> ((tx & 0x1) << 3)) & 0xff;
+
+ return VRAM(clutx + index, cluty);
+ } break;
+
+ // 15-bit
+ default: {+ return VRAM(tpx + tx, tpy + ty);
+ } break;
+ }
+}
+
+void gpu_render_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, poly_data_t data) {+ vertex_t a, b, c, p;
+
+ int tpx = (data.texp & 0xf) << 6;
+ int tpy = (data.texp & 0x10) << 4;
+ int clutx = (data.clut & 0x3f) << 4;
+ int cluty = (data.clut >> 6) & 0x1ff;
+ int depth = (data.texp >> 7) & 3;
+ int transp = (data.attrib & PA_TRANSP) != 0;
+ int transp_mode;
+
+ if (data.attrib & PA_TEXTURED) {+ transp_mode = (data.texp >> 5) & 3;
+ } else {+ transp_mode = (gpu->gpustat >> 5) & 3;
+ }
+
+ a = v0;
+
+ /* Ensure the winding order is correct */
+ if (EDGE(v0, v1, v2) < 0) {+ b = v2;
+ c = v1;
+ } else {+ b = v1;
+ c = v2;
+ }
+
+ a.x += gpu->off_x;
+ a.y += gpu->off_y;
+ b.x += gpu->off_x;
+ b.y += gpu->off_y;
+ c.x += gpu->off_x;
+ c.y += gpu->off_y;
+
+ int xmin = min3(a.x, b.x, c.x);
+ int ymin = min3(a.y, b.y, c.y);
+ int xmax = max3(a.x, b.x, c.x);
+ int ymax = max3(a.y, b.y, c.y);
+
+ float area = EDGE(a, b, c);
+
+ for (int y = ymin; y < ymax; y++) {+ for (int x = xmin; x < xmax; x++) {+ p.x = x;
+ p.y = y;
+
+ float z0 = EDGE(b, c, p);
+ float z1 = EDGE(c, a, p);
+ float z2 = EDGE(a, b, p);
+
+ if ((z0 < 0) || (z1 < 0) || (z2 < 0))
+ continue;
+
+ int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
+ (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
+
+ if (!bc)
+ continue;
+
+ uint16_t color = 0;
+ uint32_t mod = 0;
+
+ if (data.attrib & PA_SHADED) {+ int cr = (z0 * ((a.c >> 0) & 0xff) + z1 * ((b.c >> 0) & 0xff) + z2 * ((c.c >> 0) & 0xff)) / area;
+ int cg = (z0 * ((a.c >> 8) & 0xff) + z1 * ((b.c >> 8) & 0xff) + z2 * ((c.c >> 8) & 0xff)) / area;
+ int cb = (z0 * ((a.c >> 16) & 0xff) + z1 * ((b.c >> 16) & 0xff) + z2 * ((c.c >> 16) & 0xff)) / area;
+
+ int dy = (y - ymin) & 3;
+ int dx = (x - xmin) & 3;
+
+ int dither = g_psx_gpu_dither_kernel[dx + (dy * 4)];
+
+ cr += dither;
+ cg += dither;
+ cb += dither;
+
+ // Saturate (clamp) to 00-ff
+ cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
+ cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
+ cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
+
+ uint32_t rgb = (cb << 16) | (cg << 8) | cr;
+
+ mod = rgb;
+ } else {+ mod = data.v[0].c;
+ }
+
+ if (data.attrib & PA_TEXTURED) {+ uint32_t tx = ((z0 * a.tx) + (z1 * b.tx) + (z2 * c.tx)) / area;
+ uint32_t ty = ((z0 * a.ty) + (z1 * b.ty) + (z2 * c.ty)) / area;
+
+ uint16_t texel = gpu_fetch_texel(gpu, tx, ty, tpx, tpy, clutx, cluty, depth);
+
+ if (!texel)
+ continue;
+
+ if (transp) {+ transp = (texel & 0x8000) != 0;
+ }
+
+ if (data.attrib & PA_RAW) {+ color = texel;
+ } else {+ int tr = ((texel >> 0 ) & 0x1f) << 3;
+ int tg = ((texel >> 5 ) & 0x1f) << 3;
+ int tb = ((texel >> 10) & 0x1f) << 3;
+
+ int mr = (mod >> 0 ) & 0xff;
+ int mg = (mod >> 8 ) & 0xff;
+ int mb = (mod >> 16) & 0xff;
+
+ int cr = (tr * mr) / 0x80;
+ int cg = (tg * mg) / 0x80;
+ int cb = (tb * mb) / 0x80;
+
+ cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
+ cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
+ cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
+
+ uint32_t rgb = cr | (cg << 8) | (cb << 16);
+
+ color = BGR555(rgb);
+ }
+ } else {+ color = BGR555(mod);
+ }
+
+ int cr = ((color >> 0 ) & 0x1f) << 3;
+ int cg = ((color >> 5 ) & 0x1f) << 3;
+ int cb = ((color >> 10) & 0x1f) << 3;
+
+ if (transp) {+ uint16_t back = VRAM(x, y);
+
+ int br = ((back >> 0 ) & 0x1f) << 3;
+ int bg = ((back >> 5 ) & 0x1f) << 3;
+ int bb = ((back >> 10) & 0x1f) << 3;
+
+ switch (transp_mode) {+ case 0: {+ cr = (0.5f * br) + (0.5f * cr);
+ cg = (0.5f * bg) + (0.5f * cg);
+ cb = (0.5f * bb) + (0.5f * cb);
+ } break;
+ case 1: {+ cr = br + cr;
+ cg = bg + cg;
+ cb = bb + cb;
+ } break;
+ case 2: {+ cr = br - cr;
+ cg = bg - cg;
+ cb = bb - cb;
+ } break;
+ case 3: {+ cr = br + (0.25 * cr);
+ cg = bg + (0.25 * cg);
+ cb = bb + (0.25 * cb);
+ } break;
+ }
+
+ cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
+ cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
+ cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
+
+ uint32_t rgb = cr | (cg << 8) | (cb << 16);
+
+ color = BGR555(rgb);
+ }
+
+ VRAM(x, y) = color;
+ }
+ }
+}
+
+void gpu_render_rect(psx_gpu_t* gpu, rect_data_t data) {+ uint16_t width, height;
+
+ switch ((data.attrib >> 3) & 3) {+ case RS_VARIABLE: { width = data.width; height = data.height; } break;+ case RS_1X1 : { width = 1 ; height = 1 ; } break;+ case RS_8X8 : { width = 8 ; height = 8 ; } break;+ case RS_16X16 : { width = 16 ; height = 16 ; } break;+ }
+
+ int textured = (data.attrib & RA_TEXTURED) != 0;
+ int transp = (data.attrib & RA_TRANSP) != 0;
+ int transp_mode = (gpu->gpustat >> 5) & 3;
+
+ int clutx = (data.clut & 0x3f) << 4;
+ int cluty = (data.clut >> 6) & 0x1ff;
+
+ /* Offset coordinates */
+ data.v0.x += gpu->off_x;
+ data.v0.y += gpu->off_y;
+
+ /* Calculate bounding box */
+ int xmax = data.v0.x + width;
+ int ymax = data.v0.y + height;
+
+ int32_t xc = 0, yc = 0;
+
+ for (int16_t y = data.v0.y; y < ymax; y++) {+ for (int16_t x = data.v0.x; x < xmax; x++) {+ int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
+ (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
+
+ if (!bc)
+ goto skip;
+
+ uint16_t color;
+
+ if (textured) {+ uint16_t texel = gpu_fetch_texel(
+ gpu,
+ data.v0.tx + xc, data.v0.ty + yc,
+ gpu->texp_x, gpu->texp_y,
+ clutx, cluty,
+ gpu->texp_d
+ );
+
+ if (!texel)
+ goto skip;
+
+ if (transp) {+ transp = (texel & 0x8000) != 0;
+ }
+
+ int tr = ((texel >> 0 ) & 0x1f) << 3;
+ int tg = ((texel >> 5 ) & 0x1f) << 3;
+ int tb = ((texel >> 10) & 0x1f) << 3;
+
+ int mr = (data.v0.c >> 0 ) & 0xff;
+ int mg = (data.v0.c >> 8 ) & 0xff;
+ int mb = (data.v0.c >> 16) & 0xff;
+
+ int cr = (tr * mr) / 0x80;
+ int cg = (tg * mg) / 0x80;
+ int cb = (tb * mb) / 0x80;
+
+ uint32_t rgb = cr | (cg << 8) | (cb << 16);
+
+ color = BGR555(rgb);
+ } else {+ color = BGR555(data.v0.c);
+ }
+
+ int cr = ((color >> 0 ) & 0x1f) << 3;
+ int cg = ((color >> 5 ) & 0x1f) << 3;
+ int cb = ((color >> 10) & 0x1f) << 3;
+
+ if (transp) {+ uint16_t back = VRAM(x, y);
+
+ int br = ((back >> 0 ) & 0x1f) << 3;
+ int bg = ((back >> 5 ) & 0x1f) << 3;
+ int bb = ((back >> 10) & 0x1f) << 3;
+
+ switch (transp_mode) {+ case 0: {+ cr = (0.5f * br) + (0.5f * cr);
+ cg = (0.5f * bg) + (0.5f * cg);
+ cb = (0.5f * bb) + (0.5f * cb);
+ } break;
+ case 1: {+ cr = br + cr;
+ cg = bg + cg;
+ cb = bb + cb;
+ } break;
+ case 2: {+ cr = br - cr;
+ cg = bg - cg;
+ cb = bb - cb;
+ } break;
+ case 3: {+ cr = br + (0.25f * cr);
+ cg = bg + (0.25f * cg);
+ cb = bb + (0.25f * cb);
+ } break;
+ }
+
+ cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
+ cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
+ cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
+
+ uint32_t rgb = cr | (cg << 8) | (cb << 16);
+
+ color = BGR555(rgb);
+ }
+
+ VRAM(x, y) = color;
+
+ skip:
+
+ ++xc;
+ }
+
+ xc = 0;
+
+ ++yc;
+ }
+}
+
+void plotLineLow(psx_gpu_t* gpu, int x0, int y0, int x1, int y1, uint16_t color) {+ int dx = x1 - x0;
+ int dy = y1 - y0;
+ int yi = 1;
+ if (dy < 0) {+ yi = -1;
+ dy = -dy;
+ }
+ int d = (2 * dy) - dx;
+ int y = y0;
+
+ for (int x = x0; x < x1; x++) {+ if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
+ VRAM(x, y) = color;
+
+ if (d > 0) {+ y += yi;
+ d += (2 * (dy - dx));
+ } else {+ d += 2*dy;
+ }
+ }
+}
+
+void plotLineHigh(psx_gpu_t* gpu, int x0, int y0, int x1, int y1, uint16_t color) {+ int dx = x1 - x0;
+ int dy = y1 - y0;
+ int xi = 1;
+ if (dx < 0) {+ xi = -1;
+ dx = -dx;
+ }
+ int d = (2 * dx) - dy;
+ int x = x0;
+
+ for (int y = y0; y < y1; y++) {+ if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
+ VRAM(x, y) = color;
+
+ if (d > 0) {+ x = x + xi;
+ d += (2 * (dx - dy));
+ } else {+ d += 2*dx;
+ }
+ }
+}
+
+void plotLine(psx_gpu_t* gpu, int x0, int y0, int x1, int y1, uint16_t color) {+ if (abs(y1 - y0) < abs(x1 - x0)) {+ if (x0 > x1) {+ plotLineLow(gpu, x1, y1, x0, y0, color);
+ } else {+ plotLineLow(gpu, x0, y0, x1, y1, color);
+ }
+ } else {+ if (y0 > y1) {+ plotLineHigh(gpu, x1, y1, x0, y0, color);
+ } else {+ plotLineHigh(gpu, x0, y0, x1, y1, color);
+ }
+ }
+}
+
+void gpu_render_flat_line(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, uint32_t color) {+ plotLine(gpu, v0.x, v0.y, v1.x, v1.y, color);
+}
+
+void gpu_render_flat_rectangle(psx_gpu_t* gpu, vertex_t v, uint32_t w, uint32_t h, uint32_t color) {+ /* Offset coordinates */
+ v.x += gpu->off_x;
+ v.y += gpu->off_y;
+
+ /* Calculate bounding box */
+ int xmin = max(v.x, gpu->draw_x1);
+ int ymin = max(v.y, gpu->draw_y1);
+ int xmax = min(xmin + w, gpu->draw_x2);
+ int ymax = min(ymin + h, gpu->draw_y2);
+
+ for (uint32_t y = ymin; y < ymax; y++)
+ for (uint32_t x = xmin; x < xmax; x++)
+ VRAM(x, y) = color;
+}
+
+void gpu_render_textured_rectangle(psx_gpu_t* gpu, vertex_t v, uint32_t w, uint32_t h, uint16_t clutx, uint16_t cluty, uint32_t color) {+ vertex_t a = v;
+
+ a.x += gpu->off_x;
+ a.y += gpu->off_y;
+
+ int xmin = max(a.x, gpu->draw_x1);
+ int ymin = max(a.y, gpu->draw_y1);
+ int xmax = min(xmin + w, gpu->draw_x2);
+ int ymax = min(ymin + h, gpu->draw_y2);
+
+ uint32_t xc = 0, yc = 0;
+
+ for (int y = ymin; y < ymax; y++) {+ for (int x = xmin; x < xmax; x++) {+ uint16_t texel = gpu_fetch_texel(
+ gpu,
+ a.tx + xc, a.ty + yc,
+ gpu->texp_x, gpu->texp_y,
+ clutx, cluty,
+ gpu->texp_d
+ );
+
+ ++xc;
+
+ gpu->vram[x + (y * 1024)] = texel;
+ }
+
+ xc = 0;
+
+ ++yc;
+ }
+}
+
+void gpu_render_flat_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, uint32_t color) {+ vertex_t a, b, c;
+
+ a = v0;
+
+ /* Ensure the winding order is correct */
+ if (EDGE(v0, v1, v2) < 0) {+ b = v2;
+ c = v1;
+ } else {+ b = v1;
+ c = v2;
+ }
+
+ a.x += gpu->off_x;
+ a.y += gpu->off_y;
+ b.x += gpu->off_x;
+ b.y += gpu->off_y;
+ c.x += gpu->off_x;
+ c.y += gpu->off_y;
+
+ int xmin = max(min(min(a.x, b.x), c.x), gpu->draw_x1);
+ int ymin = max(min(min(a.y, b.y), c.y), gpu->draw_y1);
+ int xmax = min(max(max(a.x, b.x), c.x), gpu->draw_x2);
+ int ymax = min(max(max(a.y, b.y), c.y), gpu->draw_y2);
+
+ for (int y = ymin; y < ymax; y++) {+ for (int x = xmin; x < xmax; x++) {+ int z0 = ((b.x - a.x) * (y - a.y)) - ((b.y - a.y) * (x - a.x));
+ int z1 = ((c.x - b.x) * (y - b.y)) - ((c.y - b.y) * (x - b.x));
+ int z2 = ((a.x - c.x) * (y - c.y)) - ((a.y - c.y) * (x - c.x));
+
+ if ((z0 >= 0) && (z1 >= 0) && (z2 >= 0)) {+ gpu->vram[x + (y * 1024)] = BGR555(color);
+ }
+ }
+ }
+}
+
+void gpu_render_shaded_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2) {+ vertex_t a, b, c, p;
+
+ a = v0;
+
+ /* Ensure the winding order is correct */
+ if (EDGE(v0, v1, v2) < 0) {+ b = v2;
+ c = v1;
+ } else {+ b = v1;
+ c = v2;
+ }
+
+ a.x += gpu->off_x;
+ a.y += gpu->off_y;
+ b.x += gpu->off_x;
+ b.y += gpu->off_y;
+ c.x += gpu->off_x;
+ c.y += gpu->off_y;
+
+ int xmin = max(min(min(a.x, b.x), c.x), gpu->draw_x1);
+ int ymin = max(min(min(a.y, b.y), c.y), gpu->draw_y1);
+ int xmax = min(max(max(a.x, b.x), c.x), gpu->draw_x2);
+ int ymax = min(max(max(a.y, b.y), c.y), gpu->draw_y2);
+
+ int area = EDGE(a, b, c);
+
+ for (int y = ymin; y < ymax; y++) {+ for (int x = xmin; x < xmax; x++) {+ p.x = x;
+ p.y = y;
+
+ float z0 = EDGE((float)b, (float)c, (float)p);
+ float z1 = EDGE((float)c, (float)a, (float)p);
+ float z2 = EDGE((float)a, (float)b, (float)p);
+
+ if ((z0 >= 0) && (z1 >= 0) && (z2 >= 0)) {+ int cr = (z0 * ((a.c >> 0) & 0xff) + z1 * ((b.c >> 0) & 0xff) + z2 * ((c.c >> 0) & 0xff)) / area;
+ int cg = (z0 * ((a.c >> 8) & 0xff) + z1 * ((b.c >> 8) & 0xff) + z2 * ((c.c >> 8) & 0xff)) / area;
+ int cb = (z0 * ((a.c >> 16) & 0xff) + z1 * ((b.c >> 16) & 0xff) + z2 * ((c.c >> 16) & 0xff)) / area;
+
+ // Calculate positions within our 4x4 dither
+ // kernel
+ int dy = (y - ymin) % 4;
+ int dx = (x - xmin) % 4;
+
+ // Shift two pixels horizontally on the last
+ // two scanlines?
+ // if (dy > 1) {+ // dx = ((x + 2) - xmin) % 4;
+ // }
+
+ int dither = g_psx_gpu_dither_kernel[dx + (dy * 4)];
+
+ // Add to the original 8-bit color values
+ cr += dither;
+ cg += dither;
+ cb += dither;
+
+ // Saturate (clamp) to 00-ff
+ cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
+ cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
+ cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
+
+ uint32_t color = (cb << 16) | (cg << 8) | cr;
+
+ gpu->vram[x + (y * 1024)] = BGR555(color);
+ }
+ }
+ }
+}
+
+void gpu_render_textured_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, uint32_t tpx, uint32_t tpy, uint16_t clutx, uint16_t cluty, int depth) {+ vertex_t a, b, c;
+
+ a = v0;
+
+ /* Ensure the winding order is correct */
+ if (EDGE(v0, v1, v2) < 0) {+ b = v2;
+ c = v1;
+ } else {+ b = v1;
+ c = v2;
+ }
+
+ a.x += gpu->off_x;
+ a.y += gpu->off_y;
+ b.x += gpu->off_x;
+ b.y += gpu->off_y;
+ c.x += gpu->off_x;
+ c.y += gpu->off_y;
+
+ int xmin = max(min(min(a.x, b.x), c.x), gpu->draw_x1);
+ int ymin = max(min(min(a.y, b.y), c.y), gpu->draw_y1);
+ int xmax = min(max(max(a.x, b.x), c.x), gpu->draw_x2);
+ int ymax = min(max(max(a.y, b.y), c.y), gpu->draw_y2);
+
+ uint32_t area = EDGE(a, b, c);
+
+ for (int y = ymin; y < ymax; y++) {+ for (int x = xmin; x < xmax; x++) {+ vertex_t p;
+
+ p.x = x;
+ p.y = y;
+
+ float z0 = EDGE((float)b, (float)c, (float)p);
+ float z1 = EDGE((float)c, (float)a, (float)p);
+ float z2 = EDGE((float)a, (float)b, (float)p);
+
+ if ((z0 >= 0) && (z1 >= 0) && (z2 >= 0)) {+ uint32_t tx = ((z0 * a.tx) + (z1 * b.tx) + (z2 * c.tx)) / area;
+ uint32_t ty = ((z0 * a.ty) + (z1 * b.ty) + (z2 * c.ty)) / area;
+
+ uint16_t color = gpu_fetch_texel(
+ gpu,
+ tx, ty,
+ tpx, tpy,
+ clutx, cluty,
+ depth
+ );
+
+ if (!color) continue;
+
+ gpu->vram[x + (y * 1024)] = color;
+ }
+ }
+ }
+}
+
+void gpu_rect(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+
+ int size = (gpu->buf[0] >> 27) & 3;
+ int textured = (gpu->buf[0] & 0x04000000) != 0;
+
+ gpu->cmd_args_remaining = 1 + (size == RS_VARIABLE) + textured;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ rect_data_t rect;
+
+ rect.attrib = gpu->buf[0] >> 24;
+
+ int textured = (rect.attrib & RA_TEXTURED) != 0;
+ int raw = (rect.attrib & RA_RAW) != 0;
+
+ // Add 1 if is textured
+ int size_offset = 2 + textured;
+
+ rect.v0.c = gpu->buf[0] & 0xffffff;
+ rect.v0.x = gpu->buf[1] & 0xffff;
+ rect.v0.y = gpu->buf[1] >> 16;
+ rect.v0.tx = (gpu->buf[2] >> 0) & 0xff;
+ rect.v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ rect.clut = gpu->buf[2] >> 16;
+ rect.width = gpu->buf[size_offset] & 0xffff;
+ rect.height = gpu->buf[size_offset] >> 16;
+
+ if (textured && raw)
+ rect.v0.c = 0x808080;
+
+ gpu_render_rect(gpu, rect);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_poly(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+
+ int shaded = (gpu->buf[0] & 0x10000000) != 0;
+ int quad = (gpu->buf[0] & 0x08000000) != 0;
+ int textured = (gpu->buf[0] & 0x04000000) != 0;
+
+ int fields_per_vertex = 1 + shaded + textured;
+ int vertices = 3 + quad;
+
+ gpu->cmd_args_remaining = (fields_per_vertex * vertices) - shaded;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ poly_data_t poly;
+
+ poly.attrib = gpu->buf[0] >> 24;
+
+ int shaded = (poly.attrib & PA_SHADED) != 0;
+ int textured = (poly.attrib & PA_TEXTURED) != 0;
+
+ int color_offset = shaded * (2 + textured);
+ int vert_offset = 1 + (textured | shaded) +
+ (textured & shaded);
+ int texc_offset = textured * (2 + shaded);
+ int texp_offset = textured * (4 + shaded);
+
+ poly.clut = gpu->buf[2] >> 16;
+ poly.texp = gpu->buf[texp_offset] >> 16;
+
+ poly.v[0].c = gpu->buf[0+0*color_offset] & 0xffffff;
+ poly.v[1].c = gpu->buf[0+1*color_offset] & 0xffffff;
+ poly.v[2].c = gpu->buf[0+2*color_offset] & 0xffffff;
+ poly.v[3].c = gpu->buf[0+3*color_offset] & 0xffffff;
+ poly.v[0].x = gpu->buf[1+0*vert_offset] & 0xffff;
+ poly.v[1].x = gpu->buf[1+1*vert_offset] & 0xffff;
+ poly.v[2].x = gpu->buf[1+2*vert_offset] & 0xffff;
+ poly.v[3].x = gpu->buf[1+3*vert_offset] & 0xffff;
+ poly.v[0].y = gpu->buf[1+0*vert_offset] >> 16;
+ poly.v[1].y = gpu->buf[1+1*vert_offset] >> 16;
+ poly.v[2].y = gpu->buf[1+2*vert_offset] >> 16;
+ poly.v[3].y = gpu->buf[1+3*vert_offset] >> 16;
+ poly.v[0].tx = gpu->buf[2+0*texc_offset] & 0xff;
+ poly.v[1].tx = gpu->buf[2+1*texc_offset] & 0xff;
+ poly.v[2].tx = gpu->buf[2+2*texc_offset] & 0xff;
+ poly.v[3].tx = gpu->buf[2+3*texc_offset] & 0xff;
+ poly.v[0].ty = (gpu->buf[2+0*texc_offset] >> 8) & 0xff;
+ poly.v[1].ty = (gpu->buf[2+1*texc_offset] >> 8) & 0xff;
+ poly.v[2].ty = (gpu->buf[2+2*texc_offset] >> 8) & 0xff;
+ poly.v[3].ty = (gpu->buf[2+3*texc_offset] >> 8) & 0xff;
+
+ if (poly.attrib & PA_QUAD) {+ gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly);
+ gpu_render_triangle(gpu, poly.v[1], poly.v[2], poly.v[3], poly);
+ } else {+ gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly);
+ }
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_copy(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 3;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ uint32_t srcx = gpu->buf[1] & 0xffff;
+ uint32_t srcy = gpu->buf[1] >> 16;
+ uint32_t dstx = gpu->buf[2] & 0xffff;
+ uint32_t dsty = gpu->buf[2] >> 16;
+ uint32_t xsiz = gpu->buf[3] & 0xffff;
+ uint32_t ysiz = gpu->buf[3] >> 16;
+
+ for (int y = 0; y < ysiz; y++)
+ for (int x = 0; x < xsiz; x++)
+ VRAM(dstx + x, dsty + y) = VRAM(srcx + x, srcy + y);
+ }
+ } break;
+ }
+}
+
+void gpu_recv(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ // Save static data
+ gpu->xpos = gpu->buf[1] & 0x3ff;
+ gpu->ypos = (gpu->buf[1] >> 16) & 0x1ff;
+ gpu->xsiz = gpu->buf[2] & 0xffff;
+ gpu->ysiz = gpu->buf[2] >> 16;
+ gpu->xsiz = ((gpu->xsiz - 1) & 0x3ff) + 1;
+ gpu->ysiz = ((gpu->ysiz - 1) & 0x1ff) + 1;
+ gpu->tsiz = ((gpu->xsiz * gpu->ysiz) + 1) & 0xfffffffe;
+ gpu->addr = gpu->xpos + (gpu->ypos * 1024);
+ gpu->xcnt = 0;
+ gpu->ycnt = 0;
+ }
+ } break;
+
+ case GPU_STATE_RECV_DATA: {+ unsigned int xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
+ unsigned int ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
+
+ // To-do: This is segfaulting for some reason
+ // Fix GPU edge cases in general
+ VRAM(xpos, ypos) = gpu->recv_data & 0xffff;
+
+ ++gpu->xcnt;
+
+ xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
+ ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
+
+ if (gpu->xcnt == gpu->xsiz) {+ ++gpu->ycnt;
+ gpu->xcnt = 0;
+
+ ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
+ xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
+ }
+
+ VRAM(xpos, ypos) = gpu->recv_data >> 16;
+
+ ++gpu->xcnt;
+
+ if (gpu->xcnt == gpu->xsiz) {+ ++gpu->ycnt;
+ gpu->xcnt = 0;
+
+ xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
+ ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
+ }
+
+ gpu->tsiz -= 2;
+
+ if (!gpu->tsiz) {+ gpu->xcnt = 0;
+ gpu->ycnt = 0;
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_send(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->c0_xcnt = 0;
+ gpu->c0_ycnt = 0;
+ uint32_t c0_xpos = gpu->buf[1] & 0xffff;
+ uint32_t c0_ypos = gpu->buf[1] >> 16;
+ gpu->c0_xsiz = gpu->buf[2] & 0xffff;
+ gpu->c0_ysiz = gpu->buf[2] >> 16;
+ c0_xpos = c0_xpos & 0x3ff;
+ c0_ypos = c0_ypos & 0x1ff;
+ gpu->c0_xsiz = ((gpu->c0_xsiz - 1) & 0x3ff) + 1;
+ gpu->c0_ysiz = ((gpu->c0_ysiz - 1) & 0x1ff) + 1;
+ gpu->c0_tsiz = ((gpu->c0_xsiz * gpu->c0_ysiz) + 1) & 0xfffffffe;
+ gpu->c0_addr = c0_xpos + (c0_ypos * 1024);
+
+ printf("c0addr=%08x c0xcnt=%u c0ycnt=%u\n",+ gpu->c0_addr,
+ gpu->c0_xcnt,
+ gpu->c0_ycnt
+ );
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_28(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 4;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.x = gpu->buf[2] & 0xffff;
+ gpu->v1.y = gpu->buf[2] >> 16;
+ gpu->v2.x = gpu->buf[3] & 0xffff;
+ gpu->v2.y = gpu->buf[3] >> 16;
+ gpu->v3.x = gpu->buf[4] & 0xffff;
+ gpu->v3.y = gpu->buf[4] >> 16;
+ gpu->color = gpu->buf[0] & 0xffffff;
+
+ gpu_render_flat_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, gpu->color);
+ gpu_render_flat_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, gpu->color);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_30(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 5;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->v0.c = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.c = gpu->buf[2] & 0xffffff;
+ gpu->v1.x = gpu->buf[3] & 0xffff;
+ gpu->v1.y = gpu->buf[3] >> 16;
+ gpu->v2.c = gpu->buf[4] & 0xffffff;
+ gpu->v2.x = gpu->buf[5] & 0xffff;
+ gpu->v2.y = gpu->buf[5] >> 16;
+
+ gpu_render_shaded_triangle(gpu, gpu->v0, gpu->v1, gpu->v2);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_38(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 7;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->v0.c = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.c = gpu->buf[2] & 0xffffff;
+ gpu->v1.x = gpu->buf[3] & 0xffff;
+ gpu->v1.y = gpu->buf[3] >> 16;
+ gpu->v2.c = gpu->buf[4] & 0xffffff;
+ gpu->v2.x = gpu->buf[5] & 0xffff;
+ gpu->v2.y = gpu->buf[5] >> 16;
+ gpu->v3.c = gpu->buf[6] & 0xffffff;
+ gpu->v3.x = gpu->buf[7] & 0xffff;
+ gpu->v3.y = gpu->buf[7] >> 16;
+
+ gpu_render_shaded_triangle(gpu, gpu->v0, gpu->v1, gpu->v2);
+ gpu_render_shaded_triangle(gpu, gpu->v1, gpu->v2, gpu->v3);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_3c(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 11;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ uint32_t texp = gpu->buf[5] >> 16;
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->pal = gpu->buf[2] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->v1.tx = gpu->buf[5] & 0xff;
+ gpu->v1.ty = (gpu->buf[5] >> 8) & 0xff;
+ gpu->v2.tx = gpu->buf[8] & 0xff;
+ gpu->v2.ty = (gpu->buf[8] >> 8) & 0xff;
+ gpu->v3.tx = gpu->buf[11] & 0xff;
+ gpu->v3.ty = (gpu->buf[11] >> 8) & 0xff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.x = gpu->buf[4] & 0xffff;
+ gpu->v1.y = gpu->buf[4] >> 16;
+ gpu->v2.x = gpu->buf[7] & 0xffff;
+ gpu->v2.y = gpu->buf[7] >> 16;
+ gpu->v3.x = gpu->buf[10] & 0xffff;
+ gpu->v3.y = gpu->buf[10] >> 16;
+
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+ uint16_t tpx = (texp & 0xf) << 6;
+ uint16_t tpy = (texp & 0x10) << 4;
+ uint16_t depth = (texp >> 7) & 0x3;
+
+ gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
+ gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_2c(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 8;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ uint32_t texp = gpu->buf[4] >> 16;
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->pal = gpu->buf[2] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->v1.tx = gpu->buf[4] & 0xff;
+ gpu->v1.ty = (gpu->buf[4] >> 8) & 0xff;
+ gpu->v2.tx = gpu->buf[6] & 0xff;
+ gpu->v2.ty = (gpu->buf[6] >> 8) & 0xff;
+ gpu->v3.tx = gpu->buf[8] & 0xff;
+ gpu->v3.ty = (gpu->buf[8] >> 8) & 0xff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.x = gpu->buf[3] & 0xffff;
+ gpu->v1.y = gpu->buf[3] >> 16;
+ gpu->v2.x = gpu->buf[5] & 0xffff;
+ gpu->v2.y = gpu->buf[5] >> 16;
+ gpu->v3.x = gpu->buf[7] & 0xffff;
+ gpu->v3.y = gpu->buf[7] >> 16;
+
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+ uint16_t tpx = (texp & 0xf) << 6;
+ uint16_t tpy = (texp & 0x10) << 4;
+ uint16_t depth = (texp >> 7) & 0x3;
+
+ gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
+ gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_24(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 6;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ uint32_t texp = gpu->buf[4] >> 16;
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->pal = gpu->buf[2] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->v1.tx = gpu->buf[4] & 0xff;
+ gpu->v1.ty = (gpu->buf[4] >> 8) & 0xff;
+ gpu->v2.tx = gpu->buf[6] & 0xff;
+ gpu->v2.ty = (gpu->buf[6] >> 8) & 0xff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.x = gpu->buf[3] & 0xffff;
+ gpu->v1.y = gpu->buf[3] >> 16;
+ gpu->v2.x = gpu->buf[5] & 0xffff;
+ gpu->v2.y = gpu->buf[5] >> 16;
+
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+ uint16_t tpx = (texp & 0xf) << 6;
+ uint16_t tpy = (texp & 0x10) << 4;
+ uint16_t depth = (texp >> 7) & 0x3;
+
+ gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
+ gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+// Monochrome Opaque Quadrilateral
+void gpu_cmd_2d(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 8;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ uint32_t texp = gpu->buf[4] >> 16;
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->pal = gpu->buf[2] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->v1.tx = gpu->buf[4] & 0xff;
+ gpu->v1.ty = (gpu->buf[4] >> 8) & 0xff;
+ gpu->v2.tx = gpu->buf[6] & 0xff;
+ gpu->v2.ty = (gpu->buf[6] >> 8) & 0xff;
+ gpu->v3.tx = gpu->buf[8] & 0xff;
+ gpu->v3.ty = (gpu->buf[8] >> 8) & 0xff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.x = gpu->buf[3] & 0xffff;
+ gpu->v1.y = gpu->buf[3] >> 16;
+ gpu->v2.x = gpu->buf[5] & 0xffff;
+ gpu->v2.y = gpu->buf[5] >> 16;
+ gpu->v3.x = gpu->buf[7] & 0xffff;
+ gpu->v3.y = gpu->buf[7] >> 16;
+
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+ uint16_t tpx = (texp & 0xf) << 6;
+ uint16_t tpy = (texp & 0x10) << 4;
+ uint16_t depth = (texp >> 7) & 0x3;
+
+ gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
+ gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_64(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 3;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->pal = gpu->buf[2] >> 16;
+
+ uint32_t w = gpu->buf[3] & 0xffff;
+ uint32_t h = gpu->buf[3] >> 16;
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+
+ gpu_render_textured_rectangle(gpu, gpu->v0, w, h, clutx, cluty, gpu->color);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_7c(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->pal = gpu->buf[2] >> 16;
+
+ uint32_t w = 16;
+ uint32_t h = 16;
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+
+ gpu_render_textured_rectangle(gpu, gpu->v0, w, h, clutx, cluty, gpu->color);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_74(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v0.tx = gpu->buf[2] & 0xff;
+ gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
+ gpu->pal = gpu->buf[2] >> 16;
+
+ uint32_t w = 8;
+ uint32_t h = 8;
+ uint16_t clutx = (gpu->pal & 0x3f) << 4;
+ uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
+
+ gpu_render_textured_rectangle(gpu, gpu->v0, w, h, clutx, cluty, gpu->color);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_60(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->xsiz = gpu->buf[2] & 0xffff;
+ gpu->ysiz = gpu->buf[2] >> 16;
+
+ gpu->v0.x += gpu->off_x;
+ gpu->v0.y += gpu->off_y;
+
+ gpu_render_flat_rectangle(gpu, gpu->v0, gpu->xsiz, gpu->ysiz, BGR555(gpu->color));
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_68(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 1;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+
+ gpu->v0.x += gpu->off_x;
+ gpu->v0.y += gpu->off_y;
+
+ gpu->vram[gpu->v0.x + (gpu->v0.y * 1024)] = BGR555(gpu->color);
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_40(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->v1.x = gpu->buf[2] & 0xffff;
+ gpu->v1.y = gpu->buf[2] >> 16;
+
+ gpu_render_flat_line(gpu, gpu->v0, gpu->v1, BGR555(gpu->color));
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_02(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 2;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ gpu->color = gpu->buf[0] & 0xffffff;
+ gpu->v0.x = gpu->buf[1] & 0xffff;
+ gpu->v0.y = gpu->buf[1] >> 16;
+ gpu->xsiz = gpu->buf[2] & 0xffff;
+ gpu->ysiz = gpu->buf[2] >> 16;
+
+ gpu->v0.x = (gpu->v0.x & 0x3f0);
+ gpu->v0.y = gpu->v0.y & 0x1ff;
+ gpu->xsiz = (((gpu->xsiz & 0x3ff) + 0x0f) & 0xfffffff0);
+ gpu->ysiz = gpu->ysiz & 0x1ff;
+
+ uint16_t color = BGR555(gpu->color);
+
+ for (uint32_t y = gpu->v0.y; y < (gpu->v0.y + gpu->ysiz); y++) {+ for (uint32_t x = gpu->v0.x; x < (gpu->v0.x + gpu->xsiz); x++) {+ if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
+ VRAM(x, y) = color;
+ }
+ }
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void gpu_cmd_80(psx_gpu_t* gpu) {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->state = GPU_STATE_RECV_ARGS;
+ gpu->cmd_args_remaining = 3;
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ if (!gpu->cmd_args_remaining) {+ gpu->state = GPU_STATE_RECV_DATA;
+
+ uint32_t srcx = gpu->buf[1] & 0xffff;
+ uint32_t srcy = gpu->buf[1] >> 16;
+ uint32_t dstx = gpu->buf[2] & 0xffff;
+ uint32_t dsty = gpu->buf[2] >> 16;
+ uint32_t xsiz = gpu->buf[3] & 0xffff;
+ uint32_t ysiz = gpu->buf[3] >> 16;
+
+ for (int y = 0; y < ysiz; y++) {+ for (int x = 0; x < xsiz; x++) {+ if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
+ gpu->vram[(dstx + x) + (dsty + y) * 1024] = gpu->vram[(srcx + x) + (srcy + y) * 1024];
+ }
+ }
+
+ gpu->state = GPU_STATE_RECV_CMD;
+ }
+ } break;
+ }
+}
+
+void psx_gpu_update_cmd(psx_gpu_t* gpu) {+ int type = (gpu->buf[0] >> 29) & 7;
+
+ switch (type) {+ case 1: gpu_poly(gpu); return;
+ case 3: gpu_rect(gpu); return;
+ case 4: gpu_copy(gpu); return;
+ case 5: gpu_recv(gpu); return;
+ case 6: gpu_send(gpu); return;
+ default: break;
+ }
+
+ switch (gpu->buf[0] >> 24) {+ case 0x00: /* nop */ break;
+ case 0x01: /* Cache clear */ break;
+ case 0x02: gpu_cmd_02(gpu); break;
+ // case 0x24: gpu_cmd_24(gpu); break;
+ // case 0x25: gpu_cmd_24(gpu); break;
+ // case 0x26: gpu_cmd_24(gpu); break;
+ // case 0x27: gpu_cmd_24(gpu); break;
+ // case 0x28: gpu_cmd_28(gpu); break;
+ // case 0x2a: gpu_cmd_28(gpu); break;
+ // case 0x2c: gpu_cmd_2d(gpu); break;
+ // case 0x2d: gpu_cmd_2d(gpu); break;
+ // case 0x2e: gpu_cmd_2d(gpu); break;
+ // case 0x2f: gpu_cmd_2d(gpu); break;
+ // case 0x30: gpu_cmd_30(gpu); break;
+ // case 0x32: gpu_cmd_30(gpu); break;
+ // case 0x38: gpu_cmd_38(gpu); break;
+ // case 0x3c: gpu_cmd_3c(gpu); break;
+ // case 0x3e: gpu_cmd_3c(gpu); break;
+ case 0x40: gpu_cmd_40(gpu); break;
+ // case 0x60: gpu_cmd_60(gpu); break;
+ // case 0x62: gpu_cmd_60(gpu); break;
+ // case 0x64: gpu_cmd_64(gpu); break;
+ // case 0x65: gpu_cmd_64(gpu); break;
+ // case 0x66: gpu_cmd_64(gpu); break;
+ // case 0x67: gpu_cmd_64(gpu); break;
+ // case 0x68: gpu_cmd_68(gpu); break;
+ // case 0x74: gpu_cmd_74(gpu); break;
+ // case 0x75: gpu_cmd_74(gpu); break;
+ // case 0x76: gpu_cmd_74(gpu); break;
+ // case 0x77: gpu_cmd_74(gpu); break;
+ // case 0x7c: gpu_cmd_7c(gpu); break;
+ // case 0x7d: gpu_cmd_7c(gpu); break;
+ // case 0x7e: gpu_cmd_7c(gpu); break;
+ // case 0x7f: gpu_cmd_7c(gpu); break;
+ // case 0x80: gpu_cmd_80(gpu); break;
+ // case 0xa0: gpu_cmd_a0(gpu); break;
+ // case 0xc0: gpu_cmd_c0(gpu); break;
+ case 0xe1: {+ gpu->gpustat &= 0xfffff800;
+ gpu->gpustat |= gpu->buf[0] & 0x7ff;
+ gpu->texp_x = (gpu->gpustat & 0xf) << 6;
+ gpu->texp_y = (gpu->gpustat & 0x10) << 4;
+ gpu->texp_d = (gpu->gpustat >> 7) & 0x3;
+ } break;
+ case 0xe2: {+ gpu->texw_mx = (gpu->buf[0] >> 0 ) & 0x1f;
+ gpu->texw_my = (gpu->buf[0] >> 5 ) & 0x1f;
+ gpu->texw_ox = (gpu->buf[0] >> 10) & 0x1f;
+ gpu->texw_oy = (gpu->buf[0] >> 15) & 0x1f;
+ } break;
+ case 0xe3: {+ gpu->draw_x1 = (gpu->buf[0] >> 0 ) & 0x3ff;
+ gpu->draw_y1 = (gpu->buf[0] >> 10) & 0x1ff;
+ } break;
+ case 0xe4: {+ gpu->draw_x2 = (gpu->buf[0] >> 0 ) & 0x3ff;
+ gpu->draw_y2 = (gpu->buf[0] >> 10) & 0x1ff;
+ } break;
+ case 0xe5: {+ gpu->off_x = (gpu->buf[0] >> 0 ) & 0x7ff;
+ gpu->off_y = (gpu->buf[0] >> 11) & 0x7ff;
+ } break;
+ case 0xe6: {+ /* To-do: Implement mask bit thing */
+ } break;
+ default: {+ // log_set_quiet(0);
+ // log_fatal("Unhandled GP0(%02Xh)", gpu->buf[0] >> 24);+ log_set_quiet(1);
+
+ // exit(1);
+ } break;
+ }
+}
+
+void psx_gpu_write32(psx_gpu_t* gpu, uint32_t offset, uint32_t value) {+ switch (offset) {+ // GP0
+ case 0x00: {+ switch (gpu->state) {+ case GPU_STATE_RECV_CMD: {+ gpu->buf_index = 0;
+ gpu->buf[gpu->buf_index++] = value;
+
+ psx_gpu_update_cmd(gpu);
+ } break;
+
+ case GPU_STATE_RECV_ARGS: {+ gpu->buf[gpu->buf_index++] = value;
+ gpu->cmd_args_remaining--;
+
+ psx_gpu_update_cmd(gpu);
+ } break;
+
+ case GPU_STATE_RECV_DATA: {+ gpu->recv_data = value;
+
+ psx_gpu_update_cmd(gpu);
+ } break;
+ }
+
+ return;
+ } break;
+
+ // GP1
+ case 0x04: {+ uint8_t cmd = value >> 24;
+
+ switch (cmd) {+ case 0x00: {+ gpu->gpustat = 0x14802000;
+
+ /*
+ GP1(01h) ;clear fifo
+ GP1(02h) ;ack irq (0)
+ GP1(03h) ;display off (1)
+ GP1(04h) ;dma off (0)
+ GP1(05h) ;display address (0)
+ GP1(06h) ;display x1,x2 (x1=200h, x2=200h+256*10)
+ GP1(07h) ;display y1,y2 (y1=010h, y2=010h+240)
+ GP1(08h) ;display mode 320x200 NTSC (0)
+ GP0(E1h..E6h) ;rendering attributes (0)
+ */
+
+ gpu->disp_x1 = 0x200;
+ gpu->disp_x2 = 0xc00;
+ gpu->disp_y1 = 0x010;
+ gpu->disp_y2 = 0x100;
+ gpu->display_mode = 0;
+
+ gpu->disp_x = 0;
+ gpu->disp_y = 0;
+
+ if (gpu->event_cb_table[GPU_EVENT_DMODE])
+ gpu->event_cb_table[GPU_EVENT_DMODE](gpu);
+ } break;
+ case 0x04: {+ } break;
+ case 0x05: {+ gpu->disp_x = value & 0x3ff;
+ gpu->disp_y = (value >> 10) & 0x1ff;
+ } break;
+ case 0x06: {+ gpu->disp_x1 = value & 0xfff;
+ gpu->disp_x2 = (value >> 12) & 0xfff;
+ } break;
+ case 0x08:
+ gpu->display_mode = value & 0xffffff;
+
+ if (gpu->event_cb_table[GPU_EVENT_DMODE])
+ gpu->event_cb_table[GPU_EVENT_DMODE](gpu);
+ break;
+
+ case 0x10: {+ gpu->gp1_10h_req = value & 7;
+ } break;
+ }
+
+ log_error("GP1(%02Xh) args=%06x", value >> 24, value & 0xffffff);+
+ return;
+ } break;
+ }
+
+ log_warn("Unhandled 32-bit GPU write at offset %08x (%08x)", offset, value);+}
+
+void psx_gpu_write16(psx_gpu_t* gpu, uint32_t offset, uint16_t value) {+ log_warn("Unhandled 16-bit GPU write at offset %08x (%04x)", offset, value);+}
+
+void psx_gpu_write8(psx_gpu_t* gpu, uint32_t offset, uint8_t value) {+ log_warn("Unhandled 8-bit GPU write at offset %08x (%02x)", offset, value);+}
+
+void psx_gpu_set_event_callback(psx_gpu_t* gpu, int event, psx_gpu_event_callback_t cb) {+ gpu->event_cb_table[event] = cb;
+}
+
+void psx_gpu_set_udata(psx_gpu_t* gpu, int index, void* udata) {+ gpu->udata[index] = udata;
+}
+
+#define GPU_CYCLES_PER_HDRAW_NTSC 2560.0f
+#define GPU_CYCLES_PER_SCANL_NTSC 3413.0f
+#define GPU_SCANS_PER_VDRAW_NTSC 240
+#define GPU_SCANS_PER_FRAME_NTSC 263
+#define GPU_CYCLES_PER_SCANL_PAL 3406.0f
+#define GPU_SCANS_PER_FRAME_PAL 314
+
+void gpu_hblank_event(psx_gpu_t* gpu) {+ gpu->line++;
+
+ if (gpu->line < GPU_SCANS_PER_VDRAW_NTSC) {+ if (gpu->line & 1) {+ gpu->gpustat |= 1 << 31;
+ } else {+ gpu->gpustat &= ~(1 << 31);
+ }
+ } else {+ gpu->gpustat &= ~(1 << 31);
+ }
+
+ if (gpu->line == GPU_SCANS_PER_VDRAW_NTSC) {+ if (gpu->event_cb_table[GPU_EVENT_VBLANK])
+ gpu->event_cb_table[GPU_EVENT_VBLANK](gpu);
+
+ psx_ic_irq(gpu->ic, IC_VBLANK);
+ } else if (gpu->line == GPU_SCANS_PER_FRAME_NTSC) {+ if (gpu->event_cb_table[GPU_EVENT_VBLANK_END])
+ gpu->event_cb_table[GPU_EVENT_VBLANK_END](gpu);
+
+ // psx_ic_irq(gpu->ic, IC_SPU);
+
+ gpu->line = 0;
+ }
+}
+
+void psx_gpu_update(psx_gpu_t* gpu, int cyc) {+ int prev_hblank = (gpu->cycles >= GPU_CYCLES_PER_HDRAW_NTSC) &&
+ (gpu->cycles <= GPU_CYCLES_PER_SCANL_NTSC);
+
+ // Convert CPU (~33.8 MHz) cycles to GPU (~53.7 MHz) cycles
+ // gpu->cycles += (float)cyc * (PSX_GPU_CLOCK_FREQ_NTSC / PSX_CPU_FREQ);
+ gpu->cycles += (float)cyc * (11.0f / 7.0f);
+
+ int curr_hblank = (gpu->cycles >= GPU_CYCLES_PER_HDRAW_NTSC) &&
+ (gpu->cycles <= GPU_CYCLES_PER_SCANL_NTSC);
+
+ if (curr_hblank && !prev_hblank) {+ if (gpu->event_cb_table[GPU_EVENT_HBLANK])
+ gpu->event_cb_table[GPU_EVENT_HBLANK](gpu);
+
+ gpu_hblank_event(gpu);
+ } else if (prev_hblank && !curr_hblank) {+ if (gpu->event_cb_table[GPU_EVENT_HBLANK_END])
+ gpu->event_cb_table[GPU_EVENT_HBLANK_END](gpu);
+
+ gpu->cycles -= (float)GPU_CYCLES_PER_SCANL_NTSC;
+ }
+}
+
+void* psx_gpu_get_display_buffer(psx_gpu_t* gpu) {+ return gpu->vram + (gpu->disp_x + (gpu->disp_y * 1024));
+}
+
+void psx_gpu_destroy(psx_gpu_t* gpu) {+ free(gpu->vram);
+ free(gpu);
+}
\ No newline at end of file
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -427,6 +427,7 @@
}
void psx_spu_destroy(psx_spu_t* spu) {+ printf("psx_spu_destroy called\n");free(spu->ram);
free(spu);
}
@@ -565,16 +566,21 @@
spu->data[v].current_addr = spu->data[v].repeat_addr;
spu->data[v].playing = 0;
spu->voice[v].envcvol = 0;
+
+ adsr_load_release(spu, v);
} break;
case 3: {spu->endx |= (1 << v);
spu->data[v].current_addr = spu->data[v].repeat_addr;
+
+ adsr_load_release(spu, v);
} break;
}
- if (spu->data[v].block_flags & 4)
+ if (spu->data[v].block_flags & 4) {spu->data[v].repeat_addr = spu->data[v].current_addr;
+ }
spu_read_block(spu, v);
}
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -49,8 +49,6 @@
int index = offset >> 4;
int reg = offset & 0xf;
- //log_fatal("Timer %u %s read32", index, g_psx_timer_reg_names[reg]);-
switch (reg) {case 0: return timer->timer[index].counter;
case 4: {@@ -63,7 +61,7 @@
log_fatal("Unhandled 32-bit TIMER read at offset %08x", offset);- exit(1);
+ // exit(1);
return 0x0;
}
@@ -72,8 +70,6 @@
int index = offset >> 4;
int reg = offset & 0xf;
- //log_fatal("Timer %u %s read16 %04x", index, g_psx_timer_reg_names[reg], timer->timer[index].counter);-
switch (reg) {case 0: return timer->timer[index].counter;
case 4: {@@ -86,8 +82,6 @@
log_fatal("Unhandled 16-bit TIMER read at offset %08x", offset);- exit(1);
-
return 0x0;
}
@@ -94,8 +88,6 @@
uint8_t psx_timer_read8(psx_timer_t* timer, uint32_t offset) { log_fatal("Unhandled 8-bit TIMER read at offset %08x", offset);- exit(1);
-
return 0x0;
}
@@ -103,8 +95,6 @@
int index = offset >> 4;
int reg = offset & 0xf;
- //log_fatal("Timer %u %s write32 %08x", index, g_psx_timer_reg_names[reg], value);-
switch (reg) { case 0: {timer->timer[index].counter = value;
@@ -113,6 +103,7 @@
timer->timer[index].mode = value;
timer->timer[index].mode |= 0x400;
timer->timer[index].irq_fired = 0;
+ timer->timer[index].counter = 0;
} return;
case 8: timer->timer[index].target = value; return;
}
@@ -119,17 +110,13 @@
log_fatal("Unhandled 32-bit TIMER write at offset %08x (%02x)", offset, value);- exit(1);
+ // exit(1);
}
void psx_timer_write16(psx_timer_t* timer, uint32_t offset, uint16_t value) {- return;
-
int index = offset >> 4;
int reg = offset & 0xf;
- log_fatal("Timer %u %s write16 %04x", index, g_psx_timer_reg_names[reg], value);-
switch (reg) { case 0: {timer->timer[index].counter = value;
@@ -138,6 +125,7 @@
timer->timer[index].mode = value;
timer->timer[index].mode |= 0x400;
timer->timer[index].irq_fired = 0;
+ timer->timer[index].counter = 0;
} return;
case 8: {timer->timer[index].target = value;
@@ -146,13 +134,11 @@
log_fatal("Unhandled 16-bit TIMER write at offset %08x (%02x)", offset, value);- exit(1);
+ // exit(1);
}
void psx_timer_write8(psx_timer_t* timer, uint32_t offset, uint8_t value) { log_fatal("Unhandled 8-bit TIMER write at offset %08x (%02x)", offset, value);-
- exit(1);
}
void timer_update_timer0(psx_timer_t* timer, int cyc) {@@ -226,7 +212,7 @@
T1_MODE |= 0x400;
}
- timer->timer[0].irq_fired = 1;
+ T1_IRQ_FIRED = 1;
psx_ic_irq(timer->ic, IC_TIMER1);
}
--- a/psx/msf.c
+++ b/psx/msf.c
@@ -1,4 +1,5 @@
#include "msf.h"
+#include "log.h"
uint8_t msf_btoi(uint8_t b) {return ((b >> 4) * 10) + (b & 0xf);
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -26,7 +26,10 @@
void psx_update(psx_t* psx) {psx_cpu_cycle(psx->cpu);
- psx_cdrom_update(psx->cdrom, 22);
+
+ psx->cpu->last_cycles = 2;
+
+ psx_cdrom_update(psx->cdrom, 2);
psx_gpu_update(psx->gpu, psx->cpu->last_cycles);
psx_pad_update(psx->pad, psx->cpu->last_cycles);
psx_timer_update(psx->timer, psx->cpu->last_cycles);
@@ -53,6 +56,18 @@
}
uint32_t psx_get_display_width(psx_t* psx) {+ return psx->gpu->draw_x2 - psx->gpu->draw_x1;
+}
+
+uint32_t psx_get_display_height(psx_t* psx) {+ return psx->gpu->draw_y2 - psx->gpu->draw_y1;
+}
+
+uint32_t psx_get_display_format(psx_t* psx) {+ return (psx->gpu->display_mode >> 4) & 1;
+}
+
+uint32_t psx_get_dmode_width(psx_t* psx) { static int dmode_hres_table[] = {256, 320, 512, 640
};
@@ -64,12 +79,19 @@
}
}
-uint32_t psx_get_display_height(psx_t* psx) {+uint32_t psx_get_dmode_height(psx_t* psx) {return (psx->gpu->display_mode & 0x4) ? 480 : 240;
}
-uint32_t psx_get_display_format(psx_t* psx) {- return (psx->gpu->display_mode >> 4) & 1;
+double psx_get_display_aspect(psx_t* psx) {+ double width = psx_get_dmode_width(psx);
+ double height = psx_get_dmode_height(psx);
+ double aspect = width / height;
+
+ if (aspect > (4.0 / 3.0))
+ return 4.0 / 3.0;
+
+ return aspect;
}
void psx_init(psx_t* psx, const char* bios_path) {--- a/psx/psx.h
+++ b/psx/psx.h
@@ -39,9 +39,12 @@
void psx_run_frame(psx_t*);
void* psx_get_display_buffer(psx_t*);
void* psx_get_vram(psx_t*);
+uint32_t psx_get_dmode_width(psx_t*);
+uint32_t psx_get_dmode_height(psx_t*);
uint32_t psx_get_display_width(psx_t*);
uint32_t psx_get_display_height(psx_t*);
uint32_t psx_get_display_format(psx_t*);
+double psx_get_display_aspect(psx_t*);
uint32_t* psx_take_screenshot(psx_t*);
psx_bios_t* psx_get_bios(psx_t*);
psx_ram_t* psx_get_ram(psx_t*);
--
⑨