Skip to main content

ymfm_sys/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use cxx::UniquePtr;
4
5mod callback;
6
7pub use callback::{InterfaceCallbacks, InterfaceHandler};
8pub(crate) use callback::{
9    advance_clock, default_callbacks, read_data, write_data, ymfm_external_read,
10    ymfm_external_write, ymfm_is_busy, ymfm_set_busy_end, ymfm_set_timer, ymfm_update_irq,
11};
12
13/// Convenience alias for the common case of holding a chip instance.
14pub type ChipPtr = UniquePtr<ffi::Chip>;
15
16#[cxx::bridge(namespace = "ymfm_sys")]
17pub mod ffi {
18    extern "Rust" {
19        type InterfaceCallbacks;
20
21        fn default_callbacks() -> Box<InterfaceCallbacks>;
22
23        fn advance_clock(callbacks: &InterfaceCallbacks, clocks: i64) -> u8;
24        fn read_data(
25            callbacks: &InterfaceCallbacks,
26            access: AccessClass,
27            base: u32,
28            length: u32,
29        ) -> Vec<u8>;
30        fn write_data(callbacks: &InterfaceCallbacks, access: AccessClass, base: u32, data: &[u8]);
31        fn ymfm_external_read(
32            callbacks: &InterfaceCallbacks,
33            access: AccessClass,
34            offset: u32,
35        ) -> u8;
36        fn ymfm_external_write(
37            callbacks: &InterfaceCallbacks,
38            access: AccessClass,
39            offset: u32,
40            data: u8,
41        );
42        fn ymfm_is_busy(callbacks: &InterfaceCallbacks) -> bool;
43        fn ymfm_set_busy_end(callbacks: &InterfaceCallbacks, clocks: u32);
44        fn ymfm_set_timer(callbacks: &InterfaceCallbacks, tnum: u32, duration_in_clocks: i32);
45        fn ymfm_update_irq(callbacks: &InterfaceCallbacks, asserted: bool);
46    }
47
48    /// Supported Yamaha FM/SSG chip families.
49    ///
50    /// `Ym2610B` exists only to select the YM2610B variant at creation time;
51    /// `Chip::chip_type` normalizes it back to `Ym2610`, matching how ymfm
52    /// itself treats the two revisions identically for register routing.
53    #[repr(u32)]
54    enum ChipType {
55        Ym2149,
56        Ym2151,
57        Ym2164,
58        Ym2203,
59        Ym2413,
60        Ym2423,
61        Ym2608,
62        Ym2610,
63        Ym2610B,
64        Ym2612,
65        Ym3438,
66        Ymf276,
67        Ym3526,
68        Ym3533,
69        Y8950,
70        Ym3812,
71        Ymf262,
72        Ymf281,
73        Ymf289B,
74        Ymf278B,
75        Ymf288,
76        Ym3806,
77        Ds1001,
78        Ym2414,
79    }
80
81    /// External data classes a chip may read ROM/RAM data from.
82    #[repr(u32)]
83    enum AccessClass {
84        Io,
85        AdpcmA,
86        AdpcmB,
87        Pcm,
88    }
89
90    /// Sample-rate/accuracy tradeoff, via the ymfm `opn_fidelity` setting.
91    /// Only meaningful for YM2203/YM2608/YM2610/YM2610B; a no-op elsewhere.
92    #[repr(u32)]
93    enum Fidelity {
94        Max,
95        Min,
96        Med,
97    }
98
99    unsafe extern "C++" {
100        include!("ymfm-sys/src/shim.h");
101
102        /// Opaque handle to a single emulated chip instance.
103        type Chip;
104
105        /// Create a chip with all optional interface callbacks disabled.
106        fn create_chip(chip_type: ChipType, clock: u32) -> UniquePtr<Chip>;
107
108        /// Create a chip and forward ymfm interface callbacks to `callbacks`.
109        fn create_chip_with_callbacks(
110            chip_type: ChipType,
111            clock: u32,
112            callbacks: Box<InterfaceCallbacks>,
113        ) -> UniquePtr<Chip>;
114
115        /// Which chip this instance represents.
116        fn chip_type(self: &Chip) -> ChipType;
117
118        /// Number of output channels this chip produces per generated sample
119        /// (via the concrete ymfm chip class's `OUTPUTS` constant).
120        fn channels(self: &Chip) -> u32;
121
122        /// Native output sample rate for the clock this chip was created
123        /// with (via the ymfm `sample_rate(uint32_t input_clock)` API).
124        fn sample_rate(self: &Chip) -> u32;
125
126        /// Reset the chip to its post-power-on state (via the ymfm `reset()` API).
127        fn reset(self: Pin<&mut Chip>);
128
129        /// Select the sample-rate/accuracy tradeoff (via the ymfm
130        /// `set_fidelity(opn_fidelity)`). Only meaningful for
131        /// YM2203/YM2608/YM2610/YM2610B; a no-op on other chips.
132        fn set_fidelity(self: Pin<&mut Chip>, fidelity: Fidelity);
133
134        /// Replace the 0x90-byte instrument data on OPLL-family chips.
135        /// Returns false for unsupported chip types or an incorrectly sized
136        /// data buffer.
137        fn set_instrument_data(self: Pin<&mut Chip>, data: &[u8]) -> bool;
138
139        /// Write to a register or chip port at `offset`, via the upstream
140        /// `write(offset, data)` API. The common mapping is 0/1 for the
141        /// address/data ports. OPN/OPNA chips generally use 2/3 for their
142        /// extended address/data ports; YMF262/YMF289B use 2 for the upper
143        /// address and 3 for regular data; YMF278B uses 4/5 for PCM
144        /// address/data. YM2149 uses 2 for write data. Unsupported offsets
145        /// follow the selected chip's upstream behavior.
146        fn write(self: Pin<&mut Chip>, offset: u32, data: u8);
147
148        /// Read from a chip port at `offset`, via the upstream `read(offset)`
149        /// API. The common mapping is 0 for status and 1 for data; extended
150        /// status/data and chip-specific ports use the offsets defined by the
151        /// selected upstream chip. YM2149 reads its data port at offset 3.
152        fn read(self: Pin<&mut Chip>, offset: u32) -> u8;
153
154        /// Generate `buffer.len() / channels()` samples at the chip's native
155        /// sample rate, overwriting `buffer` (channel-interleaved); wraps
156        /// the ymfm `generate(output_data*, numsamples)` API. This generates
157        /// one native sample at a time. For each sample, it also advances the
158        /// internal clock counter used by
159        /// timers, including those required by modes such as CSM, and by
160        /// BUSY state tracking.
161        fn generate(self: Pin<&mut Chip>, buffer: &mut [i32]);
162
163        /// Serialize the full internal chip state via the ymfm
164        /// `save_restore(ymfm_saved_state&)` with `saving = true`).
165        fn save_state(self: Pin<&mut Chip>) -> Vec<u8>;
166
167        /// Restore state previously produced by `save_state` (via the ymfm
168        /// `save_restore(ymfm_saved_state&)` with `saving = false`). The
169        /// chip must be of the same type and clock as when the state was
170        /// saved; ymfm does not version or validate the saved data itself.
171        fn restore_state(self: Pin<&mut Chip>, data: &[u8]);
172    }
173}