mos_hardware/
cbm_kernal.rs

1use bitflags::bitflags;
2use core::error::Error;
3use core::ffi::CStr;
4use core::fmt;
5
6/* automatically generated by rust-bindgen 0.63.0 */
7
8pub const CH_HLINE: u32 = 192;
9pub const CH_VLINE: u32 = 221;
10pub const CH_ULCORNER: u32 = 176;
11pub const CH_URCORNER: u32 = 174;
12pub const CH_LLCORNER: u32 = 173;
13pub const CH_LRCORNER: u32 = 189;
14pub const CH_TTEE: u32 = 178;
15pub const CH_BTEE: u32 = 177;
16pub const CH_LTEE: u32 = 171;
17pub const CH_RTEE: u32 = 179;
18pub const CH_CROSS: u32 = 219;
19pub const CH_CURS_UP: u32 = 145;
20pub const CH_CURS_DOWN: u32 = 17;
21pub const CH_CURS_LEFT: u32 = 157;
22pub const CH_CURS_RIGHT: u32 = 29;
23pub const CH_PI: u32 = 222;
24pub const CH_HOME: u32 = 19;
25pub const CH_DEL: u32 = 20;
26pub const CH_INS: u32 = 148;
27pub const CH_ENTER: u32 = 13;
28pub const CH_STOP: u32 = 3;
29pub const CH_LIRA: u32 = 92;
30pub const CH_ESC: u32 = 27;
31pub const CH_FONT_LOWER: u32 = 14;
32pub const CH_FONT_UPPER: u32 = 142;
33pub const CBM_A_RO: u32 = 1;
34pub const CBM_A_WO: u32 = 2;
35pub const CBM_A_RW: u32 = 3;
36pub const CBM_READ: u32 = 0;
37pub const CBM_WRITE: u32 = 1;
38pub const CBM_SEQ: u32 = 2;
39
40#[repr(C)]
41#[derive(Debug, Default, Copy, Clone)]
42pub struct max_align_t {
43    pub __clang_max_align_nonce1: ::core::ffi::c_longlong,
44    pub __clang_max_align_nonce2: f64,
45}
46
47#[repr(C)]
48#[derive(Debug, Default, Copy, Clone)]
49pub struct cbm_dirent {
50    pub name: [::core::ffi::c_char; 17usize],
51    pub size: ::core::ffi::c_uint,
52    pub type_: ::core::ffi::c_uchar,
53    pub access: ::core::ffi::c_uchar,
54}
55
56extern "C" {
57    pub fn cbm_k_acptr() -> ::core::ffi::c_uchar;
58}
59extern "C" {
60    pub fn cbm_k_basin() -> ::core::ffi::c_uchar;
61}
62extern "C" {
63    pub fn cbm_k_bsout(C: ::core::ffi::c_uchar);
64}
65extern "C" {
66    pub fn cbm_k_chkin(FN: ::core::ffi::c_uchar) -> ::core::ffi::c_uchar;
67}
68extern "C" {
69    pub fn cbm_k_chrin() -> ::core::ffi::c_uchar;
70}
71extern "C" {
72    pub fn cbm_k_chrout(C: ::core::ffi::c_uchar);
73}
74extern "C" {
75    pub fn cbm_k_ciout(C: ::core::ffi::c_uchar);
76}
77extern "C" {
78    pub fn cbm_k_ckout(FN: ::core::ffi::c_uchar) -> ::core::ffi::c_uchar;
79}
80extern "C" {
81    pub fn cbm_k_clall();
82}
83extern "C" {
84    pub fn cbm_k_close(FN: ::core::ffi::c_uchar);
85}
86extern "C" {
87    pub fn cbm_k_clrch();
88}
89extern "C" {
90    pub fn cbm_k_getin() -> ::core::ffi::c_uchar;
91}
92extern "C" {
93    pub fn cbm_k_iobase() -> ::core::ffi::c_uint;
94}
95extern "C" {
96    pub fn cbm_k_listen(dev: ::core::ffi::c_uchar);
97}
98extern "C" {
99    pub fn cbm_k_load(flag: ::core::ffi::c_uchar, addr: ::core::ffi::c_uint)
100        -> ::core::ffi::c_uint;
101}
102extern "C" {
103    pub fn cbm_k_open() -> ::core::ffi::c_uchar;
104}
105extern "C" {
106    pub fn cbm_k_readst() -> ::core::ffi::c_uchar;
107}
108extern "C" {
109    pub fn cbm_k_save(start: ::core::ffi::c_uint, end: ::core::ffi::c_uint)
110        -> ::core::ffi::c_uchar;
111}
112extern "C" {
113    pub fn cbm_k_scnkey();
114}
115extern "C" {
116    pub fn cbm_k_second(addr: ::core::ffi::c_uchar);
117}
118extern "C" {
119    pub fn cbm_k_setlfs(
120        LFN: ::core::ffi::c_uchar,
121        DEV: ::core::ffi::c_uchar,
122        SA: ::core::ffi::c_uchar,
123    );
124}
125extern "C" {
126    pub fn cbm_k_setnam(Name: *const ::core::ffi::c_uchar);
127}
128extern "C" {
129    pub fn cbm_k_settim(timer: ::core::ffi::c_ulong);
130}
131extern "C" {
132    pub fn cbm_k_talk(dev: ::core::ffi::c_uchar);
133}
134extern "C" {
135    pub fn cbm_k_tksa(addr: ::core::ffi::c_uchar);
136}
137extern "C" {
138    pub fn cbm_k_udtim();
139}
140extern "C" {
141    pub fn cbm_k_unlsn();
142}
143extern "C" {
144    pub fn cbm_k_untlk();
145}
146
147bitflags! {
148    /// Status bits for tape and serial I/O as returned by
149    /// the READST kernal routine.
150    pub struct StatusFlags: u8 {
151        /// Serial write time out
152        const WRITE_TIME_OUT = 0b0000_0001; // bit 0
153        /// Serial read time out
154        const READ_TIME_OUT  = 0b0000_0010; // bit 1
155        /// Tape short block
156        const SHORT_BLOCK = 0b0000_0100; // bit 2
157        /// Tape long block
158        const LONG_BLOCK = 0b0000_1000; // bit 3
159        /// Unrecoverable tape read error or serial verify error
160        const READ_ERROR = 0b0001_0000; // bit 4
161        /// Tape checksum error
162        const CHECKSUM_ERROR = 0b0010_0000; // bit 5
163        /// End of file (tape) or identity (serial)
164        const END_OF_IDENTITY = 0b0100_0000; // bit 6
165        /// End of tape or device not present
166        const DEVICE_NOT_PRESENT = 0b1000_0000; // bit 7
167    }
168}
169
170#[derive(Debug)]
171pub enum FileError {
172    TooManyFiles,        // 1
173    FileOpen,            // 2
174    FileNotOpen,         // 3
175    FileNotFound,        // 4
176    DeviceNotPresent,    // 5
177    NotInputFile,        // 6
178    NotOutputFile,       // 7
179    MissingFileName,     // 8
180    IllegalDeviceNumber, // 9
181    StopKeyPushed,       // 10
182    IOError,             // 11
183    Other(u8),
184}
185
186impl FileError {
187    pub const fn new(code: u8) -> Self {
188        match code {
189            1 => Self::TooManyFiles,
190            2 => Self::FileOpen,
191            3 => Self::FileNotOpen,
192            4 => Self::FileNotFound,
193            5 => Self::DeviceNotPresent,
194            6 => Self::NotInputFile,
195            7 => Self::NotOutputFile,
196            8 => Self::MissingFileName,
197            9 => Self::IllegalDeviceNumber,
198            10 => Self::StopKeyPushed,
199            11 => Self::IOError,
200            _ => Self::Other(code),
201        }
202    }
203
204    pub const fn value(&self) -> u8 {
205        match &self {
206            Self::TooManyFiles => 1,
207            Self::FileOpen => 2,
208            Self::FileNotOpen => 3,
209            Self::FileNotFound => 4,
210            Self::DeviceNotPresent => 5,
211            Self::NotInputFile => 6,
212            Self::NotOutputFile => 7,
213            Self::MissingFileName => 8,
214            Self::IllegalDeviceNumber => 9,
215            Self::StopKeyPushed => 10,
216            Self::IOError => 11,
217            Self::Other(value) => *value,
218        }
219    }
220}
221
222impl From<u8> for FileError {
223    fn from(error_code: u8) -> Self {
224        FileError::new(error_code)
225    }
226}
227
228impl From<&FileError> for u8 {
229    fn from(error: &FileError) -> Self {
230        error.value()
231    }
232}
233
234impl Error for FileError {}
235
236impl fmt::Display for FileError {
237    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238        write!(f, "FILE ERROR: {}", u8::from(self))
239    }
240}
241
242/// Loads `filename` from `device` into `address` or to the file load address is `address == None`
243/// Returns number of loaded bytes.
244fn _cbm_load(filename: &CStr, device: u8, load_address: Option<u16>) -> u16 {
245    // logical file numner, lfn, is set to 0; but, it's not needed for loading
246    // (BASIC V2 sets it to the value of the SA for LOAD).
247    let lfn = 0u8;
248    let (address, secondary_address) = match load_address {
249        Some(address) => (address, 0u8),
250        None => (0, 1), // use file load address (first two bytes)
251    };
252    unsafe {
253        cbm_k_setlfs(lfn, device, secondary_address);
254        cbm_k_setnam(filename.to_bytes_with_nul().as_ptr());
255        cbm_k_load(lfn, address) - address
256    }
257}
258
259/// CBM devices
260#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261pub enum Device {
262    Keyboard,
263    Tape,
264    RC232,
265    CRT,
266    Printer,
267    Plotter,
268    Drive8,
269    Drive9,
270    Other(u8),
271}
272
273impl Device {
274    pub const fn value(&self) -> u8 {
275        match *self {
276            Device::Keyboard => 0,
277            Device::Tape => 1,
278            Device::RC232 => 2,
279            Device::CRT => 3,
280            Device::Printer => 4,
281            Device::Plotter => 5,
282            Device::Drive8 => 8,
283            Device::Drive9 => 9,
284            Device::Other(number) => number,
285        }
286    }
287}
288
289impl From<u8> for Device {
290    fn from(value: u8) -> Self {
291        Device::Other(value)
292    }
293}
294
295impl From<Device> for u8 {
296    fn from(device: Device) -> Self {
297        device.value()
298    }
299}
300
301/// Opens a CBM file
302///
303/// # Warning
304/// This is under constuction
305#[derive(Debug, PartialEq, Eq)]
306pub struct File {
307    logical_file_number: u8,
308}
309
310impl File {
311    /// Attempts to open a file in read-only mode.
312    pub fn open(
313        filename: &CStr,
314        device: Device,
315        logical_file_number: u8,
316    ) -> Result<Self, FileError> {
317        unsafe {
318            cbm_k_setlfs(logical_file_number, device.value(), 15);
319            cbm_k_setnam(filename.to_bytes_with_nul().as_ptr());
320        }
321        match unsafe { cbm_k_open() } {
322            0 => Ok(File {
323                logical_file_number,
324            }),
325            _ => Err(FileError::IOError),
326        }
327    }
328}
329
330impl Drop for File {
331    fn drop(&mut self) {
332        unsafe { cbm_k_close(self.logical_file_number) };
333    }
334}
335
336impl genio::Read for File {
337    type ReadError = FileError;
338
339    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError> {
340        // if we can't change to the input channel #lfn then return an error
341        if unsafe { cbm_k_chkin(self.logical_file_number) } != 0 {
342            return Err(FileError::IOError);
343        }
344        let mut bytes_read = 0;
345        while (bytes_read < buf.len()) && (unsafe { cbm_k_readst() } == 0) {
346            let byte = unsafe { cbm_k_basin() };
347            // the kernal routine BASIN sets ST to EOF if the end of file
348            // is reached the first time, then we have store tmp.
349            // every subsequent call returns EOF (bit 6) and READ ERROR in ST, then
350            // we have to exit the loop here immediatly.
351            if (unsafe { cbm_k_readst() } & 0b10111111) == 0 {
352                break;
353            }
354            buf[bytes_read] = byte;
355            bytes_read += 1;
356        }
357        Ok(bytes_read)
358    }
359}