mos_hardware/mega65/
mod.rs

1// copyright 2022 mikael lund aka wombat
2//
3// licensed under the apache license, version 2.0 (the "license");
4// you may not use this file except in compliance with the license.
5// you may obtain a copy of the license at
6//
7//     http://www.apache.org/licenses/license-2.0
8//
9// unless required by applicable law or agreed to in writing, software
10// distributed under the license is distributed on an "as is" basis,
11// without warranties or conditions of any kind, either express or implied.
12// see the license for the specific language governing permissions and
13// limitations under the license.
14
15//! MEGA65 support.
16//!
17//! The MEGA65 is a 100% open-source implementation of the official (but never-released)
18//! Commodore 65 computer. It is in development by associates of the Museum of Electronic
19//! Games and Art e. V., a not-for-profit institution "dedicated to the preservation of
20//! our digital heritage." As well as the original C65 design, the MEGA65 provides
21//! additional hardware and software enhancements, including a choice of using BASIC 10 or
22//! BASIC 65 (containing improvements that go beyond BASIC 10).
23//! The MEGA65 has an 8-bit CPU with additional 32-bit instructions implemented in FPGA.
24//! Like the original C65, it also has a Commodore 64 mode with a level of compatibility
25//! similar to that of the Commodore 128 running in C64 mode
26
27use crate::sid::*;
28use crate::vic2::*;
29use crate::{peek, petscii, poke};
30
31pub mod iomap;
32pub mod libc;
33pub mod math;
34mod memory;
35pub mod random;
36pub use memory::*;
37
38/// Default system palette
39///
40/// The following table is extracted from the MEGA65 user guide.
41///
42/// Code |  Red  | Green | Blue  | Name             | HTML color
43/// ---- | ----- | ----- | ----- | ---------------- | -----------
44/// 0    |    0  |   0   |  0    | Black            | #000000
45/// 1    |   15  |  15   | 15    | White            | #FFFFFF
46/// 2    |   15  |   0   |  0    | Red              | #FF0000
47/// 3    |    0  |  15   | 15    | Cyan             | #00FFFF
48/// 4    |   15  |   0   | 15    | Purple           | #FF00FF
49/// 5    |    0  |  15   |  0    | Green            | #00FF00
50/// 6    |    0  |   0   | 15    | Blue             | #0000FF
51/// 7    |   15  |  15   |  0    | Yellow           | #FFFF00
52/// 8    |   15  |   6   |  0    | Orange           | #FF6F00
53/// 9    |   10  |   4   |  0    | Brown            | #A04000
54/// 10   |   15  |   7   |  7    | Light Red (Pink) | #FF7777
55/// 11   |    5  |   5   |  5    | Dark Grey        | #050505
56/// 12   |    8  |   8   |  8    | Medium Grey      | #080808
57/// 13   |    9  |  15   |  9    | Light Green      | #09FF09
58/// 14   |    9  |   9   | 15    | Light Blue       | #0909FF
59/// 15   |   11  |  11   | 11    | Light Grey       | #0B0B0B
60/// 16   |   14  |   0   |  0    | Guru Meditation  | #E00000
61/// 17   |   15  |   5   |  0    | Rambutan         | #FF5000
62/// 18   |   15  |  11   |  0    | Carrot           | #FF6F00
63/// 19   |   14  |  14   |  0    | Lemon Tart       | #8E8E00
64/// 20   |    7  |  15   |  0    | Pandan           | #07FF00
65/// 21   |    6  |  14   |  6    | Seasick Green    | #06E606
66/// 22   |    0  |  14   |  3    | Soylent Green    | #00E003
67/// 23   |    0  |  15   |  9    | Slimer Green     | #00FF09
68/// 24   |    0  |  13   | 13    | The Other Cyan   | #00DDDD
69/// 25   |    0  |   9   | 15    | Sea Sky          | #009FFF
70/// 26   |    0  |   3   | 15    | Smurf Blue       | #003FFF
71/// 27   |    0  |   0   | 14    | Screen of Death  | #0000E0
72/// 28   |    7  |   0   | 15    | Plum Sauce       | #0700FF
73/// 29   |   12  |   0   | 15    | Sour Grape       | #0C00FF
74/// 30   |   15  |   0   | 11    | Bubblegum        | #FF0B0B
75/// 31   |   15  |   3   |  6    | Hot Tamales      | #FF0306
76#[repr(u8)]
77#[derive(Debug, Copy, Clone, PartialEq)]
78pub enum DefaultPalette {
79    /// Black color (<a style="color:#000000;">&#9679;</a>)
80    Black = 0,
81    /// White color (<a style="color:#FFFFFF;">&#9679;</a>)
82    White = 1,
83    /// Red color (<a style="color:#FF0000;">&#9679;</a>)
84    Red = 2,
85    /// Cyan color (<a style="color:#00FFFF;">&#9679;</a>)
86    Cyan = 3,
87    /// Purple color (<a style="color:#FF00FF;">&#9679;</a>)
88    Purple = 4,
89    /// Green color (<a style="color:#00FF00;">&#9679;</a>)
90    Green = 5,
91    /// Blue color (<a style="color:#0000FF;">&#9679;</a>)
92    Blue = 6,
93    /// Yellow color (<a style="color:#FFFF00;">&#9679;</a>)
94    Yellow = 7,
95    /// Orange color (<a style="color:#FF6F00;">&#9679;</a>)
96    Orange = 8,
97    /// Brown color (<a style="color:#A04000;">&#9679;</a>)
98    Brown = 9,
99    /// Light Red (Pink) color (<a style="color:#FF7777;">&#9679;</a>)
100    LightRed = 10,
101    /// Dark Grey color (<a style="color:#050505;">&#9679;</a>)
102    DarkGrey = 11,
103    /// Medium Grey color (<a style="color:#080808;">&#9679;</a>)
104    MediumGrey = 12,
105    /// Light Green color (<a style="color:#09FF09;">&#9679;</a>)
106    LightGreen = 13,
107    /// Light Blue color (<a style="color:#0909FF;">&#9679;</a>)
108    LightBlue = 14,
109    /// Light Grey color (<a style="color:#0B0B0B;">&#9679;</a>)
110    LightGrey = 15,
111    /// Guru Meditation color (<a style="color:#E00000;">&#9679;</a>)
112    GuruMeditation = 16,
113    /// Rambutan color (<a style="color:#FF5000;">&#9679;</a>)
114    Rambutan = 17,
115    /// Carrot color (<a style="color:#FF6F00;">&#9679;</a>)
116    Carrot = 18,
117    /// Lemon Tart color (<a style="color:#8E8E00;">&#9679;</a>)
118    LemonTart = 19,
119    /// Pandan color (<a style="color:#07FF00;">&#9679;</a>)
120    Pandan = 20,
121    /// Seasick Green color (<a style="color:#06E606;">&#9679;</a>)
122    SeasickGreen = 21,
123    /// Soylent Green color (<a style="color:#00E003;">&#9679;</a>)
124    SoylentGreen = 22,
125    /// Slimer Green color (<a style="color:#00FF09;">&#9679;</a>)
126    SlimerGreen = 23,
127    /// The Other Cyan color (<a style="color:#00DDDD;">&#9679;</a>)
128    TheOtherCyan = 24,
129    /// Sea Sky color (<a style="color:#009FFF;">&#9679;</a>)
130    SeaSky = 25,
131    /// Smurf Blue color (<a style="color:#003FFF;">&#9679;</a>)
132    SmurfBlue = 26,
133    /// Screen of Death color (<a style="color:#0000E0;">&#9679;</a>)
134    ScreenOfDeath = 27,
135    /// Plum Sauce color (<a style="color:#0700FF;">&#9679;</a>)
136    PlumSauce = 28,
137    /// Sour Grape color (<a style="color:#0C00FF;">&#9679;</a>)
138    SourGrape = 29,
139    /// Bubblegum color (<a style="color:#FF0B0B;">&#9679;</a>)
140    Bubblegum = 30,
141    /// Hot Tamales color (<a style="color:#FF0306;">&#9679;</a>)
142    HotTamales = 31,
143}
144
145pub const DEFAULT_SCREEN: *mut u8 = (0x0800) as *mut u8;
146pub const DEFAULT_UPPERCASE_FONT: *mut u8 = (0x1000) as *mut u8;
147pub const DEFAULT_MIXEDCASE_FONT: *mut u8 = (0x1800) as *mut u8;
148
149pub const VICII: *const MOSVideoInterfaceControllerII =
150    (0xd000) as *const MOSVideoInterfaceControllerII;
151
152/// Pointer to first sound interface device
153pub const SID0: *const MOSSoundInterfaceDevice = (0xd400) as *const MOSSoundInterfaceDevice;
154/// Pointer to second sound interface device
155pub const SID1: *const MOSSoundInterfaceDevice = (0xd420) as *const MOSSoundInterfaceDevice;
156/// Pointer to third sound interface device
157pub const SID2: *const MOSSoundInterfaceDevice = (0xd440) as *const MOSSoundInterfaceDevice;
158/// Pointer to fourth sound interface device
159pub const SID3: *const MOSSoundInterfaceDevice = (0xd460) as *const MOSSoundInterfaceDevice;
160
161pub const COLOR_RAM: *mut u8 = (0xd800) as *mut u8;
162
163/// Math multiplication-division status flags
164pub const MATH_STATUS: *const volatile_register::RO<math::StatusFlags> =
165    (0xd70f) as *const volatile_register::RO<math::StatusFlags>;
166
167/// Math Acceleration registers
168pub const MATH_ACCELERATOR: *const math::MathAccelerator = (0xd768) as *const math::MathAccelerator;
169
170pub enum VicBank {
171    Region0000 = 0x11, // Bank 0
172    Region4000 = 0x10, // Bank 1
173    Region8000 = 0x01, // Bank 2
174    RegionC000 = 0x00, // Bank 3
175}
176
177/// Get reference to VIC2 chip
178pub const fn vic2() -> &'static MOSVideoInterfaceControllerII {
179    unsafe { &*VICII }
180}
181
182/// Get reference to first SID chip
183pub const fn sid0() -> &'static MOSSoundInterfaceDevice {
184    unsafe { &*SID0 }
185}
186
187/// Get reference to second SID chip
188pub const fn sid1() -> &'static MOSSoundInterfaceDevice {
189    unsafe { &*SID1 }
190}
191
192/// Get reference to third SID chip
193pub const fn sid2() -> &'static MOSSoundInterfaceDevice {
194    unsafe { &*SID2 }
195}
196
197/// Get reference to fourth SID chip
198pub const fn sid3() -> &'static MOSSoundInterfaceDevice {
199    unsafe { &*SID3 }
200}
201
202/// Get reference to math accelerator
203pub const fn math_accelerator() -> &'static math::MathAccelerator {
204    unsafe { &*MATH_ACCELERATOR }
205}
206
207/// Set CPU speed to 1 Mhz
208pub fn speed_mode1() {
209    unsafe {
210        let mut val: u8 = peek!(0xd031 as *mut u8) & 0b1011_1111; // unset FAST bit
211        poke!(0xd031 as *mut u8, val);
212        val = peek!(0xd054 as *mut u8) & 0b1011_1111; // unset VFAST bit
213        poke!(0xd054 as *mut u8, val);
214    }
215}
216
217/// Set CPU speed to 3.5 Mhz
218pub fn speed_mode3() {
219    unsafe {
220        let mut val: u8 = peek!(0xd031 as *mut u8) | 0b0100_0000; // set FAST bit
221        poke!(0xd031 as *mut u8, val);
222        val = peek!(0xd054 as *mut u8) & 0b1011_1111; // unset VFAST
223        poke!(0xd054 as *mut u8, val);
224    }
225}
226
227/// Set CPU speed to 40 Mhz
228pub fn speed_mode40() {
229    unsafe {
230        let mut val: u8 = peek!(0xd031 as *mut u8) | 0b0100_0000; // set FAST bit
231        poke!(0xd031 as *mut u8, val);
232        val = peek!(0xd054 as *mut u8) | 0b0100_0000; // set VFAST bit
233        poke!(0xd054 as *mut u8, val);
234    }
235}
236
237/// Struct used to store widht-height resolutions
238#[derive(Default)]
239pub struct Resolution<T> {
240    pub width: T,
241    pub height: T,
242}
243
244/// Returns screen resolution (char width, char heigh)
245pub fn get_screen_size() -> Resolution<u8> {
246    let mut resolution = Resolution::default();
247    unsafe {
248        libc::getscreensize(&mut resolution.width, &mut resolution.height);
249    }
250    resolution
251}
252
253/// Initialises the conio internal state
254///
255/// This must be called before using any conio library function.
256pub fn conio_init() {
257    unsafe {
258        libc::conioinit();
259    }
260}
261
262/// Shift to lower case ROM charset
263pub fn set_lower_case() {
264    unsafe {
265        libc::setlowercase();
266    }
267}
268
269/// Shift to upper case ROM charset
270pub fn set_upper_case() {
271    unsafe {
272        libc::setuppercase();
273    }
274}
275
276/// Clear all chars on screen
277pub fn clear_screen() {
278    unsafe {
279        libc::clrscr();
280    }
281}
282
283/// Goto top left corner
284pub fn go_home() {
285    unsafe {
286        libc::gohome();
287    }
288}
289
290/// Goto specific character position
291pub fn goto_xy(x: u8, y: u8) {
292    unsafe {
293        libc::gotoxy(x, y);
294    }
295}
296
297/// Output multiple screen codes at X,Y coordinates
298///
299/// Works with _null-terminated_ screen codes only.
300///
301/// # Examples
302/// ~~~
303/// use mos_hardware::{petscii, petscii_null}
304/// mega65::cputs_xy(2, 3, [8, 5, 12, 12, 15, 0].as_slice());
305/// mega65::cputs_xy(4, 6, petscii_null!("hello").as_slice());
306/// ~~~
307pub fn cputs_xy(x: u8, y: u8, screen_codes: &[u8]) {
308    assert_eq!(*screen_codes.last().unwrap(), 0u8);
309    unsafe {
310        libc::cputsxy(x, y, screen_codes.as_ptr());
311    }
312}
313
314/// Output screen codes at current position
315///
316/// Works with _null-terminated_ screen codes only.
317///
318/// # Examples
319/// ~~~
320/// use mos_hardware::{petscii, petscii_null}
321/// mega65::cputs(petscii_null!("hello").as_slice());
322/// ~~~
323pub fn cputs(screen_codes: &[u8]) {
324    assert_eq!(*screen_codes.last().unwrap(), 0u8);
325    unsafe {
326        libc::cputs(screen_codes.as_ptr());
327    }
328}
329
330/// Flush keyboard buffer
331pub fn flush_keyboard_buffer() {
332    unsafe {
333        libc::flushkeybuf();
334    }
335}
336
337/// Waits until a character is in the keyboard buffer and returns as petscii
338pub fn cgetc() -> petscii::Petscii {
339    unsafe { libc::cgetc() }.into()
340}
341
342/// Sets the current border color
343pub fn set_border_color(color: u8) {
344    unsafe {
345        libc::bordercolor(color);
346    }
347}
348
349/// Sets the current screen (background) color
350pub fn set_background_color(color: u8) {
351    unsafe {
352        libc::bgcolor(color);
353    }
354}
355
356/// Sets the current text color
357pub fn set_text_color(color: u8) {
358    unsafe {
359        libc::textcolor(color);
360    }
361}
362
363/// Read real time clock
364///
365/// # Examples
366/// ~~~
367/// let rtc = mega65::get_real_time_clock();
368/// println!("TIME = {}:{}:{}", rtc.tm_hour, rtc.tm_min, rtc.tm_sec);
369/// ~~~
370pub fn get_real_time_clock() -> libc::m65_tm {
371    let mut rtc = libc::m65_tm::default();
372    unsafe {
373        libc::getrtc(&mut rtc);
374    }
375    rtc
376}
377
378/// Sets VIC-III extended attributes mode to enable blink, underline, bold, highlight
379pub fn set_extended_attributes() {
380    unsafe {
381        libc::setextendedattrib(1);
382    }
383}
384
385/// Clears VIC-III extended attributes mode to disable blink, underline, bold, highlight
386pub fn unset_extended_attributes() {
387    unsafe {
388        libc::setextendedattrib(0);
389    }
390}
391
392/// Set character set address using mega65-libc
393pub fn set_charset_address(address: u16) {
394    unsafe {
395        libc::setcharsetaddr(address as i32);
396    }
397}