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