1 /*
2 * REU.cpp - 17xx REU emulation
3 *
4 * Frodo (C) 1994-1997,2002 Christian Bauer
5 *
6
7 *
8 * Incompatibilities:
9 * ------------------
10 *
11 * - REU interrupts are not emulated
12 * - Transfer time is not accounted for, all transfers
13 * are done in 0 cycles
14 */
15
16 #include "sysdeps.h"
17
18 #include "REU.h"
19 #include "CPUC64.h"
20 #include "Prefs.h"
21
22
23 /*
24 * Constructor
25 */
26
REU(MOS6510 * CPU)27 REU::REU(MOS6510 *CPU) : the_cpu(CPU)
28 {
29 int i;
30
31 // Init registers
32 regs[0] = 0x40;
33 for (i=1; i<11; i++)
34 regs[i] = 0;
35 for (i=11; i<16; i++)
36 regs[i] = 0xff;
37
38 ex_ram = NULL;
39 ram_size = ram_mask = 0;
40
41 // Allocate RAM
42 open_close_reu(REU_NONE, ThePrefs.REUSize);
43 }
44
45
46 /*
47 * Destructor
48 */
49
~REU()50 REU::~REU()
51 {
52 // Free RAM
53 open_close_reu(ThePrefs.REUSize, REU_NONE);
54 }
55
56
57 /*
58 * Prefs may have changed, reallocate expansion RAM
59 */
60
NewPrefs(Prefs * prefs)61 void REU::NewPrefs(Prefs *prefs)
62 {
63 open_close_reu(ThePrefs.REUSize, prefs->REUSize);
64 }
65
66
67 /*
68 * Allocate/free expansion RAM
69 */
70
open_close_reu(int old_size,int new_size)71 void REU::open_close_reu(int old_size, int new_size)
72 {
73 if (old_size == new_size)
74 return;
75
76 // Free old RAM
77 if (old_size != REU_NONE) {
78 delete[] ex_ram;
79 ex_ram = NULL;
80 }
81
82 // Allocate new RAM
83 if (new_size != REU_NONE) {
84 switch (new_size) {
85 case REU_128K:
86 ram_size = 0x20000;
87 break;
88 case REU_256K:
89 ram_size = 0x40000;
90 break;
91 case REU_512K:
92 ram_size = 0x80000;
93 break;
94 }
95 ram_mask = ram_size - 1;
96 ex_ram = new uint8[ram_size];
97
98 // Set size bit in status register
99 if (ram_size > 0x20000)
100 regs[0] |= 0x10;
101 else
102 regs[0] &= 0xef;
103 }
104 }
105
106
107 /*
108 * Reset the REU
109 */
110
Reset(void)111 void REU::Reset(void)
112 {
113 int i;
114
115 for (i=1; i<11; i++)
116 regs[i] = 0;
117 for (i=11; i<16; i++)
118 regs[i] = 0xff;
119
120 if (ram_size > 0x20000)
121 regs[0] = 0x50;
122 else
123 regs[0] = 0x40;
124 }
125
126
127 /*
128 * Read from REU register
129 */
130
ReadRegister(uint16 adr)131 uint8 REU::ReadRegister(uint16 adr)
132 {
133 if (ex_ram == NULL)
134 return rand();
135
136 switch (adr) {
137 case 0:{
138 uint8 ret = regs[0];
139 regs[0] &= 0x1f;
140 return ret;
141 }
142 case 6:
143 return regs[6] | 0xf8;
144 case 9:
145 return regs[9] | 0x1f;
146 case 10:
147 return regs[10] | 0x3f;
148 default:
149 return regs[adr];
150 }
151 }
152
153
154 /*
155 * Write to REU register
156 */
157
WriteRegister(uint16 adr,uint8 byte)158 void REU::WriteRegister(uint16 adr, uint8 byte)
159 {
160 if (ex_ram == NULL)
161 return;
162
163 switch (adr) {
164 case 0: // Status register is read-only
165 case 11: // Unconnected registers
166 case 12:
167 case 13:
168 case 14:
169 case 15:
170 break;
171 case 1: // Command register
172 regs[1] = byte;
173 if ((byte & 0x90) == 0x90)
174 execute_dma();
175 break;
176 default:
177 regs[adr] = byte;
178 break;
179 }
180 }
181
182
183 /*
184 * CPU triggered REU by writing to $ff00
185 */
186
FF00Trigger(void)187 void REU::FF00Trigger(void)
188 {
189 if (ex_ram == NULL)
190 return;
191
192 if ((regs[1] & 0x90) == 0x80)
193 execute_dma();
194 }
195
196
197 /*
198 * Execute REU DMA transfer
199 */
200
execute_dma(void)201 void REU::execute_dma(void)
202 {
203 // Get C64 and REU transfer base addresses
204 uint16 c64_adr = regs[2] | (regs[3] << 8);
205 uint32 reu_adr = regs[4] | (regs[5] << 8) | (regs[6] << 16);
206
207 // Calculate transfer length
208 int length = regs[7] | (regs[8] << 8);
209 if (!length)
210 length = 0x10000;
211
212 // Calculate address increments
213 uint32 c64_inc = (regs[10] & 0x80) ? 0 : 1;
214 uint32 reu_inc = (regs[10] & 0x40) ? 0 : 1;
215
216 // Do transfer
217 switch (regs[1] & 3) {
218
219 case 0: // C64 -> REU
220 for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc)
221 ex_ram[reu_adr & ram_mask] = the_cpu->REUReadByte(c64_adr);
222 break;
223
224 case 1: // C64 <- REU
225 for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc)
226 the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]);
227 break;
228
229 case 2: // C64 <-> REU
230 for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) {
231 uint8 tmp = the_cpu->REUReadByte(c64_adr);
232 the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]);
233 ex_ram[reu_adr & ram_mask] = tmp;
234 }
235 break;
236
237 case 3: // Compare
238 for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc)
239 if (ex_ram[reu_adr & ram_mask] != the_cpu->REUReadByte(c64_adr)) {
240 regs[0] |= 0x20;
241 break;
242 }
243 break;
244 }
245
246 // Update address and length registers if autoload is off
247 if (!(regs[1] & 0x20)) {
248 regs[2] = c64_adr;
249 regs[3] = c64_adr >> 8;
250 regs[4] = reu_adr;
251 regs[5] = reu_adr >> 8;
252 regs[6] = reu_adr >> 16;
253 regs[7] = length + 1;
254 regs[8] = (length + 1) >> 8;
255 }
256
257 // Set complete bit in status register
258 regs[0] |= 0x40;
259
260 // Clear execute bit in command register
261 regs[1] &= 0x7f;
262 }
263