rustzx_core/zx/machine/
mod.rs

1//! Module with machine specifications
2
3// Allow outer modules to use ZXSpecs struct, but not construct
4mod specs;
5
6use lazy_static::lazy_static;
7use specs::ZXSpecsBuilder;
8
9pub(crate) use specs::ZXSpecs;
10
11lazy_static! {
12    /// ZX Spectrum 48K Specs
13    pub(crate) static ref SPECS_48K: ZXSpecs = {
14        ZXSpecsBuilder::new()
15            .freq_cpu(3_500_000)
16            .clocks_first_pixel(14336)
17            .clocks_ula_read_shift(2)
18            .clocks_ula_beam_shift(1)
19            .clocks_row(24, 128, 24, 48)
20            .lines(48, 192, 48, 24)
21            .contention([6, 5, 4, 3, 2, 1, 0, 0], 1)
22            .interrupt_length(32)
23            .rom_pages(1)
24            .build()
25        };
26}
27
28lazy_static! {
29    /// ZX Spectrum 128K Specs
30    pub static ref SPECS_128K: ZXSpecs = {
31        ZXSpecsBuilder::new()
32            .freq_cpu(3_546_900)
33            .clocks_first_pixel(14362)
34            .clocks_ula_read_shift(2)
35            .clocks_ula_beam_shift(1)
36            .clocks_row(24, 128, 24, 52)
37            .lines(48, 192, 48, 23)
38            .contention([6, 5, 4, 3, 2, 1, 0, 0], 1)
39            .interrupt_length(32)
40            .rom_pages(2)
41            .build()
42    };
43}
44
45/// Machine type
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum ZXMachine {
48    Sinclair48K,
49    Sinclair128K,
50}
51
52impl ZXMachine {
53    /// Returns current machine specs as ref to static value
54    pub fn specs(self) -> &'static ZXSpecs {
55        match self {
56            ZXMachine::Sinclair48K => &SPECS_48K,
57            ZXMachine::Sinclair128K => &SPECS_128K,
58        }
59    }
60
61    /// Returns contention during specified time
62    pub fn contention_clocks(self, clocks: usize) -> usize {
63        let specs = self.specs();
64        if (clocks < (specs.clocks_first_pixel - 1))
65            || (clocks >= (specs.clocks_first_pixel - 1) + specs.lines_screen * specs.clocks_line)
66        {
67            return 0;
68        }
69        let clocks_trough_line = (clocks - (specs.clocks_first_pixel - 1)) % specs.clocks_line;
70        if clocks_trough_line >= specs.clocks_screen_row {
71            return 0;
72        }
73        return self.specs().contention_pattern[clocks_trough_line % 8];
74    }
75
76    /// Checks port contention on machine
77    pub fn port_is_contended(self, port: u16) -> bool {
78        match self {
79            ZXMachine::Sinclair48K | ZXMachine::Sinclair128K => {
80                // every even port
81                (port & 0x0001) == 0
82            }
83        }
84    }
85
86    /// Returns contention status of bank
87    pub fn bank_is_contended(self, page: usize) -> bool {
88        match self {
89            ZXMachine::Sinclair48K => page == 0,
90            ZXMachine::Sinclair128K => {
91                let contended_pages = [1, 3, 5, 7];
92                contended_pages.iter().any(|&x| x == page)
93            }
94        }
95    }
96}