implement data cache

This commit is contained in:
Dillon Beliveau 2024-04-14 16:12:15 -07:00
parent 4a727909bd
commit 1e461dc014
8 changed files with 258 additions and 78 deletions

View file

@ -10,6 +10,7 @@ configure_file(../contrib/tools/dynasm/dasm_x86.lua dynasm/dasm_x86.lua COPYONLY
add_library(r4300i
r4300i.c r4300i.h r4300i_register_access.h
cache.c cache.h
mips_instructions.c mips_instructions.h
fpu_instructions.c fpu_instructions.h
tlb_instructions.c tlb_instructions.h

92
src/cpu/cache.c Normal file
View file

@ -0,0 +1,92 @@
#include "cache.h"
#include <system/n64system.h>
#include <mem/mem_util.h>
void writeback_dcache(u64 vaddr, u32 paddr) {
int cache_line = get_dcache_line_index(vaddr);
dcache_line_t* line = &N64CPU.dcache[cache_line];
if (!line->valid) {
logfatal("Writing back invalid dcache line");
}
u32 original_paddr = (line->ptag << 12) | (paddr & 0xFFF);
u32 line_start = get_dcache_line_start(original_paddr);
for (int i = 0; i < 16; i++) {
n64sys.mem.rdram[line_start + i] = line->data[i];
}
line->dirty = false;
}
INLINE dcache_line_t* prep_dcache_line(u64 vaddr, u32 paddr) {
int cache_line_index = get_dcache_line_index(vaddr);
dcache_line_t* line = &N64CPU.dcache[cache_line_index];
u32 ptag = get_paddr_ptag(paddr);
bool valid = line->valid;
bool ptag_matches = line->ptag == ptag;
bool hit = valid && ptag_matches;
bool dirty = line->dirty;
// If the cache line is valid but dirty it contains data that is not written back to RAM yet.
// If it's also not a hit, we are going to need to reload it next, so write it back.
if (valid && dirty && !hit) {
writeback_dcache(vaddr, paddr);
}
// If the cache line is not valid or it's not a hit, load it from RAM
if (!valid || !hit) {
u32 line_start = get_dcache_line_start(paddr);
if (paddr < N64_RDRAM_SIZE) {
for (int i = 0; i < 16; i++) {
line->data[i] = n64sys.mem.rdram[line_start + i];
}
} else {
logfatal("Implement me: Loading dcache from something other than RDRAM");
}
line->valid = true;
line->dirty = false;
line->ptag = ptag;
line->index = cache_line_index;
}
return line;
}
u8 cache_read_byte(u64 vaddr, u32 paddr) {
return prep_dcache_line(vaddr, paddr)->data[BYTE_ADDRESS(paddr & 0xF)];
}
void cache_write_byte(u64 vaddr, u32 paddr, u8 value) {
dcache_line_t* line = prep_dcache_line(vaddr, paddr);
line->data[BYTE_ADDRESS(paddr & 0xF)] = value;
line->dirty = true;
}
u16 cache_read_half(u64 vaddr, u32 paddr) {
return half_from_byte_array(prep_dcache_line(vaddr, paddr)->data, HALF_ADDRESS(paddr & 0xF));
}
void cache_write_half(u64 vaddr, u32 paddr, u16 value) {
dcache_line_t* line = prep_dcache_line(vaddr, paddr);
half_to_byte_array(line->data, HALF_ADDRESS(paddr & 0xF), value);
line->dirty = true;
}
u32 cache_read_word(u64 vaddr, u32 paddr) {
return word_from_byte_array(prep_dcache_line(vaddr, paddr)->data, WORD_ADDRESS(paddr & 0xF));
}
void cache_write_word(u64 vaddr, u32 paddr, u32 value) {
dcache_line_t* line = prep_dcache_line(vaddr, paddr);
word_to_byte_array(line->data, WORD_ADDRESS(paddr & 0xF), value);
line->dirty = true;
}
u64 cache_read_dword(u64 vaddr, u32 paddr) {
return dword_from_byte_array(prep_dcache_line(vaddr, paddr)->data, DWORD_ADDRESS(paddr & 0xF));
}
void cache_write_dword(u64 vaddr, u32 paddr, u64 value) {
dcache_line_t* line = prep_dcache_line(vaddr, paddr);
dword_to_byte_array(line->data, DWORD_ADDRESS(paddr & 0xF), value);
line->dirty = true;
}

54
src/cpu/cache.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef N64_CACHE_H
#define N64_CACHE_H
// #include <mem/n64bus.h>
#include <util.h>
typedef struct icache_line {
bool valid;
u32 ptag;
// since the icache will only ever be accessed as u32, just store it as u32
u32 data[8]; // 32 bytes, 8 words
} icache_line_t;
typedef struct dcache_line {
bool valid;
bool dirty;
u32 ptag;
u8 data[16];
int index;
} dcache_line_t;
INLINE int get_icache_line_index(u64 vaddr) {
return (vaddr >> 5) & 0x1FF;
}
INLINE u32 get_icache_line_start(u32 paddr) {
return paddr & ~0x1F;
}
INLINE int get_dcache_line_index(u64 vaddr) {
return (vaddr >> 4) & 0x1FF;
}
INLINE u32 get_dcache_line_start(u32 paddr) {
return paddr & ~0xF;
}
INLINE u32 get_paddr_ptag(u32 paddr) {
return paddr >> 12;
}
void writeback_dcache(u64 vaddr, u32 paddr);
u8 cache_read_byte(u64 vaddr, u32 paddr);
void cache_write_byte(u64 vaddr, u32 paddr, u8 value);
u16 cache_read_half(u64 vaddr, u32 paddr);
void cache_write_half(u64 vaddr, u32 paddr, u16 value);
u32 cache_read_word(u64 vaddr, u32 paddr);
void cache_write_word(u64 vaddr, u32 paddr, u32 value);
u64 cache_read_dword(u64 vaddr, u32 paddr);
void cache_write_dword(u64 vaddr, u32 paddr, u64 value);
#endif // N64_CACHE_H

View file

@ -1,5 +1,6 @@
#include "fpu_instructions.h"
#include <cache.h>
#include <util.h>
#include <mem/n64bus.h>
#include <math.h>
@ -1017,7 +1018,7 @@ MIPS_INSTR(mips_ldc1) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u64 value = n64_read_physical_dword(physical);
u64 value = conditional_cache_read_dword(cached, address, physical);
set_fpu_register_dword_fr(instruction.i.rt, value);
}
}
@ -1034,7 +1035,7 @@ MIPS_INSTR(mips_sdc1) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
n64_write_physical_dword(physical, value);
conditional_cache_write_dword(cached, address, physical, value);
}
}
@ -1049,7 +1050,7 @@ MIPS_INSTR(mips_lwc1) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u32 value = n64_read_physical_word(physical);
u32 value = conditional_cache_read_word(cached, address, physical);
set_fpu_register_word_fr(instruction.fi.ft, value);
}
}
@ -1066,7 +1067,7 @@ MIPS_INSTR(mips_swc1) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
n64_write_physical_word(physical, value);
conditional_cache_write_word(cached, address, physical, value);
}
}

View file

@ -1,6 +1,7 @@
#include "mips_instructions.h"
#include "r4300i_register_access.h"
#include <cache.h>
#include <mem/n64bus.h>
MIPS_INSTR(mips_nop) {}
@ -104,7 +105,8 @@ MIPS_INSTR(mips_cache) {
case DCACHE_OP(0): { // Index_Write_Back_Invalidate
int cache_line = get_dcache_line_index(virtual_address);
if (N64CPU.dcache[cache_line].valid) {
logfatal("writeback");
writeback_dcache(virtual_address, physical);
N64CPU.dcache[cache_line].valid = false;
}
break;
}
@ -133,7 +135,7 @@ MIPS_INSTR(mips_cache) {
case DCACHE_OP(4): {
int cache_line = get_dcache_line_index(virtual_address);
if (N64CPU.dcache[cache_line].valid && N64CPU.dcache[cache_line].ptag == physical_tag) {
logfatal("Hit_Invalidate dcache unimplemented");
N64CPU.dcache[cache_line].valid = false;
}
break;
}
@ -143,7 +145,8 @@ MIPS_INSTR(mips_cache) {
case DCACHE_OP(5): { // Hit_Write_Back_Invalidate
int cache_line = get_dcache_line_index(virtual_address);
if (N64CPU.dcache[cache_line].valid && N64CPU.dcache[cache_line].ptag == physical_tag) {
logfatal("Hit_Write_Back_Invalidate dcache unimplemented\n");
writeback_dcache(virtual_address, physical);
N64CPU.dcache[cache_line].valid = false;
}
break;
}
@ -153,7 +156,7 @@ MIPS_INSTR(mips_cache) {
case DCACHE_OP(6): { // Hit_Write_Back dcache
int cache_line = get_dcache_line_index(virtual_address);
if (N64CPU.dcache[cache_line].valid && N64CPU.dcache[cache_line].ptag == physical_tag) {
logfatal("Hit_Write_Back dcache unimplemented\n");
writeback_dcache(virtual_address, physical);
}
break;
}
@ -239,7 +242,7 @@ MIPS_INSTR(mips_ld) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u64 value = n64_read_physical_dword(physical);
u64 value = conditional_cache_read_dword(cached, address, physical);
set_register(instruction.i.rt, value);
}
}
@ -263,7 +266,7 @@ MIPS_INSTR(mips_lbu) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u8 value = n64_read_physical_byte(physical);
u8 value = conditional_cache_read_byte(cached, address, physical);
set_register(instruction.i.rt, value); // zero extend
}
}
@ -282,7 +285,7 @@ MIPS_INSTR(mips_lhu) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u16 value = n64_read_physical_half(physical);
u16 value = conditional_cache_read_half(cached, address, physical);
set_register(instruction.i.rt, value); // zero extend
}
}
@ -300,7 +303,7 @@ MIPS_INSTR(mips_lh) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
s16 value = n64_read_physical_half(physical);
s16 value = conditional_cache_read_half(cached, address, physical);
set_register(instruction.i.rt, (s64)value); // zero extend
}
}
@ -320,7 +323,7 @@ MIPS_INSTR(mips_lw) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
s32 value = n64_read_physical_word(physical);
s32 value = conditional_cache_read_word(cached, address, physical);
set_register(instruction.i.rt, (s64)value);
}
}
@ -338,7 +341,7 @@ MIPS_INSTR(mips_lwu) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u32 value = n64_read_physical_word(physical);
u32 value = conditional_cache_read_word(cached, address, physical);
set_register(instruction.i.rt, value);
}
}
@ -355,7 +358,7 @@ MIPS_INSTR(mips_sb) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
n64_write_physical_byte(physical, value);
conditional_cache_write_byte(cached, address, physical, value);
}
}
@ -370,7 +373,7 @@ MIPS_INSTR(mips_sh) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
n64_write_physical_half(physical, value);
conditional_cache_write_half(cached, address, physical, value);
}
}
@ -391,7 +394,7 @@ MIPS_INSTR(mips_sw) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
n64_write_physical_word(physical, get_register(instruction.i.rt));
conditional_cache_write_word(cached, address, physical, get_register(instruction.i.rt));
}
}
@ -412,7 +415,7 @@ MIPS_INSTR(mips_sd) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
n64_write_physical_dword(physical, value);
conditional_cache_write_dword(cached, address, physical, value);
}
}
@ -441,7 +444,7 @@ MIPS_INSTR(mips_lb) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
s8 value = n64_read_physical_byte(physical);
s8 value = conditional_cache_read_byte(cached, address, physical);
set_register(instruction.i.rt, (s64)value);
}
}
@ -459,7 +462,7 @@ MIPS_INSTR(mips_lwl) {
} else {
u32 shift = 8 * ((address ^ 0) & 3);
u32 mask = 0xFFFFFFFF << shift;
u32 data = n64_read_physical_word(physical & ~3);
u32 data = conditional_cache_read_word(cached, address & ~3, physical & ~3);
s32 result = (get_register(instruction.i.rt) & ~mask) | data << shift;
set_register(instruction.i.rt, (s64)result);
}
@ -477,7 +480,7 @@ MIPS_INSTR(mips_lwr) {
u32 shift = 8 * ((address ^ 3) & 3);
u32 mask = 0xFFFFFFFF >> shift;
u32 data = n64_read_physical_word(physical & ~3);
u32 data = conditional_cache_read_word(cached, address & ~3, physical & ~3);
s32 result = (get_register(instruction.i.rt) & ~mask) | data >> shift;
set_register(instruction.i.rt, (s64)result);
}
@ -495,9 +498,9 @@ MIPS_INSTR(mips_swl) {
} else {
u32 shift = 8 * ((address ^ 0) & 3);
u32 mask = 0xFFFFFFFF >> shift;
u32 data = n64_read_physical_word(physical & ~3);
u32 data = conditional_cache_read_word(cached, address & ~3, physical & ~3);
u32 oldreg = get_register(instruction.i.rt);
n64_write_physical_word(physical & ~3, (data & ~mask) | (oldreg >> shift));
conditional_cache_write_word(cached, address & ~3, physical & ~3, (data & ~mask) | (oldreg >> shift));
}
}
@ -512,9 +515,9 @@ MIPS_INSTR(mips_swr) {
} else {
u32 shift = 8 * ((address ^ 3) & 3);
u32 mask = 0xFFFFFFFF << shift;
u32 data = n64_read_physical_word(physical & ~3);
u32 data = conditional_cache_read_word(cached, address & ~3, physical & ~3);
u32 oldreg = get_register(instruction.i.rt);
n64_write_physical_word(physical & ~3, (data & ~mask) | oldreg << shift);
conditional_cache_write_word(cached, address & ~3, physical & ~3, (data & ~mask) | oldreg << shift);
}
}
@ -529,7 +532,7 @@ MIPS_INSTR(mips_ldl) {
} else {
int shift = 8 * ((address ^ 0) & 7);
u64 mask = (u64) 0xFFFFFFFFFFFFFFFF << shift;
u64 data = n64_read_physical_dword(physical & ~7);
u64 data = conditional_cache_read_dword(cached, address & ~7, physical & ~7);
u64 oldreg = get_register(instruction.i.rt);
set_register(instruction.i.rt, (oldreg & ~mask) | (data << shift));
@ -547,7 +550,7 @@ MIPS_INSTR(mips_ldr) {
} else {
int shift = 8 * ((address ^ 7) & 7);
u64 mask = (u64) 0xFFFFFFFFFFFFFFFF >> shift;
u64 data = n64_read_physical_dword(physical & ~7);
u64 data = conditional_cache_read_dword(cached, address & ~7, physical & ~7);
u64 oldreg = get_register(instruction.i.rt);
set_register(instruction.i.rt, (oldreg & ~mask) | (data >> shift));
@ -567,9 +570,9 @@ MIPS_INSTR(mips_sdl) {
int shift = 8 * ((address ^ 0) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF;
mask >>= shift;
u64 data = n64_read_physical_dword(physical & ~7);
u64 data = conditional_cache_read_dword(cached, address & ~7, physical & ~7);
u64 oldreg = get_register(instruction.i.rt);
n64_write_physical_dword(physical & ~7, (data & ~mask) | (oldreg >> shift));
conditional_cache_write_dword(cached, address & ~7, physical & ~7, (data & ~mask) | (oldreg >> shift));
}
}
@ -585,9 +588,9 @@ MIPS_INSTR(mips_sdr) {
int shift = 8 * ((address ^ 7) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF;
mask <<= shift;
u64 data = n64_read_physical_dword(physical & ~7);
u64 data = conditional_cache_read_dword(cached, address & ~7, physical & ~7);
u64 oldreg = get_register(instruction.i.rt);
n64_write_physical_dword(physical & ~7, (data & ~mask) | (oldreg << shift));
conditional_cache_write_dword(cached, address & ~7, physical & ~7, (data & ~mask) | (oldreg << shift));
}
}
@ -602,7 +605,7 @@ MIPS_INSTR(mips_ll) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
s32 result = n64_read_physical_word(physical);
s32 result = conditional_cache_read_word(cached, address, physical);
if ((address & 0b11) > 0) {
logfatal("TODO: throw an 'address error' exception! Tried to load from unaligned address 0x%016" PRIX64, address);
}
@ -632,7 +635,7 @@ MIPS_INSTR(mips_lld) {
on_tlb_exception(address);
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_LOAD), 0);
} else {
u64 result = n64_read_physical_dword(physical);
u64 result = conditional_cache_read_dword(cached, address, physical);
if ((address & 0b111) > 0) {
logfatal("TODO: throw an 'address error' exception! Tried to load from unaligned address 0x%016" PRIX64, address);
}
@ -663,7 +666,7 @@ MIPS_INSTR(mips_sc) {
r4300i_handle_exception(N64CPU.prev_pc, get_tlb_exception_code(N64CP0.tlb_error, BUS_STORE), 0);
} else {
u32 value = get_register(instruction.i.rt);
n64_write_physical_word(physical_address, value);
conditional_cache_write_word(cached, address, physical_address, value);
set_register(instruction.i.rt, 1); // Success!
}
} else {
@ -693,7 +696,7 @@ MIPS_INSTR(mips_scd) {
u32 physical_address = resolve_virtual_address_or_die(address, BUS_STORE, &cached);
u64 value = get_register(instruction.i.rt);
n64_write_physical_dword(physical_address, value);
conditional_cache_write_dword(cached, address, physical_address, value);
set_register(instruction.i.rt, 1); // Success!
} else {

View file

@ -1,5 +1,6 @@
#ifndef N64_R4300I_H
#define N64_R4300I_H
#include <cpu/cache.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
@ -621,44 +622,6 @@ typedef union fgr {
ASSERTDWORD(fgr_t);
typedef struct icache_line {
struct {
u32 valid:1;
u32 ptag:20;
};
// since the icache will only ever be accessed as u32, just store it as u32
u32 data[8]; // 32 bytes, 8 words
} icache_line_t;
typedef struct dcache_line {
struct {
u32 valid:1;
u32 dirty:1;
u32 ptag:20;
};
u8 data[16];
} dcache_line_t;
INLINE int get_icache_line_index(u64 vaddr) {
return (vaddr >> 5) & 0x1FF;
}
INLINE u32 get_icache_line_start(u32 paddr) {
return paddr & ~0x1F;
}
INLINE int get_dcache_line_index(u64 vaddr) {
return (vaddr >> 4) & 0x1FF;
}
INLINE u32 get_dcache_line_start(u32 paddr) {
return paddr & ~0xF;
}
INLINE u32 get_paddr_ptag(u32 paddr) {
return paddr >> 13;
}
typedef struct r4300i {
u64 gpr[32];
fgr_t f[32];

View file

@ -161,10 +161,10 @@ INLINE resolve_virtual_address_handler get_resolve_virtual_address_handler() {
#define resolve_virtual_address(vaddr, bus_access, cached, physical) N64CP0.resolve_virtual_address(vaddr, bus_access, cached, physical)
INLINE u32 resolve_virtual_address_or_die(u64 virtual, bus_access_t bus_access, bool* cached) {
INLINE u32 resolve_virtual_address_or_die(u64 vaddr, bus_access_t bus_access, bool* cached) {
u32 physical;
if (!resolve_virtual_address(virtual, bus_access, cached, &physical)) {
logfatal("Unhandled TLB exception at 0x%016" PRIX64 "! Stop calling resolve_virtual_address_or_die() here!", virtual);
if (!resolve_virtual_address(vaddr, bus_access, cached, &physical)) {
logfatal("Unhandled TLB exception at 0x%016" PRIX64 "! Stop calling resolve_virtual_address_or_die() here!", vaddr);
}
return physical;
}
@ -183,7 +183,73 @@ u8 n64_read_physical_byte(u32 address);
INLINE u8 n64_read_byte(u64 address) {
bool cached;
return n64_read_physical_byte(resolve_virtual_address_or_die(address, false, &cached));
return n64_read_physical_byte(resolve_virtual_address_or_die(address, BUS_LOAD, &cached));
}
INLINE u8 conditional_cache_read_byte(bool cached, u64 vaddr, u32 paddr) {
if (cached) {
return cache_read_byte(vaddr, paddr);
} else {
return n64_read_physical_byte(paddr);
}
}
INLINE void conditional_cache_write_byte(bool cached, u64 vaddr, u32 paddr, u8 value) {
if (cached) {
cache_write_byte(vaddr, paddr, value);
} else {
n64_write_physical_byte(paddr, value);
}
}
INLINE u16 conditional_cache_read_half(bool cached, u64 vaddr, u32 paddr) {
if (cached) {
return cache_read_half(vaddr, paddr);
} else {
return n64_read_physical_half(paddr);
}
}
INLINE void conditional_cache_write_half(bool cached, u64 vaddr, u32 paddr, u16 value) {
if (cached) {
cache_write_half(vaddr, paddr, value);
} else {
n64_write_physical_half(paddr, value);
}
}
INLINE u32 conditional_cache_read_word(bool cached, u64 vaddr, u32 paddr) {
if (cached) {
return cache_read_word(vaddr, paddr);
} else {
return n64_read_physical_word(paddr);
}
}
INLINE void conditional_cache_write_word(bool cached, u64 vaddr, u32 paddr, u32 value) {
if (cached) {
cache_write_word(vaddr, paddr, value);
} else {
n64_write_physical_word(paddr, value);
}
}
INLINE u64 conditional_cache_read_dword(bool cached, u64 vaddr, u32 paddr) {
if (cached) {
return cache_read_dword(vaddr, paddr);
} else {
return n64_read_physical_dword(paddr);
}
}
INLINE void conditional_cache_write_dword(bool cached, u64 vaddr, u32 paddr, u64 value) {
if (cached) {
cache_write_dword(vaddr, paddr, value);
} else {
n64_write_physical_dword(paddr, value);
}
}
#endif //N64_N64BUS_H

View file

@ -165,8 +165,8 @@ typedef struct softrdp_state {
void softrdp_init(softrdp_state_t* state, uint8_t* rdramptr);
#define full_sync_softrdp() do {} while(0)
void softrdp_enqueue_command(softrdp_state_t* rdp, int command_length, uint64_t* buffer);
#endif
#ifdef __cplusplus
}
#endif
#endif