Skip to main content

ymfm_sys/
callback.rs

1use std::cell::RefCell;
2
3use crate::ffi;
4
5/// User-provided ymfm interface handlers. Each callback is optional; omitted
6/// callbacks use the corresponding no-op/default behavior.
7#[derive(Default)]
8pub struct InterfaceHandler {
9    pub advance_clock: Option<Box<dyn FnMut(i64) -> u8>>,
10    pub read_data: Option<Box<dyn Fn(ffi::AccessClass, u32, u32) -> Vec<u8>>>,
11    pub write_data: Option<Box<dyn FnMut(ffi::AccessClass, u32, &[u8])>>,
12    pub ymfm_external_read: Option<Box<dyn FnMut(ffi::AccessClass, u32) -> u8>>,
13    pub ymfm_external_write: Option<Box<dyn FnMut(ffi::AccessClass, u32, u8)>>,
14    pub ymfm_is_busy: Option<Box<dyn Fn() -> bool>>,
15    pub ymfm_set_busy_end: Option<Box<dyn FnMut(u32)>>,
16    pub ymfm_set_timer: Option<Box<dyn FnMut(u32, i32)>>,
17    pub ymfm_update_irq: Option<Box<dyn FnMut(bool)>>,
18}
19
20/// Opaque callback adapter owned by the native chip instance.
21pub struct InterfaceCallbacks {
22    handler: RefCell<InterfaceHandler>,
23}
24
25impl InterfaceCallbacks {
26    /// Create a callback adapter containing the supplied host handlers.
27    pub fn new(handler: InterfaceHandler) -> Self {
28        Self {
29            handler: RefCell::new(handler),
30        }
31    }
32}
33
34/// Create an adapter whose callbacks all use their default behavior.
35pub(crate) fn default_callbacks() -> Box<InterfaceCallbacks> {
36    Box::new(InterfaceCallbacks::new(InterfaceHandler::default()))
37}
38
39/// Forward ymfm's IRQ state change to the host callback, if configured.
40pub(crate) fn ymfm_update_irq(callbacks: &InterfaceCallbacks, asserted: bool) {
41    if let Some(handler) = callbacks.handler.borrow_mut().ymfm_update_irq.as_mut() {
42        handler(asserted);
43    }
44}
45
46/// Advance host-side time and return the timer-expiry bit mask from the callback.
47/// Returns `0` when no callback is configured.
48pub(crate) fn advance_clock(callbacks: &InterfaceCallbacks, clocks: i64) -> u8 {
49    callbacks
50        .handler
51        .borrow_mut()
52        .advance_clock
53        .as_mut()
54        .map_or(0, |handler| handler(clocks))
55}
56
57/// Forward ymfm's BUSY deadline to the host callback, if configured.
58pub(crate) fn ymfm_set_busy_end(callbacks: &InterfaceCallbacks, clocks: u32) {
59    if let Some(handler) = callbacks.handler.borrow_mut().ymfm_set_busy_end.as_mut() {
60        handler(clocks);
61    }
62}
63
64/// Ask the host whether the emulated device is BUSY.
65/// Returns `false` when no callback is configured.
66pub(crate) fn ymfm_is_busy(callbacks: &InterfaceCallbacks) -> bool {
67    callbacks
68        .handler
69        .borrow()
70        .ymfm_is_busy
71        .as_ref()
72        .map_or(false, |handler| handler())
73}
74
75/// Forward a timer number and duration to the host callback, if configured.
76pub(crate) fn ymfm_set_timer(callbacks: &InterfaceCallbacks, tnum: u32, duration_in_clocks: i32) {
77    if let Some(handler) = callbacks.handler.borrow_mut().ymfm_set_timer.as_mut() {
78        handler(tnum, duration_in_clocks);
79    }
80}
81
82/// Read one byte from ymfm's external memory or I/O interface.
83/// Returns `0` when no callback is configured.
84pub(crate) fn ymfm_external_read(
85    callbacks: &InterfaceCallbacks,
86    access: ffi::AccessClass,
87    offset: u32,
88) -> u8 {
89    callbacks
90        .handler
91        .borrow_mut()
92        .ymfm_external_read
93        .as_mut()
94        .map_or(0, |handler| handler(access, offset))
95}
96
97/// Read a block from host-managed external data.
98/// Returns zero-filled data when no callback is configured.
99pub(crate) fn read_data(
100    callbacks: &InterfaceCallbacks,
101    access: ffi::AccessClass,
102    base: u32,
103    length: u32,
104) -> Vec<u8> {
105    callbacks.handler.borrow().read_data.as_ref().map_or_else(
106        || vec![0; length as usize],
107        |handler| handler(access, base, length),
108    )
109}
110
111/// Forward one byte written through ymfm's external memory or I/O interface.
112pub(crate) fn ymfm_external_write(
113    callbacks: &InterfaceCallbacks,
114    access: ffi::AccessClass,
115    offset: u32,
116    data: u8,
117) {
118    if let Some(handler) = callbacks.handler.borrow_mut().ymfm_external_write.as_mut() {
119        handler(access, offset, data);
120    }
121}
122
123/// Forward a block write to host-managed external data, if configured.
124pub(crate) fn write_data(
125    callbacks: &InterfaceCallbacks,
126    access: ffi::AccessClass,
127    base: u32,
128    data: &[u8],
129) {
130    if let Some(handler) = callbacks.handler.borrow_mut().write_data.as_mut() {
131        handler(access, base, data);
132    }
133}