Skip to main content

ymfm_sys/
callback.rs

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