summaryrefslogtreecommitdiff
path: root/src/state/write.rs
blob: 10955e2d09ebed92de04a6c35b5db3d348f405ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
	Copyright 2021-2023 Gabriel Jensen.

	This file is part of Luma.

	Luma is free software: you can redistribute it
	and/or modify it under the terms of the GNU
	Affero General Public License as published by
	the Free Software Foundation, either version 3
	of the License, or (at your option) any later
	version.

	Luma is distributed in the hope that it will be
	useful, but WITHOUT ANY WARRANTY; without even
	the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU
	Affero General Public License for more details.

	You should have received a copy of the GNU
	Affero General Public License along with Luma.
	If not, see <https://www.gnu.org/licenses/>.
*/

use crate::{Error, log_assignment, MEMORY_LENGTH};
use crate::cpu_mode::CpuMode;
use crate::state::{address_unused, State};

macro_rules! read_only {
	($address: expr) => {{
		match $address {
			0x00000000..=0x00003FFF => true,
			0x04000130..=0x04000131 => true, // KEYINPUT
			0x08000000..=0x0DFFFFFF => true,

			_ => false,
		}
	}};
}

impl State {
	#[inline(always)]
	pub fn write_register(&mut self, register: u8, value: u32) {
		log_assignment!(format!("r{register}"), format!("{value:#010X}"));

		let index = (register & 0b00001111) as usize;

		unsafe { **self.registers.get_unchecked_mut(index) = value };
	}

	pub fn write_word(&mut self, address: u32, value: u32) {
		log_assignment!(format!("{address:#010X}"), format!("{value:#010X}"));

		if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap();      return; }
		if address % 0x4 != 0x0          { Error::BadAlignment(address, 0x4).trap(); return; }

		if read_only!(address) || address_unused!(address) { return };

		unsafe {
			let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u32;
			*pointer = value;
		}
	}

	pub fn write_halfword(&mut self, address: u32, value: u16) {
		log_assignment!(format!("{address:#010X}"), format!("{value:#06X}"));

		if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap();      return; }
		if address % 0x2 != 0x0          { Error::BadAlignment(address, 0x2).trap(); return; }

		if read_only!(address) || address_unused!(address) { return };

		unsafe {
			let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u16;
			*pointer = value;
		}
	}

	pub fn write_byte(&mut self, address: u32, value: u8) {
		log_assignment!(format!("{address:#010X}"), format!("{value:#04X}"));

		if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return; }

		if read_only!(address) || address_unused!(address) { return };

		let memory = self.memory.as_mut_ptr() as *mut u8;

		match address {
			// Extend to halfwords:
			| 0x05000000..=0x050003FF
			| 0x06000000..=0x06017FFF
			| 0x07000000..=0x070003FF => {
				// Align to halfwords.
				let address = address & 0b11111111111111111111111111111110;

				// Repeat value.
				let value = value as u16 | (value as u16) << 0x8;

				unsafe {
					let pointer = memory.add(address as usize) as *mut u16;
					*pointer = value;
				}
			},

			// Bytes are allowed:
			_ => unsafe {
				let pointer = memory.add(address as usize);
				*pointer = value;
			},
		};
	}

	#[inline(always)]
	pub fn write_cpsr(&mut self, value: u32) {
		log_assignment!("cpsr", format!("{value:#034b}"));

		self.cpsr = value;
	}

	#[inline(always)]
	pub fn write_spsr(&mut self, mode: CpuMode, value: u32) {
		log_assignment!("spsr", format!("{value:#034b}"));

		unsafe { *self.spsr.get_unchecked_mut(Self::spsr_index(mode)) = value };
	}
}