1 |
/* |
2 |
* PearPC |
3 |
* cuda.cc |
4 |
* |
5 |
* Copyright (C) 2003-2004 Sebastian Biallas (sb@biallas.net) |
6 |
* Copyright (C) 2004 Stefan Weyergraf |
7 |
* |
8 |
* This program is free software; you can redistribute it and/or modify |
9 |
* it under the terms of the GNU General Public License version 2 as |
10 |
* published by the Free Software Foundation. |
11 |
* |
12 |
* This program is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with this program; if not, write to the Free Software |
19 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 |
* |
21 |
* From Linux 2.6.4: |
22 |
* The VIA (versatile interface adapter) interfaces to the CUDA, |
23 |
* a 6805 microprocessor core which controls the ADB (Apple Desktop |
24 |
* Bus) which connects to the keyboard and mouse. The CUDA also |
25 |
* controls system power and the RTC (real time clock) chip. |
26 |
* |
27 |
* See also: |
28 |
* http://www.howell1964.freeserve.co.uk/parts/6522_VIA.htm |
29 |
* |
30 |
* References: |
31 |
* [1] http://bbc.nvg.org/doc/datasheets/R6522_r9.zip |
32 |
*/ |
33 |
|
34 |
#include <cstdlib> |
35 |
#include <cstring> |
36 |
#include <cstdarg> |
37 |
#include <ctime> |
38 |
|
39 |
#include "cpu/cpu.h" |
40 |
#include "tools/snprintf.h" |
41 |
#include "debug/tracers.h" |
42 |
#include "io/pic/pic.h" |
43 |
#include "system/keyboard.h" |
44 |
#include "system/mouse.h" |
45 |
#include "system/sys.h" |
46 |
#include "system/sysclk.h" |
47 |
#include "system/systhread.h" |
48 |
|
49 |
#include "cuda.h" |
50 |
|
51 |
//#define IO_CUDA_TRACE2(str...) ht_printf(str) |
52 |
#define IO_CUDA_TRACE2(str...) |
53 |
|
54 |
//#define IO_CUDA_TRACE3(str...) ht_printf(str) |
55 |
#define IO_CUDA_TRACE3(str...) |
56 |
|
57 |
#define RS (0x200) |
58 |
#define B 0 /* B-side data */ |
59 |
#define A RS /* A-side data */ |
60 |
#define DIRB (2*RS) /* B-side direction (1=output) */ |
61 |
#define DIRA (3*RS) /* A-side direction (1=output) */ |
62 |
#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ |
63 |
#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ |
64 |
#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ |
65 |
#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ |
66 |
#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ |
67 |
#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ |
68 |
#define SR (10*RS) /* Shift register */ |
69 |
#define ACR (11*RS) /* Auxiliary control register */ |
70 |
#define PCR (12*RS) /* Peripheral control register */ |
71 |
#define IFR (13*RS) /* Interrupt flag register */ |
72 |
#define IER (14*RS) /* Interrupt enable register */ |
73 |
#define ANH (15*RS) /* A-side data, no handshake */ |
74 |
|
75 |
/* Bits in B data register: all active low */ |
76 |
#define TREQ 0x08 /* Transfer request (input) */ |
77 |
#define TACK 0x10 /* Transfer acknowledge (output) */ |
78 |
#define TIP 0x20 /* Transfer in progress (output) */ |
79 |
|
80 |
/* Bits in ACR */ |
81 |
#define SR_CTRL 0x1c /* Shift register control bits */ |
82 |
#define SR_EXT 0x0c /* Shift on external clock */ |
83 |
#define SR_OUT 0x10 /* Shift out if 1 */ |
84 |
|
85 |
/* Bits in IFR and IER */ |
86 |
#define IER_SET 0x80 /* set bits in IER */ |
87 |
#define IER_CLR 0 /* clear bits in IER */ |
88 |
#define SR_INT 0x04 /* Shift register full/empty */ |
89 |
|
90 |
/* Bits in ACR */ |
91 |
#define T1MODE 0xc0 /* Timer 1 mode */ |
92 |
#define T1MODE_CONT 0x40 /* continuous interrupts */ |
93 |
|
94 |
/* Bits in IFR and IER */ |
95 |
#define T1_INT 0x40 /* Timer 1 interrupt */ |
96 |
|
97 |
/* commands (1st byte) */ |
98 |
#define ADB_PACKET 0 |
99 |
#define CUDA_PACKET 1 |
100 |
#define ERROR_PACKET 2 |
101 |
#define TIMER_PACKET 3 |
102 |
#define POWER_PACKET 4 |
103 |
#define MACIIC_PACKET 5 |
104 |
#define PMU_PACKET 6 |
105 |
|
106 |
/* CUDA commands (2nd byte) */ |
107 |
#define CUDA_WARM_START 0x0 |
108 |
#define CUDA_AUTOPOLL 0x1 |
109 |
#define CUDA_GET_6805_ADDR 0x2 |
110 |
#define CUDA_GET_TIME 0x3 |
111 |
#define CUDA_GET_PRAM 0x7 |
112 |
#define CUDA_SET_6805_ADDR 0x8 |
113 |
#define CUDA_SET_TIME 0x9 |
114 |
#define CUDA_POWERDOWN 0xa |
115 |
#define CUDA_POWERUP_TIME 0xb |
116 |
#define CUDA_SET_PRAM 0xc |
117 |
#define CUDA_MS_RESET 0xd |
118 |
#define CUDA_SEND_DFAC 0xe |
119 |
#define CUDA_BATTERY_SWAP_SENSE 0x10 |
120 |
#define CUDA_RESET_SYSTEM 0x11 |
121 |
#define CUDA_SET_IPL 0x12 |
122 |
#define CUDA_FILE_SERVER_FLAG 0x13 |
123 |
#define CUDA_SET_AUTO_RATE 0x14 |
124 |
#define CUDA_GET_AUTO_RATE 0x16 |
125 |
#define CUDA_SET_DEVICE_LIST 0x19 |
126 |
#define CUDA_GET_DEVICE_LIST 0x1a |
127 |
#define CUDA_SET_ONE_SECOND_MODE 0x1b |
128 |
#define CUDA_SET_POWER_MESSAGES 0x21 |
129 |
#define CUDA_GET_SET_IIC 0x22 |
130 |
#define CUDA_WAKEUP 0x23 |
131 |
#define CUDA_TIMER_TICKLE 0x24 |
132 |
#define CUDA_COMBINED_FORMAT_IIC 0x25 |
133 |
|
134 |
|
135 |
/* ADB commands */ |
136 |
#define ADB_BUSRESET 0x00 |
137 |
#define ADB_FLUSH 0x01 |
138 |
#define ADB_WRITEREG 0x08 |
139 |
#define ADB_READREG 0x0c |
140 |
|
141 |
/* ADB device commands */ |
142 |
#define ADB_CMD_SELF_TEST 0xff |
143 |
#define ADB_CMD_CHANGE_ID 0xfe |
144 |
#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd |
145 |
#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00 |
146 |
|
147 |
/* ADB default device IDs (upper 4 bits of ADB command byte) */ |
148 |
#define ADB_DONGLE 1 |
149 |
#define ADB_KEYBOARD 2 |
150 |
#define ADB_MOUSE 3 |
151 |
#define ADB_TABLET 4 |
152 |
#define ADB_MODEM 5 |
153 |
#define ADB_MISC 7 |
154 |
|
155 |
#define ADB_RET_OK 0 |
156 |
#define ADB_RET_INUSE 1 |
157 |
#define ADB_RET_NOTPRESENT 2 |
158 |
#define ADB_RET_TIMEOUT 3 |
159 |
#define ADB_RET_UNEXPECTED_RESULT 4 |
160 |
#define ADB_RET_REQUEST_ERROR 5 |
161 |
#define ADB_RET_BUS_ERROR 6 |
162 |
|
163 |
#define ADB_PACKET 0 |
164 |
#define CUDA_PACKET 1 |
165 |
#define ERROR_PACKET 2 |
166 |
#define TIMER_PACKET 3 |
167 |
#define POWER_PACKET 4 |
168 |
#define MACIIC_PACKET 5 |
169 |
#define PMU_PACKET 6 |
170 |
|
171 |
// VIA timer runs at a frequency of 1/1.27655us |
172 |
// or 783361.40378364 ticks/second |
173 |
#define VIA_TIMER_FREQ_DIV_HZ_TIMES_1000 (783361404ULL) |
174 |
|
175 |
enum cuda_state { |
176 |
cuda_idle, |
177 |
cuda_writing, |
178 |
cuda_reading, |
179 |
}; |
180 |
|
181 |
struct cuda_control { |
182 |
byte rA; |
183 |
byte rB; |
184 |
byte rDIRB; |
185 |
byte rDIRA; |
186 |
byte rT1CL; |
187 |
byte rT1CH; |
188 |
byte rT1LL; |
189 |
byte rT1LH; |
190 |
byte rT2CL; |
191 |
byte rT2CH; |
192 |
byte rSR; |
193 |
byte rACR; |
194 |
byte rPCR; |
195 |
byte rIFR; |
196 |
byte rIER; |
197 |
byte rANH; |
198 |
|
199 |
// private |
200 |
uint64 T1_end; // in cpu ticks |
201 |
bool autopoll; |
202 |
cuda_state state; |
203 |
int left; |
204 |
int pos; |
205 |
uint8 data[100]; |
206 |
|
207 |
int keybaddr; |
208 |
int keybhandler; |
209 |
int mouseaddr; |
210 |
int mousehandler; |
211 |
|
212 |
sys_semaphore idle_sem; |
213 |
}; |
214 |
|
215 |
static cuda_control gCUDA; |
216 |
static sys_mutex gCUDAMutex; |
217 |
|
218 |
static void cuda_send_packet(uint8 type, int nb, ...) |
219 |
{ |
220 |
gCUDA.data[0] = type; |
221 |
va_list va; |
222 |
va_start(va, nb); |
223 |
for (int i=0; i<nb; i++) { |
224 |
uint8 b = va_arg(va, int); |
225 |
gCUDA.data[i+1] = b; |
226 |
} |
227 |
IO_CUDA_TRACE3("send: "); |
228 |
for (int i=0; i<nb+1; i++) { |
229 |
IO_CUDA_TRACE3("%02x ", gCUDA.data[i]); |
230 |
} |
231 |
IO_CUDA_TRACE3("\n"); |
232 |
va_end(va); |
233 |
gCUDA.pos = 0; |
234 |
gCUDA.left = nb+1; |
235 |
gCUDA.rIFR |= SR_INT; |
236 |
gCUDA.rB &= ~TREQ; |
237 |
gCUDA.rB |= TIP; |
238 |
pic_raise_interrupt(IO_PIC_IRQ_CUDA); |
239 |
} |
240 |
|
241 |
static void cuda_receive_adb_packet() |
242 |
{ |
243 |
IO_CUDA_TRACE3("===========================================\n"); |
244 |
IO_CUDA_TRACE3("ADB_PACKET ");// %02x %02x %02x %02x %02x\n", gCUDA.data[1], gCUDA.data[2], gCUDA.data[3], gCUDA.data[4], gCUDA.data[5]); |
245 |
for (int i=1; i<gCUDA.pos; i++) { |
246 |
IO_CUDA_TRACE3("%02x ", gCUDA.data[i]); |
247 |
} |
248 |
IO_CUDA_TRACE3("\n"); |
249 |
// gSinglestep = true; |
250 |
IO_CUDA_TRACE2("ADB_PACKET "); |
251 |
if (gCUDA.data[1] == ADB_BUSRESET) { |
252 |
IO_CUDA_TRACE2("ADB_BUSRESET %02x\n", gCUDA.data[2]); |
253 |
cuda_send_packet(ADB_PACKET, 2, 0, 0); |
254 |
return; |
255 |
} |
256 |
int devaddr = gCUDA.data[1] >> 4; |
257 |
int cmd = gCUDA.data[1] & 0xf; |
258 |
if (cmd == ADB_FLUSH) { |
259 |
// FIXME: ok? |
260 |
cuda_send_packet(ADB_PACKET, 2, 0, 0); |
261 |
return; |
262 |
} |
263 |
int reg = cmd & 3; |
264 |
cmd &= 0xc; |
265 |
IO_CUDA_TRACE3("devaddr %x reg %x cmd %s\n", devaddr, reg, (cmd==ADB_WRITEREG)?"write":"read"); |
266 |
switch (cmd) { |
267 |
case ADB_WRITEREG: |
268 |
switch (reg) { |
269 |
case 2: |
270 |
if (devaddr == gCUDA.keybaddr) { |
271 |
// LED stat |
272 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
273 |
} else if (devaddr == gCUDA.mouseaddr) { |
274 |
// gSinglestep = true; |
275 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
276 |
} else { |
277 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_NOTPRESENT); |
278 |
} |
279 |
break; |
280 |
case 3: |
281 |
if (devaddr == gCUDA.keybaddr) { |
282 |
switch (gCUDA.data[3]) { |
283 |
case ADB_CMD_SELF_TEST: |
284 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
285 |
break; |
286 |
case ADB_CMD_CHANGE_ID: |
287 |
case ADB_CMD_CHANGE_ID_AND_ACT: |
288 |
case ADB_CMD_CHANGE_ID_AND_ENABLE: |
289 |
gCUDA.keybaddr = gCUDA.data[2] & 0xf; |
290 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
291 |
break; |
292 |
default: |
293 |
gCUDA.keybaddr = gCUDA.data[2] & 0xf; |
294 |
gCUDA.keybhandler = gCUDA.data[3]; |
295 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
296 |
break; |
297 |
} |
298 |
} else if (devaddr == gCUDA.mouseaddr) { |
299 |
switch (gCUDA.data[3]) { |
300 |
case ADB_CMD_SELF_TEST: |
301 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
302 |
break; |
303 |
case ADB_CMD_CHANGE_ID: |
304 |
case ADB_CMD_CHANGE_ID_AND_ACT: |
305 |
case ADB_CMD_CHANGE_ID_AND_ENABLE: |
306 |
gCUDA.mouseaddr = gCUDA.data[2] & 0xf; |
307 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
308 |
break; |
309 |
default: |
310 |
gCUDA.mouseaddr = gCUDA.data[2] & 0xf; |
311 |
gCUDA.mousehandler = gCUDA.data[3]; |
312 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
313 |
break; |
314 |
} |
315 |
} else { |
316 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_NOTPRESENT); |
317 |
} |
318 |
break; |
319 |
default: |
320 |
IO_CUDA_ERR("unknown reg %02x for device %02x\n", reg, devaddr); |
321 |
} |
322 |
break; |
323 |
case ADB_READREG: { |
324 |
switch (reg) { |
325 |
case 1: |
326 |
if (devaddr == gCUDA.keybaddr) { |
327 |
IO_CUDA_WARN("keyb reg1\n"); |
328 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
329 |
} else if (devaddr == gCUDA.mouseaddr) { |
330 |
// gSinglestep = true; |
331 |
IO_CUDA_WARN("read reg 1 of mouse unsupported.\n"); |
332 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_OK); |
333 |
} else { |
334 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_NOTPRESENT); |
335 |
} |
336 |
break; |
337 |
case 2: |
338 |
if (devaddr == gCUDA.keybaddr) { |
339 |
// LED stat |
340 |
// 111b == all off |
341 |
int ledstat = gKeyboard->getKeybLEDs(); |
342 |
int keyb = 0xff; |
343 |
if (!(ledstat & KEYB_LED_NUM)) keyb &= ~0x80; |
344 |
if (!(ledstat & KEYB_LED_SCROLL)) keyb &= ~0x40; |
345 |
if (!(ledstat & KEYB_LED_CAPS)) keyb &= ~0x20; |
346 |
cuda_send_packet(ADB_PACKET, 3, ADB_RET_OK, 0xff, keyb); |
347 |
} else if (devaddr == gCUDA.mouseaddr) { |
348 |
// gSinglestep = true; |
349 |
IO_CUDA_WARN("read reg 2 of mouse unsupported.\n"); |
350 |
} else { |
351 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_NOTPRESENT); |
352 |
} |
353 |
break; |
354 |
case 3: |
355 |
if (devaddr == gCUDA.keybaddr) { |
356 |
// cuda_send_packet(ADB_PACKET, 3, ADB_RET_OK, gCUDA.keybaddr, gCUDA.keybhandler); |
357 |
cuda_send_packet(ADB_PACKET, 3, ADB_RET_OK, gCUDA.keybhandler, gCUDA.keybaddr); |
358 |
} else if (devaddr == gCUDA.mouseaddr) { |
359 |
// cuda_send_packet(ADB_PACKET, 3, ADB_RET_OK, gCUDA.mouseaddr, gCUDA.mousehandler); |
360 |
cuda_send_packet(ADB_PACKET, 3, ADB_RET_OK, gCUDA.mousehandler, gCUDA.mouseaddr); |
361 |
} else { |
362 |
cuda_send_packet(ADB_PACKET, 1, ADB_RET_NOTPRESENT); |
363 |
} |
364 |
break; |
365 |
default: |
366 |
IO_CUDA_ERR("unknown reg %02x for device %02x\n", reg, devaddr); |
367 |
} |
368 |
break; |
369 |
} |
370 |
default: |
371 |
IO_CUDA_ERR("unknown adb command\n"); |
372 |
} |
373 |
/* |
374 |
default: |
375 |
switch (gCUDA.data[1] & 0xf0) { |
376 |
case (ADB_KEYBOARD << 4): |
377 |
switch (gCUDA.data[1] & 0xf) { |
378 |
case 0xf: |
379 |
IO_CUDA_TRACE2("KEYBOARD: GET DEVICE INFO %02x\n", gCUDA.data[1]); |
380 |
cuda_send_packet(ADB_PACKET, 4, 0, 0, 0, 1); |
381 |
return; |
382 |
} |
383 |
IO_CUDA_TRACE2("KEYBOARD: unknown %02x\n", gCUDA.data[1]); |
384 |
cuda_send_packet(ADB_PACKET, 1, 0x2); |
385 |
return; |
386 |
} |
387 |
IO_CUDA_TRACE2("unknown adb (%02x)!\n", gCUDA.data[1]); |
388 |
// IO_CUDA_ERR("!\n"); |
389 |
cuda_send_packet(ADB_PACKET, 1, 0x2); |
390 |
} |
391 |
*/ |
392 |
} |
393 |
|
394 |
static void cuda_receive_cuda_packet() |
395 |
{ |
396 |
IO_CUDA_TRACE2("CUDA_PACKET "); |
397 |
switch (gCUDA.data[1]) { |
398 |
case CUDA_AUTOPOLL: { |
399 |
IO_CUDA_TRACE2("CUDA_AUTOPOLL=%02x\n", gCUDA.data[2]); |
400 |
if (gCUDA.data[2]) { |
401 |
gCUDA.autopoll = true; |
402 |
} else { |
403 |
gCUDA.autopoll = false; |
404 |
} |
405 |
cuda_send_packet(CUDA_PACKET, 1, gCUDA.data[2]); |
406 |
break; |
407 |
} |
408 |
case CUDA_GET_TIME: { |
409 |
IO_CUDA_TRACE2("CUDA_GET_TIME %02x\n", gCUDA.data[2]); |
410 |
time_t tt; |
411 |
time(&tt); |
412 |
uint32 t = (uint32)tt+ 2082844800; |
413 |
cuda_send_packet(CUDA_PACKET, 6, 0, 0, t>>24, t>>16, t>>8, t); |
414 |
break; |
415 |
} |
416 |
case CUDA_SET_TIME: { |
417 |
IO_CUDA_TRACE2("CUDA_SET_TIME %02x\n", gCUDA.data[2]); |
418 |
cuda_send_packet(CUDA_PACKET, 1, 0); |
419 |
break; |
420 |
} |
421 |
case CUDA_RESET_SYSTEM: { |
422 |
IO_CUDA_WARN("reset!\n"); |
423 |
ppc_cpu_stop(); |
424 |
break; |
425 |
} |
426 |
case CUDA_FILE_SERVER_FLAG: { |
427 |
IO_CUDA_TRACE2("FILE_SERVER_FLAG %02x\n", gCUDA.data[2]); |
428 |
cuda_send_packet(CUDA_PACKET, 1, 0); |
429 |
break; |
430 |
} |
431 |
case CUDA_SET_DEVICE_LIST: { |
432 |
IO_CUDA_TRACE2("SET_DEVICE_LIST %02x %02x %02x\n", gCUDA.data[2], gCUDA.data[3], gCUDA.data[4]); |
433 |
cuda_send_packet(CUDA_PACKET, 1, 0); |
434 |
break; |
435 |
} |
436 |
case CUDA_SET_AUTO_RATE: { |
437 |
IO_CUDA_TRACE2("SET_AUTO_RATE %02x\n", gCUDA.data[2]); |
438 |
cuda_send_packet(CUDA_PACKET, 1, 0); |
439 |
break; |
440 |
} |
441 |
case CUDA_SET_POWER_MESSAGES: { |
442 |
IO_CUDA_TRACE2("CUDA_SET_POWER_MESSAGES %02x\n", gCUDA.data[2]); |
443 |
cuda_send_packet(CUDA_PACKET, 1, 0); |
444 |
break; |
445 |
} |
446 |
case CUDA_POWERDOWN: { |
447 |
IO_CUDA_WARN("power down!\n"); |
448 |
ppc_cpu_stop(); |
449 |
break; |
450 |
} |
451 |
default: |
452 |
IO_CUDA_ERR("unknown cuda (%02x)!\n", gCUDA.data[1]); |
453 |
} |
454 |
} |
455 |
|
456 |
static void cuda_receive_packet() |
457 |
{ |
458 |
IO_CUDA_TRACE2("cuda received packet: (%d) ", gCUDA.pos); |
459 |
switch (gCUDA.data[0]) { |
460 |
case ADB_PACKET: |
461 |
cuda_receive_adb_packet(); |
462 |
break; |
463 |
case CUDA_PACKET: |
464 |
cuda_receive_cuda_packet(); |
465 |
break; |
466 |
case ERROR_PACKET: |
467 |
IO_CUDA_TRACE2("ERROR_PACKET %02x %02x\n", gCUDA.data[1], gCUDA.data[2]); |
468 |
IO_CUDA_ERR("error packet\n"); |
469 |
break; |
470 |
case TIMER_PACKET: |
471 |
IO_CUDA_TRACE2("TIMER_PACKET %02x %02x\n", gCUDA.data[1], gCUDA.data[2]); |
472 |
IO_CUDA_ERR("timer packet\n"); |
473 |
break; |
474 |
case POWER_PACKET: |
475 |
IO_CUDA_TRACE2("POWER_PACKET %02x %02x\n", gCUDA.data[1], gCUDA.data[2]); |
476 |
IO_CUDA_ERR("power packet\n"); |
477 |
break; |
478 |
case MACIIC_PACKET: |
479 |
IO_CUDA_TRACE2("MACIIC_PACKET %02x %02x\n", gCUDA.data[1], gCUDA.data[2]); |
480 |
IO_CUDA_ERR("maciic packet\n"); |
481 |
break; |
482 |
case PMU_PACKET: |
483 |
IO_CUDA_TRACE2("PMU_PACKET %02x %02x\n", gCUDA.data[1], gCUDA.data[2]); |
484 |
IO_CUDA_ERR("pmu packet\n"); |
485 |
break; |
486 |
default: |
487 |
IO_CUDA_TRACE2("unknown generic (%02x)!\n", gCUDA.data[0]); |
488 |
IO_CUDA_ERR("unknown packet\n", gCUDA.data[0]); |
489 |
break; |
490 |
} |
491 |
} |
492 |
|
493 |
static void cuda_update_T1() |
494 |
{ |
495 |
uint64 clk = sys_get_hiresclk_ticks(); |
496 |
if (clk < gCUDA.T1_end) { |
497 |
uint64 ticks_per_sec = 1000ULL * sys_get_hiresclk_ticks_per_second(); |
498 |
uint64 T1 = (gCUDA.T1_end - clk) * VIA_TIMER_FREQ_DIV_HZ_TIMES_1000 / ticks_per_sec; |
499 |
gCUDA.rT1CL = T1; |
500 |
gCUDA.rT1CH = T1 >> 8; |
501 |
gCUDA.rIFR &= ~T1_INT; |
502 |
// |
503 |
// uint64 tmp = gCUDA.T1_end - clk; |
504 |
// IO_CUDA_WARN("T1 running, T1 now %04x, T1_end-clk=%08qx\n", (uint32)T1, tmp); |
505 |
} else { |
506 |
uint64 ticks_per_sec = 1000ULL * sys_get_hiresclk_ticks_per_second(); |
507 |
uint64 T1_latch = (gCUDA.rT1LH << 8) | gCUDA.rT1LL; |
508 |
uint64 full_T1_interval_ticks = (T1_latch+1) * ticks_per_sec / VIA_TIMER_FREQ_DIV_HZ_TIMES_1000; |
509 |
uint64 T1_end = clk + full_T1_interval_ticks - (clk - gCUDA.T1_end) % full_T1_interval_ticks; |
510 |
gCUDA.T1_end = T1_end; |
511 |
uint64 T1 = (gCUDA.T1_end - clk) * VIA_TIMER_FREQ_DIV_HZ_TIMES_1000 / ticks_per_sec; |
512 |
gCUDA.rT1CL = T1; |
513 |
gCUDA.rT1CH = T1 >> 8; |
514 |
gCUDA.rIFR |= T1_INT; |
515 |
// |
516 |
// uint64 tmp = gCUDA.T1_end - clk; |
517 |
// IO_CUDA_WARN("T1 overflowed, setting interrupt flag, T1 set to %04x, T1_end-clk=%08qx, T1_latch = %04x\n", (uint32)T1, tmp, T1_latch); |
518 |
} |
519 |
} |
520 |
|
521 |
static void cuda_start_T1() |
522 |
{ |
523 |
uint64 clk = sys_get_hiresclk_ticks(); |
524 |
uint64 ticks_per_sec = 1000ULL * sys_get_hiresclk_ticks_per_second(); |
525 |
uint32 T1 = (gCUDA.rT1CH << 8) | gCUDA.rT1CL; |
526 |
/* uint64 tmp = static_cast<uint64>(T1) * ticks_per_sec / VIA_TIMER_FREQ_DIV_HZ_TIMES_1000; |
527 |
printf("T1 for %lld ticks (%g seconds vs. %g)\n", |
528 |
tmp, static_cast<double>(tmp)/static_cast<double>(ticks_per_sec / 1000), |
529 |
static_cast<double>(T1) * 1.27655 / 1000000.0);*/ |
530 |
gCUDA.T1_end = clk + T1 * ticks_per_sec / VIA_TIMER_FREQ_DIV_HZ_TIMES_1000; |
531 |
gCUDA.rIFR &= ~T1_INT; |
532 |
IO_CUDA_TRACE("T1 restarted, T1 = %08x\n", T1); |
533 |
} |
534 |
|
535 |
void cuda_write(uint32 addr, uint32 data, int size) |
536 |
{ |
537 |
sys_lock_mutex(gCUDAMutex); |
538 |
|
539 |
IO_CUDA_TRACE("%d write word @%08x: %08x\n", gCUDA.state, addr, data); |
540 |
addr -= IO_CUDA_PA_START; |
541 |
switch (addr) { |
542 |
case A: |
543 |
IO_CUDA_TRACE("->A\n"); |
544 |
gCUDA.rA = data; |
545 |
break; |
546 |
case B: { |
547 |
bool ack = false; |
548 |
if (gCUDA.rB & TACK) { |
549 |
if (!(data & TACK)) { |
550 |
gCUDA.rIFR |= SR_INT; |
551 |
if (gCUDA.state == cuda_idle) { |
552 |
data &= ~TREQ; |
553 |
} |
554 |
ack = true; |
555 |
} |
556 |
} else { |
557 |
if ((data & TACK)) { |
558 |
gCUDA.rIFR |= SR_INT; |
559 |
if (gCUDA.state == cuda_idle) { |
560 |
if (data & TIP) { |
561 |
data |= TREQ; |
562 |
} else { |
563 |
data &= ~TREQ; |
564 |
} |
565 |
} |
566 |
ack = true; |
567 |
} |
568 |
} |
569 |
if ((gCUDA.state == cuda_reading) && ack && (gCUDA.rACR & SR_OUT) |
570 |
&& !(!(gCUDA.rB & TIP) && (data & TIP))) { |
571 |
// don't ask... |
572 |
gCUDA.data[gCUDA.pos] = gCUDA.rSR; |
573 |
IO_CUDA_TRACE2(";; %d:%x\n", gCUDA.pos, gCUDA.rSR); |
574 |
gCUDA.pos++; |
575 |
if (gCUDA.pos > 10) { |
576 |
gCUDA.pos = 0; |
577 |
IO_CUDA_ERR("cuda overflow!\n"); |
578 |
} |
579 |
} |
580 |
if ((gCUDA.state == cuda_writing) && ack) { |
581 |
if (gCUDA.left <= 1) { |
582 |
data |= TREQ; |
583 |
} |
584 |
// gCUDA.rB = data; |
585 |
// break; |
586 |
} |
587 |
if ((gCUDA.rB & TIP) && !(data & TIP)) { |
588 |
gCUDA.rIFR |= SR_INT; |
589 |
// IO_CUDA_TRACE2("^ from: %08x %02x\n", gCPU.pc, gCUDA.rIFR); |
590 |
if (gCUDA.rACR & SR_OUT) { |
591 |
gCUDA.state = cuda_reading; |
592 |
IO_CUDA_TRACE2("CUDA CHANGE STATE %d: to %d\n", __LINE__, gCUDA.state); |
593 |
gCUDA.pos = 1; |
594 |
data |= TREQ; |
595 |
gCUDA.data[0] = gCUDA.rSR; |
596 |
IO_CUDA_TRACE2(";; %d:%x\n", gCUDA.pos, gCUDA.rSR); |
597 |
} else { |
598 |
if (gCUDA.left) { |
599 |
gCUDA.state = cuda_writing; |
600 |
IO_CUDA_TRACE2("CUDA CHANGE STATE %d: to %d\n", __LINE__, gCUDA.state); |
601 |
} else { |
602 |
// data &= ~TIP; |
603 |
} |
604 |
data &= ~TREQ; |
605 |
} |
606 |
} |
607 |
pic_raise_interrupt(IO_PIC_IRQ_CUDA); |
608 |
if (!(gCUDA.rB & TIP) && (data & TIP)) { |
609 |
gCUDA.rIFR |= SR_INT; |
610 |
// IO_CUDA_TRACE2("v from: %08x %d\n", gCPU.pc, gCUDA.state); |
611 |
data |= TREQ | TIP; |
612 |
gCUDA.rB = data; |
613 |
if (gCUDA.state == cuda_reading) { |
614 |
cuda_receive_packet(); |
615 |
if (!gCUDA.left) { |
616 |
// pic_cancel_interrupt(IO_PIC_IRQ_CUDA); |
617 |
gCUDA.rIFR &= ~SR_INT; |
618 |
} |
619 |
} else if (gCUDA.state == cuda_writing) { |
620 |
IO_CUDA_TRACE2("cuda sent packet (%d)\n", gCUDA.pos); |
621 |
gCUDA.left = 0; |
622 |
gCUDA.pos = 0; |
623 |
} |
624 |
gCUDA.state = cuda_idle; |
625 |
sys_signal_semaphore(gCUDA.idle_sem); |
626 |
IO_CUDA_TRACE2("CUDA CHANGE STATE %d: to %d\n", __LINE__, gCUDA.state); |
627 |
} else { |
628 |
gCUDA.rB = data; |
629 |
} |
630 |
IO_CUDA_TRACE("->B(%02x)\n", gCUDA.rB); |
631 |
break; |
632 |
} |
633 |
case DIRB: |
634 |
IO_CUDA_TRACE("->DIRB\n"); |
635 |
gCUDA.rDIRB = data; |
636 |
break; |
637 |
case DIRA: |
638 |
IO_CUDA_TRACE("->DIRA\n"); |
639 |
gCUDA.rDIRA = data; |
640 |
break; |
641 |
case T1CL: |
642 |
IO_CUDA_TRACE("->T1CL\n"); |
643 |
// same as writing to T1LL |
644 |
gCUDA.rT1CL = data; |
645 |
gCUDA.rT1LL = data; |
646 |
break; |
647 |
case T1CH: |
648 |
IO_CUDA_TRACE("->T1CH\n"); |
649 |
/* from [1]: "[T1C-L] is loaded automatically from the low-order\ |
650 |
* latch (T1L-L) when the processor writes into the high-order counter\ |
651 |
* (T1C-H)" |
652 |
* and: "8 bits loaded into high-order latches. also at this time both \ |
653 |
* high- and low-order latches transferred into T1 counter" |
654 |
*/ |
655 |
gCUDA.rT1LH = data; |
656 |
gCUDA.rT1CH = gCUDA.rT1LH; |
657 |
gCUDA.rT1CL = gCUDA.rT1LL; |
658 |
cuda_start_T1(); |
659 |
break; |
660 |
case T1LL: |
661 |
IO_CUDA_TRACE("->T1LL\n"); |
662 |
/* from [1]: "this operation is no different than a write into reg 4" |
663 |
* reg4 is T1CL |
664 |
*/ |
665 |
gCUDA.rT1CL = data; |
666 |
gCUDA.rT1LL = data; |
667 |
break; |
668 |
case T1LH: |
669 |
IO_CUDA_TRACE("->T1LH\n"); |
670 |
gCUDA.rT1LH = data; |
671 |
break; |
672 |
case T2CL: |
673 |
IO_CUDA_ERR("->T2CL\n"); |
674 |
gCUDA.rT2CL = data; |
675 |
break; |
676 |
case T2CH: |
677 |
IO_CUDA_ERR("->T2CH\n"); |
678 |
gCUDA.rT2CH = data; |
679 |
break; |
680 |
case ACR: |
681 |
IO_CUDA_TRACE("->ACR\n"); |
682 |
gCUDA.rACR = data; |
683 |
break; |
684 |
case SR: |
685 |
IO_CUDA_TRACE("->SR\n"); |
686 |
gCUDA.rSR = data; |
687 |
break; |
688 |
case PCR: |
689 |
IO_CUDA_TRACE("->PCR\n"); |
690 |
gCUDA.rPCR = data; |
691 |
break; |
692 |
case IFR: |
693 |
IO_CUDA_TRACE("->IFR\n"); |
694 |
gCUDA.rIFR = data; |
695 |
break; |
696 |
case IER: |
697 |
IO_CUDA_TRACE("->IER\n"); |
698 |
gCUDA.rIER = data; |
699 |
break; |
700 |
case ANH: |
701 |
IO_CUDA_TRACE("->ANH\n"); |
702 |
gCUDA.rANH = data; |
703 |
break; |
704 |
default: |
705 |
IO_CUDA_ERR("unknown service\n"); |
706 |
} |
707 |
|
708 |
sys_unlock_mutex(gCUDAMutex); |
709 |
} |
710 |
|
711 |
void cuda_read(uint32 addr, uint32 &data, int size) |
712 |
{ |
713 |
sys_lock_mutex(gCUDAMutex); |
714 |
|
715 |
IO_CUDA_TRACE("%d read word @%08x\n", gCUDA.state, addr); |
716 |
addr -= IO_CUDA_PA_START; |
717 |
switch (addr) { |
718 |
case A: |
719 |
IO_CUDA_TRACE("A(%02x)->\n", gCUDA.rA); |
720 |
data = gCUDA.rA; |
721 |
break; |
722 |
case B: |
723 |
IO_CUDA_TRACE("B(%02x)->\n", gCUDA.rB); |
724 |
data = gCUDA.rB; |
725 |
break; |
726 |
case DIRB: |
727 |
IO_CUDA_TRACE("DIRB(%02x)->\n", gCUDA.rDIRB); |
728 |
data = gCUDA.rDIRB; |
729 |
break; |
730 |
case DIRA: |
731 |
IO_CUDA_TRACE("DIRA->\n"); |
732 |
data = gCUDA.rDIRA; |
733 |
break; |
734 |
case T1CL: |
735 |
IO_CUDA_TRACE("T1CL->\n"); |
736 |
cuda_update_T1(); |
737 |
data = gCUDA.rT1CL; |
738 |
break; |
739 |
case T1CH: { |
740 |
IO_CUDA_TRACE("T1CH->\n"); |
741 |
cuda_update_T1(); |
742 |
// uint64 clk = sys_get_cpu_ticks(); |
743 |
// IO_CUDA_WARN("read %08x: T1 = %04x clk = %08qx, T1_end = %08qx\n", |
744 |
// gCPU.current_code_base + gCPU.pc_ofs, |
745 |
// (gCUDA.rT1CH<<8) | gCUDA.rT1CL, |
746 |
// clk, gCUDA.T1_end); |
747 |
data = gCUDA.rT1CH; |
748 |
break; |
749 |
} |
750 |
case T1LL: |
751 |
IO_CUDA_WARN("T1LL->\n"); |
752 |
data = gCUDA.rT1LL; |
753 |
break; |
754 |
case T1LH: |
755 |
IO_CUDA_WARN("T1LH->\n"); |
756 |
data = gCUDA.rT1LH; |
757 |
break; |
758 |
case T2CL: |
759 |
IO_CUDA_ERR("T2CL->\n"); |
760 |
data = gCUDA.rT2CL; |
761 |
break; |
762 |
case T2CH: |
763 |
IO_CUDA_ERR("T2CH->\n"); |
764 |
data = gCUDA.rT2CH; |
765 |
break; |
766 |
case ACR: |
767 |
IO_CUDA_TRACE("ACR->\n"); |
768 |
data = gCUDA.rACR; |
769 |
break; |
770 |
case SR: |
771 |
IO_CUDA_TRACE("SR->\n"); |
772 |
data = gCUDA.rSR; |
773 |
if (gCUDA.state == cuda_writing) { |
774 |
if (gCUDA.left) { |
775 |
data = gCUDA.data[gCUDA.pos]; |
776 |
IO_CUDA_TRACE2("::%d:%02x\n", gCUDA.pos, data); |
777 |
gCUDA.pos++; |
778 |
gCUDA.left--; |
779 |
} |
780 |
if (gCUDA.left <= 0) { |
781 |
IO_CUDA_TRACE2("stop\n"); |
782 |
gCUDA.rB |= TREQ; |
783 |
gCUDA.rB &= ~TIP; |
784 |
} |
785 |
gCUDA.rIFR &= ~SR_INT; |
786 |
} else if (gCUDA.state == cuda_reading) { |
787 |
gCUDA.rB &= ~TREQ; |
788 |
gCUDA.rIFR &= ~SR_INT; |
789 |
} else { |
790 |
gCUDA.rB |= TREQ; |
791 |
gCUDA.rIFR &= ~SR_INT; |
792 |
} |
793 |
break; |
794 |
case PCR: |
795 |
IO_CUDA_TRACE("PCR->\n"); |
796 |
data = gCUDA.rPCR; |
797 |
break; |
798 |
case IFR: |
799 |
data = gCUDA.rIFR; |
800 |
if (gCUDA.state == cuda_idle) { |
801 |
if (!gCUDA.left /*&& !(gCUDA.rIER & SR_INT)*/) { |
802 |
if (cuda_interrupt()) { |
803 |
data |= SR_INT; |
804 |
// if (gCUDA.autopoll) pic_raise_interrupt(IO_PIC_IRQ_CUDA); |
805 |
} |
806 |
} |
807 |
// ht_printf("is idle!\n"); |
808 |
} else { |
809 |
// ht_printf("state not idle bla !\n"); |
810 |
// data |= SR_INT; |
811 |
} |
812 |
cuda_update_T1(); |
813 |
IO_CUDA_TRACE("%d IFR->(%02x/%02x)\n", gCUDA.state, gCUDA.rIFR, data); |
814 |
break; |
815 |
case IER: |
816 |
IO_CUDA_TRACE("IER->\n"); |
817 |
data = gCUDA.rIER; |
818 |
break; |
819 |
case ANH: |
820 |
IO_CUDA_TRACE("ANH->\n"); |
821 |
data = gCUDA.rANH; |
822 |
break; |
823 |
default: |
824 |
IO_CUDA_ERR("unknown service\n"); |
825 |
} |
826 |
|
827 |
sys_unlock_mutex(gCUDAMutex); |
828 |
} |
829 |
|
830 |
bool cuda_interrupt() |
831 |
{ |
832 |
return false; |
833 |
} |
834 |
|
835 |
static sys_semaphore gCUDAEventSem; |
836 |
static Queue gCUDAEvents(true); |
837 |
|
838 |
static bool cudaEventHandler(const SystemEvent &ev) |
839 |
{ |
840 |
sys_lock_semaphore(gCUDAEventSem); |
841 |
// ht_printf("queue %d\n", ev.key.pressed); |
842 |
gCUDAEvents.enQueue(new SystemEventObject(ev)); |
843 |
sys_unlock_semaphore(gCUDAEventSem); |
844 |
sys_signal_semaphore(gCUDAEventSem); |
845 |
return true; |
846 |
} |
847 |
|
848 |
static bool doProcessCudaEvent(const SystemEvent &ev) |
849 |
{ |
850 |
switch (ev.type) { |
851 |
case sysevKey: { |
852 |
uint8 k = ev.key.keycode; |
853 |
if (!ev.key.pressed) { |
854 |
k |= 0x80; |
855 |
} |
856 |
cuda_send_packet(ADB_PACKET, 4, 0x40, 0x2c, k, 0xff); |
857 |
return true; |
858 |
} |
859 |
case sysevMouse: { |
860 |
int dx = ev.mouse.relx; //* 256 / gDisplay->mClientChar.width; |
861 |
int dy = ev.mouse.rely; //* 256 / gDisplay->mClientChar.height; |
862 |
if (dx < 0) { |
863 |
if (dx < -63) { |
864 |
dx = 127; |
865 |
} else { |
866 |
dx += 128; |
867 |
} |
868 |
} else if (dx > 63) { |
869 |
dx = 63; |
870 |
} |
871 |
if (dy < 0) { |
872 |
if (dy < -63) { |
873 |
dy = 127; |
874 |
} else { |
875 |
dy += 128; |
876 |
} |
877 |
} else if (dy > 63) { |
878 |
dy = 63; |
879 |
} |
880 |
if (!ev.mouse.button2) dx |= 0x80; |
881 |
if (!ev.mouse.button1) dy |= 0x80; |
882 |
// ht_printf("adb mouse: cur: %d, %d d: %d, %d\n", ev.mouseEvent.x, ev.mouseEvent.y, dx, dy); |
883 |
cuda_send_packet(ADB_PACKET, 4, 0x40, 0x3c, dy, dx); |
884 |
return true; |
885 |
} |
886 |
default: |
887 |
return false; |
888 |
} |
889 |
} |
890 |
|
891 |
static bool tryProcessCudaEvent(const SystemEvent &ev) |
892 |
{ |
893 |
uint timeout_msec = 200; |
894 |
uint64 time_end = sys_get_hiresclk_ticks() + sys_get_hiresclk_ticks_per_second() |
895 |
* timeout_msec / 1000; |
896 |
// ht_printf("process %d\n", ev.key.pressed); |
897 |
while (sys_get_hiresclk_ticks() < time_end) { |
898 |
sys_lock_mutex(gCUDAMutex); |
899 |
static int lockuphack = 0; |
900 |
if (gCUDA.state == cuda_idle) { |
901 |
if (!gCUDA.left /*&& !(gCUDA.rIFR & SR_INT)*/) { |
902 |
lockuphack = 0; |
903 |
bool k = doProcessCudaEvent(ev); |
904 |
sys_unlock_mutex(gCUDAMutex); |
905 |
// IO_CUDA_WARN("Tried to process event: %d.\n", k); |
906 |
return k; |
907 |
} else { |
908 |
IO_CUDA_TRACE2("left: %d\n", gCUDA.left); |
909 |
if (lockuphack++ == 20) { |
910 |
/* gCUDA.left = 0; |
911 |
gCUDA.rA = TREQ; |
912 |
gCUDA.rACR = 0; |
913 |
lockuphack = 0; |
914 |
IO_CUDA_WARN("lock-up parachute\n");*/ |
915 |
} |
916 |
} |
917 |
} else { |
918 |
IO_CUDA_TRACE2("cuda not idle (%d)!\n", gCUDA.state); |
919 |
} |
920 |
sys_unlock_mutex(gCUDAMutex); |
921 |
sys_lock_semaphore(gCUDA.idle_sem); |
922 |
sys_wait_semaphore_bounded(gCUDA.idle_sem, 10); |
923 |
sys_unlock_semaphore(gCUDA.idle_sem); |
924 |
} |
925 |
IO_CUDA_WARN("Event processing timed out. Event dropped.\n"); |
926 |
return false; |
927 |
} |
928 |
|
929 |
static void *cudaEventLoop(void *arg) |
930 |
{ |
931 |
if (sys_create_semaphore(&gCUDAEventSem)) { |
932 |
IO_CUDA_ERR("Can't create semaphore\n"); |
933 |
} |
934 |
gKeyboard->attachEventHandler(cudaEventHandler); |
935 |
gMouse->attachEventHandler(cudaEventHandler); |
936 |
sys_lock_semaphore(gCUDAEventSem); |
937 |
while (1) { |
938 |
// IO_CUDA_WARN("waiting on semaphore\n"); |
939 |
sys_wait_semaphore(gCUDAEventSem); |
940 |
// IO_CUDA_WARN("semaphore signalled\n"); |
941 |
SystemEventObject *seo; |
942 |
while ((seo = (SystemEventObject*)gCUDAEvents.deQueue())) { |
943 |
tryProcessCudaEvent(seo->mEv); |
944 |
delete seo; |
945 |
} |
946 |
} |
947 |
} |
948 |
|
949 |
bool cuda_prom_get_key(uint32 &key) |
950 |
{ |
951 |
if (gCUDA.left == 5 && gCUDA.data[2] == 0x2c) { |
952 |
key = gCUDA.data[3]; |
953 |
gCUDA.left = 0; |
954 |
return true; |
955 |
} else { |
956 |
gCUDA.left = 0; |
957 |
return false; |
958 |
} |
959 |
} |
960 |
|
961 |
void cuda_init() |
962 |
{ |
963 |
memset(&gCUDA, 0, sizeof gCUDA); |
964 |
gCUDA.state = cuda_idle; |
965 |
gCUDA.keybaddr = ADB_KEYBOARD; |
966 |
gCUDA.keybhandler = 1; |
967 |
gCUDA.mouseaddr = ADB_MOUSE; |
968 |
gCUDA.mousehandler = 2; |
969 |
gCUDA.T1_end = 0; |
970 |
gCUDA.rT1LL = 0xff; |
971 |
gCUDA.rT1LH = 0xff; |
972 |
|
973 |
if (sys_create_mutex(&gCUDAMutex)) { |
974 |
IO_CUDA_ERR("Can't create mutex\n"); |
975 |
} |
976 |
|
977 |
if (sys_create_semaphore(&gCUDA.idle_sem)) { |
978 |
IO_CUDA_ERR("Can't create semaphore\n"); |
979 |
} |
980 |
|
981 |
sys_thread cudaEventLoopThread; |
982 |
sys_create_thread(&cudaEventLoopThread, 0, cudaEventLoop, NULL); |
983 |
} |
984 |
|
985 |
void cuda_done() |
986 |
{ |
987 |
sys_destroy_mutex(gCUDAMutex); |
988 |
sys_destroy_semaphore(gCUDA.idle_sem); |
989 |
} |
990 |
|
991 |
void cuda_init_config() |
992 |
{ |
993 |
} |