Skip to main content

megadrive_sys/
lib.rs

1#![no_std]
2
3use core::ptr::{read_volatile, write_volatile};
4
5pub mod z80;
6pub mod vdp;
7pub mod ports;
8pub mod fm;
9pub mod psg;
10
11extern "C" {
12    static _data_src: *const u32;
13    static _data_start: *mut u32;
14    static _data_end: *mut u32;
15    static _bss_start: *mut u32;
16    static _bss_end: *mut u32;
17}
18
19#[no_mangle]
20fn _init_runtime() {
21    // Implement SEGA copy protection.
22    init_tmss();
23
24    // Shutdown the Z80 and set it up for RAM access.
25    // This is required to access peripherals on the Z80 bus.
26    // More consideration will be needed here when the Z80 is considered for
27    // use proper.
28    z80::halt(true);
29    z80::request_bus(true);
30    z80::halt(false);
31
32    // Copy .data & zero .bss.
33    unsafe {
34        let data_count = _data_end.offset_from(_data_start) as usize;
35        let data_src = core::slice::from_raw_parts(_data_src, data_count);
36        let data_dest = core::slice::from_raw_parts_mut(_data_start, data_count);
37        for (dst, src) in data_dest.into_iter().zip(data_src.into_iter()) {
38            *dst = *src;
39        }
40
41        let bss_count = _bss_end.offset_from(_bss_start) as usize;
42        let bss = core::slice::from_raw_parts_mut(_bss_start, bss_count);
43        for b in bss {
44            *b = 0;
45        }
46    }
47}
48
49/// An enum for the various region variants of the Mega Drive.
50#[derive(Clone, Copy, Debug)]
51pub enum Region {
52    Invalid,
53    Japan,
54    Europe,
55    USA,
56}
57
58/// A struct containing version information extracted from the console.
59///
60/// This can be used to determine region, resolution, frame rate and hardware
61/// revision.
62#[derive(Clone, Copy, Debug)]
63pub struct Version(u8);
64
65impl Version {
66    /// Retrieve the hardware revision.
67    pub fn hardware_revision(self) -> u8 { self.0 & 0xf }
68
69    /// Check if a FDD is attached.
70    pub fn has_fdd(self) -> bool { (self.0 & 0x20) != 0 }
71
72    /// Returns true if this is a PAL system.
73    pub fn is_pal(self) -> bool { (self.0 & 0x40) != 0 }
74
75    /// Returns true if this is a NTSC system.
76    pub fn is_ntsc(self) -> bool { !self.is_pal() }
77
78    /// Returns true if this is an 'overseas' model, i.e. not for use in Japan.
79    pub fn is_overseas(self) -> bool { (self.0 & 0x80) != 0 }
80
81    /// Return the region variation of this console.
82    pub fn region(self) -> Region {
83        match (self.is_pal(), self.is_overseas()) {
84            (false, false) => Region::Japan,
85            (false, true) => Region::USA,
86            (true, false) => Region::Europe,
87            (true, true) => Region::Invalid,
88        }
89    }
90}
91
92const VERSION_REG: *mut u8 = (0xa10001) as _;
93
94/// Read the console version information.
95pub fn version() -> Version {
96    let v = unsafe { read_volatile(VERSION_REG) };
97    Version(v)
98}
99
100// TMSS - copy protection for the Mega Drive.
101const TMSS_CODE: &'static [u8; 4] = b"SEGA";
102const TMSS_REG: *mut u32 = 0xa14000 as _;
103
104fn init_tmss() {
105    if version().hardware_revision() > 0 {
106        unsafe {
107            let tmss_code: *const u32 = core::mem::transmute(&TMSS_CODE[0]);
108            write_volatile(TMSS_REG, *tmss_code);
109        }
110    }
111}