summaryrefslogtreecommitdiff
path: root/src/luma/device/branch.rs
blob: 6bc23204b81d95fdbf60331aab50f9eeff030e34 (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
/*
	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::luma::device::{Branch, Device};

impl Device {
	pub fn branch(&mut self, kind: Branch) {
		match kind {
			Branch::Offset(offset, l) => {
				if l { // Check the l flag.
					// Store the address of the following instruction 
					// in r14 (lr).

					let pc_offset: u32 = match self.thumb() {
						false => 0x4,
						true  => 0x2,
					};

					self.registers[0xE] = self.registers[0xF] - pc_offset;
		
					self.log("link", format!("r14 => r15-{pc_offset}={:#010X}", self.registers[0xE]));
				}

				// Add the offset to r15 (pc).

				let (address, _) = self.registers[0xF].overflowing_add_signed(offset);
	
				// Add extra offset to move to the new fetch 
				// instruction.
				let pc_offset = match self.thumb() {
					false => 0x8,
					true  => 0x4,
				};

				self.registers[0xF] = address + pc_offset;
				
				self.log("branch", format!("r15 => r15{offset:+}+{pc_offset} ({:#010X})", self.registers[0xF]));
			},
			Branch::Register(register) => {
				// Use the address stored in 'register' as the new 
				// value in r15 (pc).

				let value = self.registers[register as usize];

				let t = value & 0b00000000000000000000000000000001 != 0x0;

				self.cpsr = self.cpsr & 0b11111111111111111111111111011111 | (t as u32) << 0x5;
				self.exchange(t);

				let address = value & 0b11111111111111111111111111111110;

				// Add extra offset to move to the new fetch 
				// instruction.
				let pc_offset: u32 = match t {
					false => 0x8,
					true  => 0x4,
				};

				self.registers[0xF] = address + pc_offset;

				self.log("branch", format!("r15 => r{register}{pc_offset:+} ({:#010X})", self.registers[0xF]));
			},
		}
	}
}