-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspc700.cpp
More file actions
executable file
·145 lines (127 loc) · 3.82 KB
/
Copy pathspc700.cpp
File metadata and controls
executable file
·145 lines (127 loc) · 3.82 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "Arduino.h"
#include "spc700.h"
/**
* Changes the direction of the data bus for writing / reading.
*
* @param {uint8_t} direction The direction of the databuse: INPUT / OUTPUT
*/
void change_data_direction(uint8_t direction) {
DDRD = (DDRD & 0x03) | (direction == OUTPUT ? 0xFC : 0x00);
DDRB = (DDRB & 0xFC) | (direction == OUTPUT ? 0x03 : 0x00);
}
/**
* Resets the SPC
*/
void spc_reset() {
digitalWrite(PIN_RESET, LOW);
NOP;
digitalWrite(PIN_RESET, HIGH);
uint8_t port0 = 0;
uint8_t port1 = 0;
// Wait for ports zero and one to read 0xAA and 0xBB respectively. This
// is the sign that IPL ROM has finished and is waiting for commands.
while (port0 != 0xAA && port1 != 0xBB) {
port0 = spc_read(PORT_0);
port1 = spc_read(PORT_1);
}
}
/**
* Reads a byte from one of the SPC ports
*
* @param {uint8_t} addr The port address to read from
* @return {uint8_t} The value read from the data bus
*/
uint8_t spc_read(uint8_t addr) {
// Set the address
PORTB = (PORTB & B11110011) | ((addr & 0x03) << 2);
digitalWrite(PIN_READ, LOW);
NOP;
// Read the data straight off the registers
change_data_direction(INPUT);
uint8_t result = ((PINB & 0x03) << 6) | ((PIND & 0xFC) >> 2);
change_data_direction(OUTPUT);
digitalWrite(PIN_READ, HIGH);
return result;
}
/**
* Writes a value out to the database at a particular port
*
* @param {uint8_t} addr The port address to write to
* @param {uint8_t} value The value to write
*/
void spc_write(uint8_t addr, uint8_t value) {
change_data_direction(OUTPUT);
// Set the address and the upper two bits of data
PORTB = (PORTB & 0xF0) | ((addr & 0x03) << 2) | ((value & 0xC0) >> 6);
// Lower six bits
PORTD = (PORTD & 0x03) | ((value & 0x3F) << 2);
// Cycle the write line
digitalWrite(PIN_WRITE, LOW);
NOP;
digitalWrite(PIN_WRITE, HIGH);
}
/**
* Writes a chunk of data to the SPC. Requires that transfer mode
* already be active.
*
* @param {uint8_t *} data Pointer to the data to write
* @param {uint16_t} len The length of the data to write
*/
void spc_write_chunk(uint8_t *data, uint16_t len) {
uint8_t check = 0;
for (uint16_t i = 0; i < len; i++) {
spc_write(PORT_1, data[i]);
spc_write(PORT_0, check);
spc_zero_wait(check++);
}
}
/**
* Waits for PORT0 to present a particular value
*/
void spc_zero_wait(uint8_t value) {
while (spc_read(PORT_0) != value);
}
/**
* Sets the SPC up for a fresh new transfer
*
* @param {uint16_t} addr The address to begin transferring to
*/
void spc_begin_transfer(uint16_t addr) {
spc_write(PORT_2, addr & 0xFF);
spc_write(PORT_3, addr >> 8);
spc_write(PORT_1, 0x01);
spc_write(PORT_0, 0xCC);
spc_zero_wait(0xCC);
}
/**
* Tells the SPC that a new chunk is beginning or the transfer is ended.
* This is defined solely by the value passed to port 1.
*
* @param {uint16_t} The addres to transfer to or execute from
* @param {bool} end Is this the end of the transfer
*/
void spc_set_transfer_phase(uint16_t addr, bool transfer_end) {
spc_write(PORT_2, addr & 0xFF);
spc_write(PORT_3, addr >> 8);
spc_write(PORT_1, transfer_end ? 0x00 : 0x01);
uint8_t port_zero_check = spc_read(PORT_0) + 2;
port_zero_check = port_zero_check == 0 ? 2 : port_zero_check;
spc_write(PORT_0, port_zero_check);
spc_zero_wait(port_zero_check);
}
/**
* Gets the SPC ready to write a new chunk of data
*
* @param {uint16_t} addr The address to begin transferring to
*/
void spc_begin_chunk(uint16_t addr) {
spc_set_transfer_phase(addr, false);
}
/**
* Ends a data transfer with the SPC and begins execution
*
* @param {uint16_t} addr The address to begin execution at
*/
void spc_end_transfer(uint16_t addr) {
spc_set_transfer_phase(addr, true);
}