1 |
cebix |
1.1 |
/* |
2 |
|
|
* REU.cpp - 17xx REU emulation |
3 |
|
|
* |
4 |
cebix |
1.4 |
* Frodo (C) 1994-1997,2002-2005 Christian Bauer |
5 |
cebix |
1.1 |
* |
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 |
|
|
} |