mirror of
https://github.com/whaison/psxact.git
synced 2024-05-16 19:10:33 -04:00
Finally booting through the rest of the BIOS. Whew. :-)
This commit is contained in:
parent
09c90456ee
commit
4405d7a069
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace cpu {
|
|||
|
||||
void disassemble();
|
||||
|
||||
void run(int count);
|
||||
void tick();
|
||||
|
||||
void enter_exception(uint32_t code);
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue