diff --git a/build.rs b/build.rs index d16285c8..90cb9034 100644 --- a/build.rs +++ b/build.rs @@ -209,7 +209,7 @@ fn main() { .allowlist_function("rdp_translate_busy_until") .allowlist_function("rdp_onscreen_message") .allowlist_function("rdp_check_callback") - .allowlist_function("rdp_check_framebuffers") + .allowlist_function("rdp_check_rdram") .allowlist_function("rdp_state_size") .allowlist_function("rdp_save_state") .allowlist_function("rdp_load_state") diff --git a/parallel-rdp/interface.cpp b/parallel-rdp/interface.cpp index 1334a207..26c40adf 100644 --- a/parallel-rdp/interface.cpp +++ b/parallel-rdp/interface.cpp @@ -1,6 +1,7 @@ #include "interface.hpp" #include "../retroachievements/retroachievements.h" #include "rdp_device.hpp" +#include "rdram_tracker.hpp" #include "spirv.hpp" #include "spirv_crt.hpp" #include "wsi.hpp" @@ -72,14 +73,15 @@ enum user_event_codes { typedef struct { uint32_t depthbuffer_address; uint32_t framebuffer_address; - uint32_t framebuffer_y_offset; uint32_t texture_address; uint32_t framebuffer_pixel_size; uint32_t framebuffer_width; uint32_t texture_pixel_size; uint32_t texture_width; - uint32_t framebuffer_height; - bool depth_buffer_enabled; + // scissor as inclusive pixel bounds (empty when x1 < x0 or y1 < y0) + int32_t clip_x0, clip_y0, clip_x1, clip_y1; + bool depth_compare; + bool depth_update; } FrameBufferInfo; typedef struct { @@ -120,8 +122,76 @@ static GFX_INFO gfx_info; static const uint32_t *fragment_spirv; static size_t fragment_size; -static std::vector rdram_dirty; -static uint64_t sync_signal; +// RDRAM handed to parallel-rdp that the GPU may not have finished with yet +static RdramTracker rdram_tracker; + +// the timeline value that will cover commands enqueued now +static uint64_t mark_stamp() { return processor->get_timeline_value() + 1; } + +// A primitive covering pixels [x0, x1] x [y0, y1] before scissoring. +// parallel-rdp only writes back pixels a primitive actually touched, so the +// RDRAM involved is the primitive's footprint inside the scissor. The color +// buffer always counts as written (that also covers blender reads). +static void mark_primitive(int32_t x0, int32_t x1, int32_t y0, int32_t y1) { + auto &fb = rdp_device.frame_buffer_info; + x0 = std::max(x0, fb.clip_x0); + x1 = std::min({x1, fb.clip_x1, int32_t(fb.framebuffer_width) - 1}); + y0 = std::max(y0, fb.clip_y0); + y1 = std::min(y1, fb.clip_y1); + if (x1 < x0 || y1 < y0) + return; + + // parallel-rdp stores I4 color buffers as one byte per pixel + uint32_t bytes = + fb.framebuffer_pixel_size ? 1u << (fb.framebuffer_pixel_size - 1) : 1u; + uint64_t stamp = mark_stamp(); + rdram_tracker.mark_rect(fb.framebuffer_address, fb.framebuffer_width, bytes, + x0, x1, y0, y1, RdramTracker::Access::Write, stamp); + + if (fb.depth_update) + rdram_tracker.mark_rect(fb.depthbuffer_address, fb.framebuffer_width, 2, x0, + x1, y0, y1, RdramTracker::Access::Write, stamp); + else if (fb.depth_compare) + rdram_tracker.mark_rect(fb.depthbuffer_address, fb.framebuffer_width, 2, x0, + x1, y0, y1, RdramTracker::Access::Read, stamp); +} + +// texels [first, first + count) of the current texture image +static void mark_texels(uint32_t first, uint32_t count) { + auto &fb = rdp_device.frame_buffer_info; + uint64_t bits = 4u << fb.texture_pixel_size; + uint64_t start = first * bits / 8; + uint64_t end = ((uint64_t(first) + count) * bits + 7) / 8; + rdram_tracker.mark(fb.texture_address + start, end - start, + RdramTracker::Access::Read, mark_stamp()); +} + +// mirrors the RDRAM reads of parallel-rdp's Renderer::load_tile +static void mark_texture_load(RDP::Op op, uint32_t w1, uint32_t w2) { + auto &fb = rdp_device.frame_buffer_info; + uint32_t sl = (w1 >> 12) & 0xFFF, tl = w1 & 0xFFF; + uint32_t sh = (w2 >> 12) & 0xFFF, th = w2 & 0xFFF; + + if (op == RDP::Op::LoadBlock) { + // a single run of texels; sl/sh/tl are integers, low bits of w2 are dxt + uint32_t count = (sh - sl + 1) & 0xFFF; + if (count == 0 || count > 2048) + return; + mark_texels(tl * fb.texture_width + sl, count); + return; + } + + // LoadTile / LoadTLut: 10.2 coordinates, fetched one row at a time + sl >>= 2; + tl >>= 2; + sh >>= 2; + th >>= 2; + uint32_t count = (sh - sl + 1) & 0xFFF; + if (th < tl || count == 0) + return; + for (uint32_t t = tl; t <= th; t++) + mark_texels(t * fb.texture_width + sl, count); +} static TTF_Font *message_font; static std::queue messages; @@ -337,11 +407,7 @@ JoystickEvent get_joystick_event() { return joystick_event; } -void rdp_idle() { - processor->idle(); - sync_signal = 0; - rdram_dirty.assign(gfx_info.RDRAM_SIZE >> 3, false); -} +void rdp_idle() { processor->idle(); } static void rdp_new_processor() { RDP::CommandProcessorFlags flags = @@ -373,8 +439,8 @@ static void rdp_new_processor() { LOGE("Failed to get hidden_rdram\n"); } - sync_signal = 0; - rdram_dirty.assign(gfx_info.RDRAM_SIZE >> 3, false); + // the new processor's timeline starts over + rdram_tracker.reset(gfx_info.RDRAM_SIZE); } static ImageHandle create_message_image(Vulkan::Device &device, int width, @@ -758,25 +824,19 @@ CALL_BACK rdp_check_callback() { return return_value; } -void rdp_check_framebuffers(uint32_t address, uint32_t length) { - if (sync_signal) { - address >>= 3; - length = (length + 7) >> 3; +void rdp_check_rdram(uint32_t address, uint32_t length, bool write) { + uint64_t completed = processor->get_completed_timeline(); + // fast path: everything the RDP has touched is already done + if (rdram_tracker.max_stamp() <= completed) + return; - if (address >= rdram_dirty.size()) - return; - - uint32_t end_addr = - std::min(address + length, static_cast(rdram_dirty.size())); - - auto it = std::find(rdram_dirty.begin() + address, - rdram_dirty.begin() + end_addr, true); - if (it != rdram_dirty.begin() + end_addr) { - processor->wait_for_timeline(sync_signal); - rdram_dirty.assign(gfx_info.RDRAM_SIZE >> 3, false); - sync_signal = 0; - } - } + uint64_t stamp = rdram_tracker.conflict(address, length, write); + if (stamp <= completed) + return; + // commands since the last signal haven't been submitted to the GPU yet + if (stamp > processor->get_timeline_value()) + processor->signal_timeline(); + processor->wait_for_timeline(stamp); } size_t rdp_state_size() { @@ -1043,9 +1103,8 @@ uint64_t rdp_process_commands(uint64_t now) { : uint64_t(sh >= sl ? ((sh - sl) >> 2) + 1 : 0) * uint64_t(th >= tl ? ((th - tl) >> 2) + 1 : 0); // LoadBlock is one contiguous transfer; LoadTile fetches row by row - uint64_t rows = command == 0x33 - ? 1 - : uint64_t(th >= tl ? ((th - tl) >> 2) + 1 : 0); + uint64_t rows = + command == 0x33 ? 1 : uint64_t(th >= tl ? ((th - tl) >> 2) + 1 : 0); uint32_t bits = 4u << rdp_device.frame_buffer_info.texture_pixel_size; rdp_add_busy(RDP_PRIMITIVE_SETUP_CLOCKS + (texels * bits + 63) / 64 + rows * RDP_SCANLINE_CLOCKS); @@ -1059,99 +1118,28 @@ uint64_t rdp_process_commands(uint64_t now) { case RDP::Op::ShadeTriangle: case RDP::Op::ShadeZBufferTriangle: case RDP::Op::ShadeTextureTriangle: - case RDP::Op::ShadeTextureZBufferTriangle: + case RDP::Op::ShadeTextureZBufferTriangle: { + // s11.2 Y range, rows [yh, yl); X is only bounded by the scissor + int32_t yl = sign_extend_14(w1), yh = sign_extend_14(w2); + mark_primitive(0, INT32_MAX, yh >> 2, (yl - 1) >> 2); + } break; case RDP::Op::TextureRectangle: case RDP::Op::TextureRectangleFlip: case RDP::Op::FillRectangle: { - uint32_t offset_address = - (rdp_device.frame_buffer_info.framebuffer_address + - pixel_size(rdp_device.frame_buffer_info.framebuffer_pixel_size, - rdp_device.frame_buffer_info.framebuffer_y_offset * - rdp_device.frame_buffer_info.framebuffer_width)) >> - 3; - if (offset_address < rdram_dirty.size() && !rdram_dirty[offset_address]) { - uint32_t end_addr = std::min( - offset_address + - ((pixel_size( - rdp_device.frame_buffer_info.framebuffer_pixel_size, - rdp_device.frame_buffer_info.framebuffer_width * - rdp_device.frame_buffer_info.framebuffer_height) + - 7) >> - 3), - static_cast(rdram_dirty.size())); - std::fill(rdram_dirty.begin() + offset_address, - rdram_dirty.begin() + end_addr, true); - } - - if (rdp_device.frame_buffer_info.depth_buffer_enabled) { - offset_address = - (rdp_device.frame_buffer_info.depthbuffer_address + - pixel_size(2, - rdp_device.frame_buffer_info.framebuffer_y_offset * - rdp_device.frame_buffer_info.framebuffer_width)) >> - 3; - if (offset_address < rdram_dirty.size() && - !rdram_dirty[offset_address]) { - uint32_t end_addr = std::min( - offset_address + - ((pixel_size( - 2, - rdp_device.frame_buffer_info.framebuffer_width * - rdp_device.frame_buffer_info.framebuffer_height) + - 7) >> - 3), - static_cast(rdram_dirty.size())); - std::fill(rdram_dirty.begin() + offset_address, - rdram_dirty.begin() + end_addr, true); - } - } + // 10.2 coordinates; copy/fill modes include the last row and column + int32_t xl = (w1 >> 12) & 0xFFF, yl = w1 & 0xFFF; + int32_t xh = (w2 >> 12) & 0xFFF, yh = w2 & 0xFFF; + bool copy_fill = rdp_device.cycle_type >= 2; + if (copy_fill) + yl |= 3; + mark_primitive(xh >> 2, copy_fill ? xl >> 2 : (xl - 1) >> 2, yh >> 2, + (yl - 1) >> 2); } break; case RDP::Op::LoadTLut: - case RDP::Op::LoadTile: { - uint32_t upper_left_t = (w1 & 0xFFF) >> 2; - uint32_t offset_address = - (rdp_device.frame_buffer_info.texture_address + - pixel_size(rdp_device.frame_buffer_info.texture_pixel_size, - upper_left_t * - rdp_device.frame_buffer_info.texture_width)) >> - 3; - if (offset_address < rdram_dirty.size() && !rdram_dirty[offset_address]) { - uint32_t lower_right_t = (w2 & 0xFFF) >> 2; - uint32_t end_addr = std::min( - offset_address + - ((pixel_size(rdp_device.frame_buffer_info.texture_pixel_size, - (lower_right_t - upper_left_t) * - rdp_device.frame_buffer_info.texture_width) + - 7) >> - 3), - static_cast(rdram_dirty.size())); - std::fill(rdram_dirty.begin() + offset_address, - rdram_dirty.begin() + end_addr, true); - } - } break; - case RDP::Op::LoadBlock: { - uint32_t upper_left_s = ((w1 >> 12) & 0xFFF); - uint32_t upper_left_t = (w1 & 0xFFF); - uint32_t offset_address = - (rdp_device.frame_buffer_info.texture_address + - pixel_size(rdp_device.frame_buffer_info.texture_pixel_size, - upper_left_s + - upper_left_t * - rdp_device.frame_buffer_info.texture_width)) >> - 3; - if (offset_address < rdram_dirty.size() && !rdram_dirty[offset_address]) { - uint32_t lower_right_s = ((w2 >> 12) & 0xFFF); - uint32_t end_addr = std::min( - offset_address + - ((pixel_size(rdp_device.frame_buffer_info.texture_pixel_size, - lower_right_s - upper_left_s) + - 7) >> - 3), - static_cast(rdram_dirty.size())); - std::fill(rdram_dirty.begin() + offset_address, - rdram_dirty.begin() + end_addr, true); - } - } break; + case RDP::Op::LoadTile: + case RDP::Op::LoadBlock: + mark_texture_load(RDP::Op(command), w1, w2); + break; case RDP::Op::SetColorImage: rdp_device.frame_buffer_info.framebuffer_address = (w2 & 0x00FFFFFF); rdp_device.frame_buffer_info.framebuffer_pixel_size = (w1 >> 19) & 0x3; @@ -1177,20 +1165,26 @@ uint64_t rdp_process_commands(uint64_t now) { rdp_device.scissor_field = (w2 >> 25) & 1; rdp_device.scissor_keep_odd = (w2 >> 24) & 1; - rdp_device.frame_buffer_info.framebuffer_y_offset = upper_left_y; - rdp_device.frame_buffer_info.framebuffer_height = - lower_right_y - upper_left_y; + // parallel-rdp draws subpixel rows/columns in [hi, lo) of the 10.2 + // scissor, so the last pixel touched is (lo - 1) >> 2 + auto &fb = rdp_device.frame_buffer_info; + fb.clip_x0 = int32_t(upper_left_x); + fb.clip_y0 = int32_t(upper_left_y); + fb.clip_x1 = (int32_t((w2 >> 12) & 0xFFF) - 1) >> 2; + fb.clip_y1 = (int32_t(w2 & 0xFFF) - 1) >> 2; } break; case RDP::Op::SetOtherModes: { uint8_t cycle_type = (w1 >> 20) & 3; rdp_device.cycle_type = cycle_type; - uint8_t depth_read_write = (w2 >> 4) & 3; - rdp_device.frame_buffer_info.depth_buffer_enabled = - ((cycle_type & 2) == 0) && (depth_read_write != 0); + // z_compare_en is bit 4, z_update_en is bit 5; ignored in copy/fill + bool depth_allowed = (cycle_type & 2) == 0; + rdp_device.frame_buffer_info.depth_compare = + depth_allowed && (w2 & (1u << 4)); + rdp_device.frame_buffer_info.depth_update = + depth_allowed && (w2 & (1u << 5)); } break; case RDP::Op::SyncFull: - sync_signal = processor->signal_timeline(); - + processor->signal_timeline(); // SyncFull itself adds RDP_SYNC_CLOCKS, so busy_until > now here interrupt_timer = rdp_device.busy_until - now; break; diff --git a/parallel-rdp/interface.hpp b/parallel-rdp/interface.hpp index 5d603640..019469d3 100644 --- a/parallel-rdp/interface.hpp +++ b/parallel-rdp/interface.hpp @@ -63,7 +63,7 @@ uint64_t rdp_process_commands(uint64_t now); void rdp_translate_busy_until(uint64_t old_count, uint64_t new_count); void rdp_idle(); void rdp_onscreen_message(const char *message, MESSAGE_LENGTH milliseconds); -void rdp_check_framebuffers(uint32_t address, uint32_t length); +void rdp_check_rdram(uint32_t address, uint32_t length, bool write); size_t rdp_state_size(); void rdp_save_state(uint8_t *state); void rdp_load_state(GFX_INFO _gfx_info, const uint8_t *state, diff --git a/parallel-rdp/parallel-rdp-standalone b/parallel-rdp/parallel-rdp-standalone index 9ba7406d..9105dc07 160000 --- a/parallel-rdp/parallel-rdp-standalone +++ b/parallel-rdp/parallel-rdp-standalone @@ -1 +1 @@ -Subproject commit 9ba7406d4da0021a26450599714dabec7f8367f7 +Subproject commit 9105dc073feb5c2cc551a4623dd5fde47ebc96d3 diff --git a/parallel-rdp/rdram_tracker.hpp b/parallel-rdp/rdram_tracker.hpp new file mode 100644 index 00000000..84316696 --- /dev/null +++ b/parallel-rdp/rdram_tracker.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include +#include + +// Tracks RDRAM that parallel-rdp may still read or write on the GPU, so an +// emulated access that overlaps it can wait first. +// +// Every 8-byte block holds a stamp: the timeline value that covers the last +// RDP command reading or writing it (0 if never touched). The tracker knows +// nothing about the GPU; callers pass the stamp when marking, and compare the +// stamp returned by conflict() against the GPU's timeline. Stamps never need +// clearing: once the GPU completes a value, every block stamped with it or +// earlier is free. +class RdramTracker { +public: + enum class Access { Read, Write }; + + // forget all stamps, e.g. when the timeline starts over; rdram_size fixes + // the trackable range + void reset(size_t rdram_size) { + write_stamps.assign(rdram_size >> 3, 0); + read_stamps.assign(rdram_size >> 3, 0); + newest = 0; + } + + // the highest stamp ever marked; once the GPU completes it nothing can + // conflict + uint64_t max_stamp() const { return newest; } + + // the RDP accesses [address, address + length) in commands covered by + // timeline value `stamp`. Stamps only grow, so a later mark always wins. + void mark(uint64_t address, uint64_t length, Access access, uint64_t stamp) { + size_t first, last; + if (!block_range(address, length, first, last)) + return; + // timeline values stay far below 2^32 in practice (about one per SyncFull) + uint32_t s = uint32_t(stamp); + newest = std::max(newest, s); + auto &stamps = access == Access::Write ? write_stamps : read_stamps; + std::fill(stamps.begin() + first, stamps.begin() + last + 1, s); + } + + // the RDP accesses pixels [x0, x1] x [y0, y1] of an image `width` pixels + // wide at `base` + void mark_rect(uint32_t base, uint32_t width, uint32_t bytes_per_pixel, + int32_t x0, int32_t x1, int32_t y0, int32_t y1, Access access, + uint64_t stamp) { + if (x1 < x0 || y1 < y0) + return; + uint64_t stride = uint64_t(width) * bytes_per_pixel; + if (x0 == 0 && uint32_t(x1) + 1 == width) { + mark(base + y0 * stride, (y1 - y0 + 1) * stride, access, stamp); + return; + } + for (int32_t y = y0; y <= y1; y++) + mark(base + y * stride + uint64_t(x0) * bytes_per_pixel, + uint64_t(x1 - x0 + 1) * bytes_per_pixel, access, stamp); + } + + // The timeline value an emulated access to [address, address + length) has + // to wait for, 0 if none. Emulated reads conflict with RDP writes; emulated + // writes conflict with RDP reads and writes. + uint64_t conflict(uint64_t address, uint64_t length, bool write) const { + size_t first, last; + if (!block_range(address, length, first, last)) + return 0; + uint32_t stamp = *std::max_element(write_stamps.begin() + first, + write_stamps.begin() + last + 1); + if (write) + stamp = + std::max(stamp, *std::max_element(read_stamps.begin() + first, + read_stamps.begin() + last + 1)); + return stamp; + } + +private: + // the 8-byte blocks [first, last] covering [address, address + length) + bool block_range(uint64_t address, uint64_t length, size_t &first, + size_t &last) const { + uint64_t blocks = write_stamps.size(); + first = address >> 3; + if (length == 0 || first >= blocks) + return false; + last = std::min((address + length - 1) >> 3, blocks - 1); + return true; + } + + std::vector write_stamps; // color / depth buffer writes + std::vector read_stamps; // texture loads, depth compare + uint32_t newest = 0; +}; diff --git a/src/device/pi.rs b/src/device/pi.rs index 4843871a..724a134e 100644 --- a/src/device/pi.rs +++ b/src/device/pi.rs @@ -65,6 +65,9 @@ fn dma_read(device: &mut device::Device) { length += 1; } + // check RDP before PI reads from RDRAM + ui::video::check_rdram_read(dram_addr, length); + let cycles = (handler.read)(device, cart_addr, dram_addr, length); device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles); @@ -92,7 +95,7 @@ fn dma_write(device: &mut device::Device) { } // check RDP before PI writes to RDRAM - ui::video::check_framebuffers(dram_addr, length); + ui::video::check_rdram_write(dram_addr, length); let cycles = (handler.write)(device, cart_addr, dram_addr, length); diff --git a/src/device/rdram.rs b/src/device/rdram.rs index d37903e0..b20961ce 100644 --- a/src/device/rdram.rs +++ b/src/device/rdram.rs @@ -58,7 +58,7 @@ pub fn read_mem( ); let masked_address = address as usize & RDRAM_MASK; - ui::video::check_framebuffers(masked_address as u32, 4); + ui::video::check_rdram_read(masked_address as u32, 4); u32::from_ne_bytes( device @@ -72,13 +72,15 @@ pub fn read_mem( } pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) { - ui::video::check_framebuffers(address as u32, 4); + let masked_address = address as usize & RDRAM_MASK; + + ui::video::check_rdram_write(masked_address as u32, 4); let mut data = u32::from_ne_bytes( device .rdram .mem - .get(address as usize..(address + 4) as usize) + .get(masked_address..masked_address + 4) .unwrap_or(&[0; 4]) .try_into() .unwrap_or_default(), @@ -87,7 +89,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3 device .rdram .mem - .get_mut(address as usize..(address + 4) as usize) + .get_mut(masked_address..masked_address + 4) .unwrap_or(&mut [0; 4]) .copy_from_slice(&data.to_ne_bytes()); } @@ -107,13 +109,15 @@ pub fn write_mem_repeat(device: &mut device::Device, address: u64, value: u32, m ); } - ui::video::check_framebuffers(address as u32, repeat_length); + let masked_address = address as usize & RDRAM_MASK; - for i in (0..repeat_length as u64).step_by(4) { + ui::video::check_rdram_write(masked_address as u32, repeat_length); + + for i in (0..repeat_length as usize).step_by(4) { device .rdram .mem - .get_mut((address + i) as usize..(address + i) as usize + 4) + .get_mut(masked_address + i..masked_address + i + 4) .unwrap_or(&mut [0; 4]) .copy_from_slice(&value.to_ne_bytes()); } diff --git a/src/device/rsp_interface.rs b/src/device/rsp_interface.rs index 985de792..2469ed84 100644 --- a/src/device/rsp_interface.rs +++ b/src/device/rsp_interface.rs @@ -148,8 +148,10 @@ fn do_dma(device: &mut device::Device, dma: RspDma) { let mut dram_addr = dma.dramaddr & 0xfffff8; let offset = dma.memaddr & 0x1000; - ui::video::check_framebuffers(dram_addr, count * (length + skip) - skip); + let dram_length = count * (length + skip) - skip; if dma.dir == DmaDir::Read { + // SP read DMA: DMEM/IMEM -> RDRAM + ui::video::check_rdram_write(dram_addr, dram_length); let mut j = 0; while j < count { let mut i = 0; @@ -174,6 +176,7 @@ fn do_dma(device: &mut device::Device, dma: RspDma) { j += 1; } } else { + ui::video::check_rdram_read(dram_addr, dram_length); let mut j = 0; while j < count { let mut i = 0; diff --git a/src/device/si.rs b/src/device/si.rs index 35460182..8250aff0 100644 --- a/src/device/si.rs +++ b/src/device/si.rs @@ -83,6 +83,8 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u fn copy_pif_rdram(device: &mut device::Device) { let dram_addr = device.si.regs[SI_DRAM_ADDR_REG] as usize & device::rdram::RDRAM_MASK; if device.si.dma_dir == DmaDir::Write { + // check RDP before SI reads from RDRAM + ui::video::check_rdram_read(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32); let mut i = 0; while i < device::pif::PIF_RAM_SIZE { let data = u32::from_ne_bytes( @@ -99,7 +101,7 @@ fn copy_pif_rdram(device: &mut device::Device) { } } else if device.si.dma_dir == DmaDir::Read { // check RDP before SI writes to RDRAM - ui::video::check_framebuffers(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32); + ui::video::check_rdram_write(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32); let mut i = 0; while i < device::pif::PIF_RAM_SIZE { let data = u32::from_be_bytes(device.pif.ram[i..i + 4].try_into().unwrap()); diff --git a/src/ui/video.rs b/src/ui/video.rs index c6fecc63..a654f0ca 100644 --- a/src/ui/video.rs +++ b/src/ui/video.rs @@ -297,8 +297,14 @@ pub fn translate_rdp_busy_until(old_count: u64, new_count: u64) { unsafe { rdp_translate_busy_until(old_count, new_count) } } -pub fn check_framebuffers(address: u32, length: u32) { - unsafe { rdp_check_framebuffers(address, length) } +// emulated read: wait if the RDP has queued writes to this range +pub fn check_rdram_read(address: u32, length: u32) { + unsafe { rdp_check_rdram(address, length, false) } +} + +// emulated write: wait if the RDP has queued reads or writes to this range +pub fn check_rdram_write(address: u32, length: u32) { + unsafe { rdp_check_rdram(address, length, true) } } pub fn onscreen_message(message: &str, milliseconds: MESSAGE_LENGTH) {