mos_hardware/
c64.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//! Commodore 64 support.
16//!
17//! Future information may be incorporated using the
18//! [Ultimate Commodore 64 Reference](https://github.com/mist64/c64ref)
19
20use crate::cia::*;
21use crate::sid::*;
22use crate::vic2::*;
23use bitflags::bitflags;
24use volatile_register::RW;
25
26bitflags! {
27    /// Control flags for the CPU port `R6510` at 0x0001
28    ///
29    /// Three-word combination constants like `RAM_IO_KERNAL` refer to banking configurations
30    /// of what is visible at addresses `$A000-BFFF`, `$D000-DFFF`, and `$E000-FFFF`.
31    /// Regardless of `0x0001`, the VIC-II chip *always* sees the `CHARROM` at `$1000-1FFF` and `$9000-9FFF`,
32    /// and RAM everywhere else.
33    ///
34    /// [More information](https://codebase64.org/doku.php?id=base:memory_management).
35    ///
36    /// # Examples
37    ///
38    /// Here's an example that makes the RAM available "under" both the BASIC and KERNAL
39    /// ROMs located at 0xA000-0xBFFF and 0xE000-0xFFFF.
40    /// The VIC, SID, and CIA I/O devices are left accessible at 0xD000-0xDFFF:
41    /// ~~~
42    /// (*CPU_PORT).write(CpuPortFlags::RAM_IO_RAM);
43    /// assert_eq!(CpuPortFlags::RAM_IO_RAM.bits(), 0x35);
44    /// assert_eq!(CpuPortFlags::RAM_IO_KERNAL.bitw(), 0x36);
45    /// ~~~
46    pub struct CpuPortFlags: u8 {
47        const DEFAULT              = Self::BASIC_IO_KERNAL.bits;
48        const BASIC_IO_KERNAL      = 0b00110111;
49        const RAM_RAM_RAM          = 0b00110000;
50        const RAM_CHAR_RAM         = 0b00110001;
51        const RAM_CHAR_KERNAL      = 0b00110010;
52        const BASIC_CHAR_KERNAL    = 0b00110011;
53        const RAM_IO_RAM           = 0b00110101;
54        const RAM_IO_KERNAL        = 0b00110110;
55        const DATASETTE_SIGNAL     = 0b00001000; // bit 3
56        const DATASETTE_BUTTON_OFF = 0b00010000; // bit 4
57        const DATASETTE_MOTOR_OFF  = 0b00100000; // bit 5
58    }
59}
60
61/// Pointer to the `R6510` register for 6510 I/O (0x0001)
62pub const CPU_PORT: *mut RW<CpuPortFlags> = (0x0001) as *mut RW<CpuPortFlags>;
63
64/// Pointer to beginning of default video memory (0x0400)
65pub const DEFAULT_VIDEO_MEMORY: *mut u8 = (0x0400) as *mut u8;
66
67/// Pointer to the default video matrix area (0x0400)
68///
69/// The video matrix is where text screen characters are stored in RAM.
70/// By default this corresponds to 25 lines, each with 40 columns.
71pub const DEFAULT_VIDEO_MATRIX: *mut [u8; 25 * 40] = (0x0400) as *mut [u8; 25 * 40];
72
73/// Default sprite shape pointers (0x0400)
74///
75/// This is the default location for sprite shape pointers, i.e.
76/// relative to the default screen memory location 0x0400.
77/// Individual sprite shape pointers can be calculated with
78/// `vic2::to_sprite_pointer()`.
79pub const DEFAULT_SPRITE_PTR: [*mut u8; 8] = [
80    (0x0400 + 0x3F8) as *mut u8,
81    (0x0400 + 0x3F8 + 1) as *mut u8,
82    (0x0400 + 0x3F8 + 2) as *mut u8,
83    (0x0400 + 0x3F8 + 3) as *mut u8,
84    (0x0400 + 0x3F8 + 4) as *mut u8,
85    (0x0400 + 0x3F8 + 5) as *mut u8,
86    (0x0400 + 0x3F8 + 6) as *mut u8,
87    (0x0400 + 0x3F8 + 7) as *mut u8,
88];
89
90/// Default upper case font in the CHARROM (0x1000)
91pub const DEFAULT_UPPERCASE_FONT: *mut u8 = (0x1000) as *mut u8;
92
93/// Default mixed case font in the CHARROM (0x1800)
94pub const DEFAULT_MIXEDCASE_FONT: *mut u8 = (0x1800) as *mut u8;
95
96/// Pointer to BASIC ROM start (0xa000)
97pub const BASIC_ROM: *mut u8 = (0xa000) as *mut u8;
98
99/// Pointer to BASIC ROM area (0xa000 - 0xbfff)
100pub const BASIC_ROM_AREA: *mut [u8; 0x1fff] = (0xa000) as *mut [u8; 0x1fff];
101
102/// Pointer to the video interface controller (0xd000)
103pub const VIC: *const MOSVideoInterfaceControllerII =
104    (0xd000) as *const MOSVideoInterfaceControllerII;
105
106/// Pointer to the sound interface device (0xd400)
107pub const SID: *const MOSSoundInterfaceDevice = (0xd400) as *const MOSSoundInterfaceDevice;
108
109/// Pointer to default color RAM (0xd800)
110pub const COLOR_RAM: *mut u8 = (0xd800) as *mut u8;
111
112/// Pointer to first complex interface adapter (0xdc00)
113pub const CIA1: *const MOSComplexInterfaceAdapter6526 =
114    (0xdc00) as *const MOSComplexInterfaceAdapter6526;
115
116/// Pointer to second complex interface adapter (0xdd00)
117pub const CIA2: *const MOSComplexInterfaceAdapter6526 =
118    (0xdd00) as *const MOSComplexInterfaceAdapter6526;
119
120/// Pointer to the KERNAL ROM memory area (0xe000 - 0xffff)
121pub const KERNAL_ROM: *mut [u8; 8192] = (0xe000) as *mut [u8; 8192];
122
123bitflags! {
124    /// Flags for the `CIA1::control_a` register (0xdc0e)
125    pub struct CIA1ControlAFlags: u8 {
126        /// Start (1) or stop (0) timer A
127        const START         = 0b00000001; // bit 0
128        const PBON          = 0b00000010;
129        const OUTMODE       = 0b00000100;
130        const RUNMODE       = 0b00001000;
131        const FORCE_LOAD    = 0b00010000;
132        const INMODE        = 0b00100000;
133        const SERIAL_OUTPUT = 0b01000000;
134        const FIFTY_HZ_RTC  = 0b10000000;
135    }
136}
137
138pub enum VicBank {
139    Region0000 = 0x11, // Bank 0
140    Region4000 = 0x10, // Bank 1
141    Region8000 = 0x01, // Bank 2
142    RegionC000 = 0x00, // Bank 3
143}
144
145extern "C" {
146    // defined in c to allow assembly and interrupt attribute
147    fn hardware_raster_irq_c(triggering_raster_line: u8);
148}
149
150/// Setup hardware raster interrupt (0xfffe)
151///
152/// This registers a Rust function, `called_every_frame()` to be triggered
153/// at a specific raster line. The BASIC and KERNAL roms are disabled so
154/// suffix your main program with an endless loop.
155/// `fn called_every_frame()` must be defined and *exported*
156/// on the Rust side and will be called from C via a wrapper. This is because
157/// the LLVM `__interrupt__` attribute is currently not available from Rust.
158///
159/// # Examples
160/// ```
161/// #[no_mangle]
162/// pub unsafe extern fn called_every_frame() {
163///    ...
164/// }
165///
166/// #[start]
167/// fn _main(_argc: isize, _argv: *const *const u8) -> isize {
168///    c64::hardware_raster_irq(100); // trigger at raster line 100
169///    loop {}                        // let's not return to dead BASIC
170/// }
171/// ```
172pub fn hardware_raster_irq(triggering_raster_line: u8) {
173    unsafe {
174        hardware_raster_irq_c(triggering_raster_line);
175    }
176}
177
178/// Special keyboard and PETSCII codes
179pub enum Keyboard {
180    Delete = 0x14,
181    Stop = 0x03,
182    Return = 0x0d,
183    Home = 0x13,
184    CursorDown = 0x11,
185    CursorRight = 0x1d,
186    Space = 0x20,
187    ArrowLeft = 0x5f,
188    Run = 0x83,
189    F1 = 0x85,
190    F2 = 0x86,
191    F3 = 0x87,
192    F4 = 0x88,
193    F5 = 0x89,
194    F6 = 0x8a,
195    F7 = 0x8b,
196    F8 = 0x8c,
197    ShiftReturn = 0x8d,
198    CursorUp = 0x91,
199    Clear = 0x93,
200    Insert = 0x94,
201    CursorLeft = 0x9d,
202}
203
204/// Get reference to VIC2 chip
205pub const fn vic2() -> &'static MOSVideoInterfaceControllerII {
206    unsafe { &*VIC }
207}
208
209/// Get reference to SID chip
210pub const fn sid() -> &'static MOSSoundInterfaceDevice {
211    unsafe { &*SID }
212}
213
214/// Get reference to CIA1 chip
215pub const fn cia1() -> &'static MOSComplexInterfaceAdapter6526 {
216    unsafe { &*CIA1 }
217}
218
219/// Get reference to CIA2 chip
220pub const fn cia2() -> &'static MOSComplexInterfaceAdapter6526 {
221    unsafe { &*CIA2 }
222}
223
224/// Clears screen, functional style (fill with SPACE character)
225pub fn clear_screen() {
226    unsafe {
227        (*DEFAULT_VIDEO_MATRIX)
228            .iter_mut()
229            .for_each(|i| *i = Keyboard::Space as u8);
230    }
231}
232
233/// Shift to lower case ROM charset
234pub fn set_lower_case() {
235    unsafe {
236        vic2().screen_and_charset_bank.write(23);
237    }
238}
239
240/// Shift to upper case ROM charset
241pub fn set_upper_case() {
242    unsafe {
243        vic2().screen_and_charset_bank.write(21);
244    }
245}