Finally booting through the rest of the BIOS. Whew. :-)

This commit is contained in:
Adam Becker 2016-11-25 21:22:38 -07:00
parent 09c90456ee
commit 4405d7a069
18 changed files with 203 additions and 81 deletions

View file

@ -7,9 +7,9 @@ This emulator is designed to work "out of the box", no plug-in hell required!
## Current Status
Currently, the emulator hangs after drawing the Sony™ logo while trying
to communicate with the CD-ROM drive. Textured primitives are partially
implemented.
Currently, the emulator can get through the BIOS, but hasn't booted any
commercial games yet. A software rasterizer is used to attempt pixel accurate
graphical output, the results are quite nice!
![Current status](images/current.png)
@ -28,6 +28,9 @@ $ psxact <bios file here> <game file here>
This project uses CMake for builds, and requires SDL2.
I personally use the `FindSDL2.cmake` script available
[here](https://github.com/tcbrindle/sdl2-cmake-scripts).
## Contributing
If you'd like to contribute, please create a fork and issue pull requests! I am

View file

@ -24,7 +24,6 @@ void bus::initialize(const std::string &bios_file_name, const std::string &game_
}
void bus::irq(int interrupt) {
printf("bus::irq(%d)\n", interrupt);
cpu::state.i_stat |= (1 << interrupt);
}

View file

@ -40,8 +40,6 @@ static void set_data(uint8_t data) {
uint32_t cdrom::bus_read(int width, uint32_t address) {
assert(width == BYTE);
printf("cdrom::bus_read(%d, 0x%08x)\n", width, address);
switch (address - 0x1f801800) {
case 0: { // status register
uint32_t result = state.index;
@ -69,8 +67,6 @@ uint32_t cdrom::bus_read(int width, uint32_t address) {
void cdrom::bus_write(int width, uint32_t address, uint32_t data) {
assert(width == BYTE);
printf("cdrom::bus_write(%d, 0x%08x, 0x%08x)\n", width, address, data);
switch (address - 0x1f801800) {
case 0:
state.index = data & 3;

View file

@ -32,40 +32,32 @@ void cpu::initialize() {
state.regs.next_pc = state.regs.pc + 4;
}
void cpu::run(int count) {
bool dis = false;
void cpu::tick() {
cpu::read_code();
for (int i = 0; i < count; i++) {
cpu::read_code();
state.is_branch_delay_slot = state.is_branch;
state.is_branch = false;
if (dis) {
disassemble();
}
state.is_load_delay_slot = state.is_load;
state.is_load = false;
state.is_branch_delay_slot = state.is_branch;
state.is_branch = false;
if (state.i_stat & state.i_mask) {
state.cop0.regs[13] |= (1 << 10);
} else {
state.cop0.regs[13] &= ~(1 << 10);
}
state.is_load_delay_slot = state.is_load;
state.is_load = false;
auto iec = (state.cop0.regs[12] & 1) != 0;
auto irq = (state.cop0.regs[12] & state.cop0.regs[13] & 0xff00) != 0;
if (state.i_stat & state.i_mask) {
state.cop0.regs[13] |= (1 << 10);
} else {
state.cop0.regs[13] &= ~(1 << 10);
}
auto iec = (state.cop0.regs[12] & 1) != 0;
auto irq = (state.cop0.regs[12] & state.cop0.regs[13] & 0xff00) != 0;
if (iec && irq) {
enter_exception(0x0);
} else {
auto code = (cpu::state.code >> 26) & 63;
if (code)
op_table[code]();
else
op_table_special[(cpu::state.code >> 0) & 63]();
}
if (iec && irq) {
enter_exception(0x0);
} else {
auto code = (cpu::state.code >> 26) & 63;
if (code)
op_table[code]();
else
op_table_special[(cpu::state.code >> 0) & 63]();
}
}

View file

@ -43,7 +43,7 @@ namespace cpu {
void disassemble();
void run(int count);
void tick();
void enter_exception(uint32_t code);

View file

@ -30,8 +30,6 @@ static uint32_t get_register_index(uint32_t address) {
}
uint32_t dma::bus_read(int width, uint32_t address) {
printf("dma::bus_read(%d, 0x%08x)\n", width, address);
auto channel = get_channel_index(address);
if (channel == 7) {
switch (get_register_index(address)) {
@ -53,8 +51,6 @@ uint32_t dma::bus_read(int width, uint32_t address) {
}
void dma::bus_write(int width, uint32_t address, uint32_t data) {
printf("dma::bus_write(%d, 0x%08x, 0x%08x)\n", width, address, data);
auto channel = get_channel_index(address);
if (channel == 7) {
switch (get_register_index(address)) {

View file

@ -1,8 +1,7 @@
#include <cassert>
#include "gpu_core.hpp"
#include "../bus.hpp"
#include "../memory/vram.hpp"
#include <stdexcept>
#include <cassert>
gpu::state_t gpu::state;
@ -14,7 +13,6 @@ uint32_t gpu::data() {
return (upper << 16) | lower;
}
printf("gpu::data()\n");
return 0;
}
@ -24,15 +22,12 @@ uint32_t gpu::stat() {
// 27 Ready to send VRAM to CPU (0=No, 1=Ready) ;GP0(C0h) ;via GPUREAD
// 28 Ready to receive DMA Block (0=No, 1=Ready) ;GP0(...) ;via GP0
printf("gpu::stat()\n");
return (gpu::state.status & ~0x00080000) | 0x1c002000;
}
uint32_t gpu::bus_read(int width, uint32_t address) {
assert(width == WORD);
printf("gpu::bus_read(%d, 0x%08x)\n", width, address);
switch (address) {
case 0x1f801810: return data();
case 0x1f801814: return stat();
@ -42,11 +37,12 @@ uint32_t gpu::bus_read(int width, uint32_t address) {
void gpu::bus_write(int width, uint32_t address, uint32_t data) {
assert(width == WORD);
printf("gpu::bus_write(%d, 0x%08x, 0x%08x)\n", width, address, data);
switch (address) {
case 0x1f801810: return gp0(data);
case 0x1f801814: return gp1(data);
case 0x1f801810:
return gp0(data);
case 0x1f801814:
return gp1(data);
}
}
@ -62,7 +58,7 @@ uint16_t gpu::vram_transfer() {
transfer.run.x++;
if (transfer.run.x == transfer.reg.w) {
transfer.run.x = transfer.reg.x;
transfer.run.x = 0;
transfer.run.y++;
if (transfer.run.y == transfer.reg.h) {

View file

@ -126,6 +126,7 @@ namespace gpu {
int clut_y;
int base_u;
int base_v;
int depth;
};
void draw_poly3(const polygon_t<3> &p);

View file

@ -16,6 +16,9 @@ static int dither_lut[4][4] = {
};
void gpu::draw_point(int x, int y, int r, int g, int b) {
if (x < state.drawing_area_x1 || x > state.drawing_area_x2) return;
if (y < state.drawing_area_y1 || y > state.drawing_area_y2) return;
auto dither = dither_lut[y & 3][x & 3];
r = clip<0, 255>(r + dither);

View file

@ -1,10 +1,10 @@
#include "gpu_core.hpp"
#include "../memory/vram.hpp"
void fill_texture(const gpu::texture::polygon_t<3> &p,
const int &w0,
const int &w1,
const int &w2, int x, int y) {
void fill_texture_4bpp(const gpu::texture::polygon_t<3> &p,
const int &w0,
const int &w1,
const int &w2, int x, int y) {
int area = w0 + w1 + w2;
int u = ((p.v[0].u * w0) + (p.v[1].u * w1) + (p.v[2].u * w2)) / area;
@ -28,6 +28,21 @@ void fill_texture(const gpu::texture::polygon_t<3> &p,
vram::write(x, y, color);
}
void fill_texture_15bpp(const gpu::texture::polygon_t<3> &p,
const int &w0,
const int &w1,
const int &w2, int x, int y) {
int area = w0 + w1 + w2;
int u = ((p.v[0].u * w0) + (p.v[1].u * w1) + (p.v[2].u * w2)) / area;
int v = ((p.v[0].v * w0) + (p.v[1].v * w1) + (p.v[2].v * w2)) / area;
auto color = vram::read(p.base_u + u, p.base_v + v);
if (color) {
vram::write(x, y, color);
}
}
static int min3(int a, int b, int c) {
if (a <= b && a <= c) return a;
if (b <= a && b <= c) return b;
@ -80,7 +95,11 @@ static void fill_poly3_texture(const gpu::texture::polygon_t<3> &t) {
(w2 > 0 || (w2 == 0 && is_top_left_01));
if (draw) {
fill_texture(t, w0, w1, w2, p.x, p.y);
switch (t.depth) {
case 0: fill_texture_4bpp (t, w0, w1, w2, p.x, p.y); break;
case 2: fill_texture_15bpp(t, w0, w1, w2, p.x, p.y); break;
case 3: fill_texture_15bpp(t, w0, w1, w2, p.x, p.y); break;
}
}
w0 += x12;
@ -108,9 +127,9 @@ void gpu::texture::draw_poly3(const gpu::texture::polygon_t<3> &p) {
auto &v2 = p.v[2];
if (double_area(v0.point, v1.point, v2.point) < 0) {
fill_poly3_texture({v0, v1, v2, p.clut_x, p.clut_y, p.base_u, p.base_v});
fill_poly3_texture({v0, v1, v2, p.clut_x, p.clut_y, p.base_u, p.base_v, p.depth});
} else {
fill_poly3_texture({v0, v2, v1, p.clut_x, p.clut_y, p.base_u, p.base_v});
fill_poly3_texture({v0, v2, v1, p.clut_x, p.clut_y, p.base_u, p.base_v, p.depth});
}
}
@ -120,6 +139,6 @@ void gpu::texture::draw_poly4(const gpu::texture::polygon_t<4> &p) {
auto &v2 = p.v[2];
auto &v3 = p.v[3];
gpu::texture::draw_poly3({v0, v1, v2, p.clut_x, p.clut_y, p.base_u, p.base_v});
gpu::texture::draw_poly3({v1, v2, v3, p.clut_x, p.clut_y, p.base_u, p.base_v});
gpu::texture::draw_poly3({v0, v1, v2, p.clut_x, p.clut_y, p.base_u, p.base_v, p.depth});
gpu::texture::draw_poly3({v1, v2, v3, p.clut_x, p.clut_y, p.base_u, p.base_v, p.depth});
}

View file

@ -1,15 +1,16 @@
#include <cassert>
#include "gpu_core.hpp"
#include "../memory/vram.hpp"
static int command_size[256] = {
1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $10
1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 9, 1, 1, 1, // $20
1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 9, 9, 1, 1, // $20
6, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, // $30
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $40
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $50
1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, // $60
1, 1, 1, 1, 1, 4, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, // $60
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $70
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $80
@ -25,8 +26,8 @@ static int command_size[256] = {
static gpu::point_t gp0_to_point(uint32_t point) {
gpu::point_t result;
result.x = int(point & 0xffff);
result.y = int(point >> 16);
result.x = utility::sclip<11>(point);
result.y = utility::sclip<11>(point >> 16);
return result;
}
@ -118,7 +119,8 @@ void gpu::gp0(uint32_t data) {
break;
}
case 0x2c: { // textured quad, opaque
case 0x2c:
case 0x2d: { // textured quad, opaque
auto color = state.fifo.buffer[0];
auto point1 = state.fifo.buffer[1];
auto coord1 = state.fifo.buffer[2];
@ -139,6 +141,7 @@ void gpu::gp0(uint32_t data) {
p.clut_y = ((coord1 >> 22) & 0x1ff) * 1;
p.base_u = ((coord2 >> 16) & 0x00f) * 64;
p.base_v = ((coord2 >> 20) & 0x001) * 256;
p.depth = ((coord2 >> 23) & 0x003);
gpu::texture::draw_poly4(p);
break;
@ -179,6 +182,46 @@ void gpu::gp0(uint32_t data) {
break;
}
case 0x65: {
//auto color = gp0_to_color(state.fifo.buffer[0]);
auto point1 = gp0_to_point(state.fifo.buffer[1]);
auto coord = state.fifo.buffer[2];
auto point2 = gp0_to_point(state.fifo.buffer[3]);
assert((state.status & 0x180) == 0);
auto base_u = ((state.status >> 0) & 0xf) * 64;
auto base_v = ((state.status >> 4) & 0x1) * 256;
auto clut_x = ((coord >> 16) & 0x03f) * 16;
auto clut_y = ((coord >> 22) & 0x1ff);
for (int y = 0; y < point2.y; y++) {
for (int x = 0; x < point2.x; x++) {
auto texel = vram::read(base_u + (x / 4),
base_v + y);
int index = 0;
switch (x & 3) {
case 0: index = (texel >> 0) & 0xf; break;
case 1: index = (texel >> 4) & 0xf; break;
case 2: index = (texel >> 8) & 0xf; break;
case 3: index = (texel >> 12) & 0xf; break;
}
auto color = vram::read(clut_x + index,
clut_y);
vram::write(point1.x + x,
point1.y + y,
color);
}
}
break;
}
case 0x68: {
auto color = gp0_to_color(state.fifo.buffer[0]);
auto point = gp0_to_point(state.fifo.buffer[1]);

View file

@ -1,17 +1,24 @@
#include <cstdio>
#include "input.hpp"
uint32_t input::bus_read(int width, uint32_t address) {
printf("input::bus_read(%d, 0x%08x)\n", width, address);
static input::state_t state;
uint32_t input::bus_read(int width, uint32_t address) {
switch (address) {
case 0x1f801040:
state.status &= ~(1 << 1);
return 0xffffffff;
case 0x1f801044:
return (1 << 0) | (1 << 2);
return state.status | (1 << 0) | (1 << 2);
}
return 0;
}
void input::bus_write(int width, uint32_t address, uint32_t data) {
printf("input::bus_write(%d, 0x%08x, 0x%08x)\n", width, address, data);
switch (address) {
case 0x1f801040:
state.status |= (1 << 1);
break;
}
}

View file

@ -4,6 +4,10 @@
#include <cstdint>
namespace input {
struct state_t {
uint32_t status;
};
uint32_t bus_read(int width, uint32_t address);
void bus_write(int width, uint32_t address, uint32_t data);

View file

@ -12,5 +12,8 @@ uint16_t vram::read(int x, int y) {
}
void vram::write(int x, int y, uint16_t data) {
if (x < 0 || x > 0x400) return;
if (y < 0 || y > 0x200) return;
buffer.h[(y * 1024) + x] = data;
}

View file

@ -3,14 +3,13 @@
#include "cpu/cpu_core.hpp"
#include "gpu/gpu_core.hpp"
#include "renderer.hpp"
#include "timer/timer_core.hpp"
int main(int argc, char *argv[]) {
if (argc != 3) {
return -1;
}
freopen("C:/Users/Adam.Becker/stdout.log", "w", stdout);
std::string bios_file_name(argv[1]);
std::string game_file_name(argv[2]);
@ -21,7 +20,11 @@ int main(int argc, char *argv[]) {
while (renderer::render()) {
for (int i = 0; i < 10; i++) {
cpu::run(33868800 / 60 / 10);
for (int i = 0; i < 33868800 / 60 / 10; i++) {
cpu::tick();
timer::tick_timer_2();
}
cdrom::run();
}

View file

@ -1,11 +1,8 @@
#include <cstdio>
#include "spu_core.hpp"
uint32_t spu::bus_read(int width, uint32_t address) {
printf("spu::bus_read(%d, 0x%08x)\n", width, address);
return 0;
}
void spu::bus_write(int width, uint32_t address, uint32_t data) {
printf("spu::bus_write(%d, 0x%08x, 0x%08x)\n", width, address, data);
}

View file

@ -1,11 +1,58 @@
#include <cstdio>
#include "timer_core.hpp"
#include "../bus.hpp"
static timer::state_t timers[3];
uint32_t timer::bus_read(int width, uint32_t address) {
printf("timer::bus_read(%d, 0x%08x)\n", width, address);
int n = (address >> 4) & 3;
switch ((address & 0xf) / 4) {
case 0: return timers[n].counter;
case 1: return timers[n].control;
case 2: return timers[n].compare;
}
return 0;
}
void timer::bus_write(int width, uint32_t address, uint32_t data) {
printf("timer::bus_write(%d, 0x%08x, 0x%08x)\n", width, address, data);
int n = (address >> 4) & 3;
switch ((address & 0xf) / 4) {
case 0:
timers[n].counter = uint16_t(data);
break;
case 1:
timers[n].control = uint16_t(data | 0x400);
timers[n].counter = 0;
break;
case 2:
timers[n].compare = uint16_t(data);
break;
}
}
void timer::tick_timer_2() {
// system clock/8
timers[2].divider++;
if (timers[2].divider == 8) {
timers[2].divider = 0;
timers[2].counter++;
if (timers[2].counter == timers[2].compare) {
timers[2].control |= 0x800;
if (timers[2].control & 0x0008) {
timers[2].counter = 0;
}
if (timers[2].control & 0x0010) {
timers[2].control &= ~0x0400;
bus::irq(6);
}
}
}
}

View file

@ -4,9 +4,22 @@
#include <cstdint>
namespace timer {
struct state_t {
uint16_t counter;
uint16_t control;
uint16_t compare;
int divider;
};
uint32_t bus_read(int width, uint32_t address);
void bus_write(int width, uint32_t address, uint32_t data);
void run_timer_0();
void run_timer_1();
void tick_timer_2();
}
#endif //PSXACT_TIMER_CORE_HPP