1use bitflags::bitflags;
2use core::error::Error;
3use core::ffi::CStr;
4use core::fmt;
5
6pub 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 pub struct StatusFlags: u8 {
151 const WRITE_TIME_OUT = 0b0000_0001; const READ_TIME_OUT = 0b0000_0010; const SHORT_BLOCK = 0b0000_0100; const LONG_BLOCK = 0b0000_1000; const READ_ERROR = 0b0001_0000; const CHECKSUM_ERROR = 0b0010_0000; const END_OF_IDENTITY = 0b0100_0000; const DEVICE_NOT_PRESENT = 0b1000_0000; }
168}
169
170#[derive(Debug)]
171pub enum FileError {
172 TooManyFiles, FileOpen, FileNotOpen, FileNotFound, DeviceNotPresent, NotInputFile, NotOutputFile, MissingFileName, IllegalDeviceNumber, StopKeyPushed, IOError, 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
242fn _cbm_load(filename: &CStr, device: u8, load_address: Option<u16>) -> u16 {
245 let lfn = 0u8;
248 let (address, secondary_address) = match load_address {
249 Some(address) => (address, 0u8),
250 None => (0, 1), };
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#[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#[derive(Debug, PartialEq, Eq)]
306pub struct File {
307 logical_file_number: u8,
308}
309
310impl File {
311 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 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 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}