1 |
/* |
2 |
* Display_Acorn.h - C64 graphics display, emulator window handling, |
3 |
* RISC OS specific stuff |
4 |
* |
5 |
* Frodo (C) 1994-1997,2002-2009 Christian Bauer |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
9 |
* the Free Software Foundation; either version 2 of the License, or |
10 |
* (at your option) any later version. |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
*/ |
21 |
|
22 |
|
23 |
#include "C64.h" |
24 |
#include "ROlib.h" |
25 |
#include "AcornGUI.h" |
26 |
#include "SAM.h" |
27 |
#include "VIC.h" |
28 |
|
29 |
|
30 |
|
31 |
// (from Display_x.i) |
32 |
/* |
33 |
C64 keyboard matrix: |
34 |
|
35 |
Bit 7 6 5 4 3 2 1 0 |
36 |
0 CUD F5 F3 F1 F7 CLR RET DEL |
37 |
1 SHL E S Z 4 A W 3 |
38 |
2 X T F C 6 D R 5 |
39 |
3 V U H B 8 G Y 7 |
40 |
4 N O K M 0 J I 9 |
41 |
5 , @ : . - L P + |
42 |
6 / ^ = SHR HOM ; * £ |
43 |
7 R/S Q C= SPC 2 CTL <- 1 |
44 |
*/ |
45 |
|
46 |
|
47 |
#define IntKey_MinCode 3 // Scan from ShiftLeft (leave out Shift, Ctrl, Alt) |
48 |
#define IntKey_MaxCode 124 |
49 |
#define IntKey_Copy 105 |
50 |
|
51 |
// Maps internal keyboard numbers (Acorn) to C64 keyboard-matrix. |
52 |
// Format: top nibble - row#, bottom nibble - column (bit#). |
53 |
// Entry == 0xff <==> don't map |
54 |
char KeysAcornToCBM[] = { |
55 |
0x17, 0x72, 0x75, 0x17, // 0 - 3: SHL, CTRL, ALT(C=), SHL |
56 |
0x72, 0x75, 0x64, 0x72, // 4 - 7: CTRL, ALT, SHR, CTRL |
57 |
0x75, 0xff, 0xff, 0xff, // 8 - 11: ALT, MouseSlct, MouseMen, MouseAdj |
58 |
0xff, 0xff, 0xff, 0xff, // 12 - 15: dummies |
59 |
0x76, 0x10, 0x13, 0x20, // 16 - 19: q, 3,4,5 |
60 |
0x03, 0x33, 0xff, 0x53, // 20 - 23: F4(F7), 8, F7, - |
61 |
0x23, 0x02, 0xff, 0xff, // 24 - 27: 6, crsrL, num6, num7 |
62 |
0xff, 0xff, 0xff, 0xff, // 28 - 31: F11, F12, F10, ScrLock |
63 |
0xff, 0x11, 0x16, 0x26, // 32 - 35: Print, w, e, t |
64 |
0x30, 0x41, 0x40, 0x43, // 36 - 39: 7, i, 9, 0 |
65 |
0x53, 0x07, 0xff, 0xff, // 40 - 43: -, crsrD, num8, num9 |
66 |
0x77, 0x71, 0x60, 0x00, // 44 - 47: break, `, £, DEL |
67 |
0x70, 0x73, 0x22, 0x21, // 48 - 51: 1, 2, d, r |
68 |
0x23, 0x36, 0x46, 0x51, // 52 - 55: 6, u, o, p |
69 |
0x56, 0x07, 0x50, 0x53, // 56 - 59: [(@), crsrU, num+(+), num-(-) |
70 |
0xff, 0x00, 0x63, 0xff, // 60 - 63: numENTER, insert, home, pgUp |
71 |
0x17, 0x12, 0x27, 0x25, // 64 - 67: capsLCK, a, x, f |
72 |
0x31, 0x42, 0x45, 0x73, // 68 - 71: y, j, k, 2 |
73 |
0x55, 0x01, 0xff, 0xff, // 72 - 75: ;(:), RET, num/, dummy |
74 |
0xff, 0xff, 0xff, 0x62, // 76 - 79: num., numLCK, pgDown, '(;) |
75 |
0xff, 0x15, 0x24, 0x32, // 80 - 83: dummy, s, c, g |
76 |
0x35, 0x47, 0x52, 0x55, // 84 - 87: h, n, l, ;(:) |
77 |
0x61, 0x00, 0xff, 0xff, // 88 - 91: ](*), Delete, num#, num* |
78 |
0xff, 0x65, 0xff, 0xff, // 92 - 95: dummy, =, dummies |
79 |
0x72, 0x14, 0x74, 0x37, // 96 - 99: TAB(CTRL), z, SPACE, v |
80 |
0x34, 0x44, 0x57, 0x54, // 100-103: b, m, ',', . |
81 |
0x67, 0xff, 0xff, 0xff, // 104-107: /, Copy, num0, num1 |
82 |
0xff, 0xff, 0xff, 0xff, // 108-111: num3, dummies |
83 |
0x77, 0x04, 0x05, 0x06, // 112-115: ESC, F1(F1), F2(F3), F3(F5) |
84 |
0xff, 0xff, 0xff, 0xff, // 116-119: F5, F6, F8, F9 |
85 |
0x66, 0x02, 0xff, 0xff, // 120-123: \(^), crsrR, num4, num5 |
86 |
0xff, 0xff, 0xff, 0xff // 124-127: num2, dummies |
87 |
}; |
88 |
|
89 |
|
90 |
// Special keycodes that have to be processed seperately: |
91 |
#define IntKey_CrsrL 25 |
92 |
#define IntKey_CrsrR 121 |
93 |
#define IntKey_CrsrU 57 |
94 |
#define IntKey_CrsrD 41 |
95 |
#define IntKey_Insert 61 |
96 |
#define IntKey_NumLock 77 |
97 |
#define IntKey_F5 116 |
98 |
#define IntKey_F6 117 |
99 |
#define IntKey_F7 22 |
100 |
#define IntKey_F8 118 |
101 |
#define IntKey_PageUp 63 |
102 |
#define IntKey_PageDown 78 |
103 |
#define IntKey_NumSlash 74 |
104 |
#define IntKey_NumStar 91 |
105 |
#define IntKey_NumCross 90 |
106 |
|
107 |
#define KeyJoy1_Up 108 // num3 |
108 |
#define KeyJoy1_Down 76 // num. |
109 |
#define KeyJoy1_Left 107 // num1 |
110 |
#define KeyJoy1_Right 124 // num2 |
111 |
#define KeyJoy1_Fire 60 // numReturn |
112 |
#define KeyJoy2_Up 67 // "f" |
113 |
#define KeyJoy2_Down 82 // "c" |
114 |
#define KeyJoy2_Left 97 // "z" |
115 |
#define KeyJoy2_Right 66 // "x" |
116 |
#define KeyJoy2_Fire 83 // "g" |
117 |
|
118 |
|
119 |
|
120 |
|
121 |
C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) |
122 |
{ |
123 |
int i; |
124 |
|
125 |
bitmap = new uint8[DISPLAY_X * DISPLAY_Y]; |
126 |
screen = new ROScreen(); |
127 |
ModeChange(); |
128 |
for (i=0; i<8; i++) {lastkeys[i] = 0;} |
129 |
// First joystick: mapped to port 2 if numLOCK is on, else port 2 |
130 |
JoystickKeys[0].up = KeyJoy1_Up; JoystickKeys[0].down = KeyJoy1_Down; |
131 |
JoystickKeys[0].left = KeyJoy1_Left; JoystickKeys[0].right = KeyJoy1_Right; |
132 |
JoystickKeys[0].fire = KeyJoy1_Fire; |
133 |
// Second joystick: only active if numLOCK is off! Mapped to port 2 then. |
134 |
JoystickKeys[1].up = KeyJoy2_Up; JoystickKeys[1].down = KeyJoy2_Down; |
135 |
JoystickKeys[1].left = KeyJoy2_Left; JoystickKeys[1].right = KeyJoy2_Right; |
136 |
JoystickKeys[1].fire = KeyJoy2_Fire; |
137 |
} |
138 |
|
139 |
|
140 |
C64Display::~C64Display(void) |
141 |
{ |
142 |
delete bitmap; delete screen; |
143 |
} |
144 |
|
145 |
|
146 |
void C64Display::ModeChange(void) |
147 |
{ |
148 |
register int i; |
149 |
|
150 |
screen->ReadMode(); |
151 |
// find best matching colours in current mode. |
152 |
switch (screen->ldbpp) |
153 |
{ |
154 |
case 0: |
155 |
case 1: |
156 |
case 2: |
157 |
case 3: for (i=0; i<16; i++) // for 1,2,4 and 8bpp |
158 |
{ |
159 |
mode_cols[i] = ModeColourNumber((palette_blue[i] << 24) + (palette_green[i] << 16) + (palette_red[i] << 8)); |
160 |
} |
161 |
break; |
162 |
case 4: for (i=0; i<16; i++) // for 16bpp |
163 |
{ |
164 |
int r,g,b; |
165 |
|
166 |
r = (palette_red[i] + 4) & 0x1f8; if (r > 0xff) {r = 0xf8;} |
167 |
g = (palette_green[i] + 4) & 0x1f8; if (g > 0xff) {g = 0xf8;} |
168 |
b = (palette_blue[i] + 4) & 0x1f8; if (b > 0xff) {b = 0xf8;} |
169 |
mode_cols[i] = (r >> 3) | (g << 2) | (b << 7); |
170 |
} |
171 |
break; |
172 |
case 5: for (i=0; i<16; i++) // for 32bpp |
173 |
{ |
174 |
mode_cols[i] = palette_red[i] | (palette_green[i] << 8) | (palette_blue[i] << 16); |
175 |
} |
176 |
break; |
177 |
} |
178 |
} |
179 |
|
180 |
|
181 |
uint8 *C64Display::BitmapBase(void) |
182 |
{ |
183 |
return bitmap; |
184 |
} |
185 |
|
186 |
|
187 |
void C64Display::InitColors(uint8 *colors) |
188 |
{ |
189 |
register int i; |
190 |
|
191 |
// write index mapping C64colours -> ROcolours |
192 |
if (screen->ldbpp <= 3) // at most 8bpp ==> use actual colour |
193 |
{ |
194 |
for (i=0; i<256; i++) {colors[i] = mode_cols[i&15];} |
195 |
} |
196 |
else // else use index (takes time but can't be changed... |
197 |
{ |
198 |
for (i=0; i<256; i++) {colors[i] = i&15;} |
199 |
} |
200 |
} |
201 |
|
202 |
|
203 |
int C64Display::BitmapXMod(void) |
204 |
{ |
205 |
return DISPLAY_X; |
206 |
} |
207 |
|
208 |
|
209 |
// This routine reads the raw keyboard data from the host machine. Not entirely |
210 |
// conformant with Acorn's rules but the only way to detect multiple simultaneous |
211 |
// keypresses. |
212 |
void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick, uint8 *joystick2) |
213 |
{ |
214 |
register int scan_from=IntKey_MinCode, code, row, col; |
215 |
int status; |
216 |
uint8 kjoy, kjoy2; |
217 |
uint32 newkeys[8]; |
218 |
UBYTE kjoy, kjoy2; |
219 |
|
220 |
// Clear keyboard |
221 |
for (code=0; code<8; code++) {key_matrix[code] = 0xff; rev_matrix[code] = 0xff; newkeys[code] = 0;} |
222 |
kjoy = kjoy2 = 0xff; |
223 |
status = ReadKeyboardStatus(); |
224 |
if ((status & 16) == 0) {key_matrix[1] &= 0x7f; rev_matrix[7] &= 0xfd;} // Caps lock |
225 |
|
226 |
while (scan_from <= IntKey_MaxCode) |
227 |
{ |
228 |
if ((code = ScanKeys(scan_from)) != 0xff) |
229 |
{ |
230 |
newkeys[code >> 5] |= (1 << (code & 0x1f)); // update keys pressed |
231 |
row = KeysAcornToCBM[code]; |
232 |
if ((status & 4) != 0) // numLOCK off? ==> check for 2nd keyboard joystick too |
233 |
{ |
234 |
if (code == JoystickKeys[1].up) {kjoy2 &= 0xfe; row = 0xff;} |
235 |
else if (code == JoystickKeys[1].down) {kjoy2 &= 0xfd; row = 0xff;} |
236 |
else if (code == JoystickKeys[1].left) {kjoy2 &= 0xfb; row = 0xff;} |
237 |
else if (code == JoystickKeys[1].right) {kjoy2 &= 0xf7; row = 0xff;} |
238 |
else if (code == JoystickKeys[1].fire) {kjoy2 &= 0xef; row = 0xff;} |
239 |
} |
240 |
// check 1st keyboard joystick |
241 |
if (code == JoystickKeys[0].up) {kjoy &= 0xfe; row = 0xff;} |
242 |
else if (code == JoystickKeys[0].down) {kjoy &= 0xfd; row = 0xff;} |
243 |
else if (code == JoystickKeys[0].left) {kjoy &= 0xfb; row = 0xff;} |
244 |
else if (code == JoystickKeys[0].right) {kjoy &= 0xf7; row = 0xff;} |
245 |
else if (code == JoystickKeys[0].fire) {kjoy &= 0xef; row = 0xff;} |
246 |
|
247 |
// If key not mapped to joystick: try mapping to keyboard |
248 |
if (row != 0xff) |
249 |
{ |
250 |
col = row & 7; row >>= 4; |
251 |
key_matrix[row] &= ~(1<<col); rev_matrix[col] &= ~(1<<row); |
252 |
} |
253 |
|
254 |
// None of the keys listed below should be used for |
255 |
// joystick definitions since they're always used here. |
256 |
switch(code) |
257 |
{ |
258 |
// For either of these: additionally set SHIFT key. |
259 |
case IntKey_CrsrL: // already mapped to CrsrL |
260 |
case IntKey_CrsrU: // already mapped to CrsrD |
261 |
case IntKey_Insert: // already mapped to DEL |
262 |
key_matrix[6] &= (0xff - (1<<4)); rev_matrix[4] &= (0xff - (1<<6)); |
263 |
break; |
264 |
case IntKey_F6: |
265 |
if ((status & 2) == 0) // call SAM only in multitasking mode! |
266 |
{ |
267 |
TheC64->Pause(); SAM(TheC64); TheC64->Resume(); |
268 |
} |
269 |
break; |
270 |
case IntKey_F7: TheC64->NMI(); break; |
271 |
case IntKey_F8: TheC64->Reset(); break; |
272 |
default: break; |
273 |
} |
274 |
|
275 |
// These shouldn't auto-repeat, therefore I check them seperately. |
276 |
if ((lastkeys[code >> 5] & (1 << (code & 0x1f))) == 0) |
277 |
{ |
278 |
// Icons should be updated, not force-redrawed (--> single tasking) |
279 |
switch (code) |
280 |
{ |
281 |
// decrease framerate |
282 |
case IntKey_PageUp: |
283 |
TheC64->TheWIMP->PrefsWindow-> |
284 |
WriteIconNumberU(Icon_Prefs_SkipFText,++ThePrefs.SkipFrames); |
285 |
break; |
286 |
// increase framerate |
287 |
case IntKey_PageDown: if (ThePrefs.SkipFrames > 0) |
288 |
{ |
289 |
TheC64->TheWIMP->PrefsWindow-> |
290 |
WriteIconNumberU(Icon_Prefs_SkipFText,--ThePrefs.SkipFrames); |
291 |
} |
292 |
break; |
293 |
// toggle floppy emulation status |
294 |
case IntKey_NumSlash: |
295 |
{ |
296 |
register int eor, i; |
297 |
Prefs *prefs = new Prefs(ThePrefs); |
298 |
|
299 |
// If Emulation active then ungrey icons now, else grey them |
300 |
prefs->Emul1541Proc = !prefs->Emul1541Proc; |
301 |
TheC64->TheWIMP->SetLEDIcons(prefs->Emul1541Proc); |
302 |
TheC64->NewPrefs(prefs); |
303 |
ThePrefs = *prefs; |
304 |
// Show change in prefs window too |
305 |
TheC64->TheWIMP->PrefsWindow-> |
306 |
SetIconState(Icon_Prefs_Emul1541,(prefs->Emul1541Proc)?IFlg_Slct:0,IFlg_Slct); |
307 |
delete prefs; |
308 |
} |
309 |
break; |
310 |
// toggle speed limiter |
311 |
case IntKey_NumStar: |
312 |
ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; |
313 |
TheC64->TheWIMP->SetSpeedLimiter(ThePrefs.LimitSpeed); |
314 |
break; |
315 |
// toggle sound emulation |
316 |
case IntKey_F5: |
317 |
{ |
318 |
Window *pw = TheC64->TheWIMP->PrefsWindow; |
319 |
int i, j; |
320 |
Prefs *prefs = new Prefs(ThePrefs); |
321 |
|
322 |
if (prefs->SIDType == SIDTYPE_NONE) {prefs->SIDType = SIDTYPE_DIGITAL; i = 1;} |
323 |
else {prefs->SIDType = SIDTYPE_NONE; i = 0;} |
324 |
for (j=0; j<3; j++) |
325 |
{ |
326 |
pw->SetIconState(SIDtoIcon[j], (j==i) ? IFlg_Slct : 0, IFlg_Slct); |
327 |
} |
328 |
TheC64->TheWIMP->SoundWindow-> |
329 |
SetIconState(Icon_Sound_Notes, (i==0) ? IFlg_Grey : 0, IFlg_Grey); |
330 |
TheC64->NewPrefs(prefs); |
331 |
ThePrefs = *prefs; |
332 |
delete prefs; |
333 |
} |
334 |
break; |
335 |
case IntKey_Copy: TheC64->Pause(); |
336 |
TheC64->TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Pause,PANE_TEXT_RESUME); break; |
337 |
default: break; |
338 |
} |
339 |
} |
340 |
} |
341 |
scan_from = code+1; |
342 |
} |
343 |
for (code=0; code<8; code++) {lastkeys[code] = newkeys[code];} |
344 |
*joystick = kjoy; *joystick2 = kjoy2; |
345 |
} |
346 |
|
347 |
|
348 |
bool C64Display::NumLock(void) |
349 |
{ |
350 |
return(((ReadKeyboardStatus() & 4) == 0) ? true : false); |
351 |
} |
352 |
|
353 |
|
354 |
/* |
355 |
* Prefs may have changed |
356 |
*/ |
357 |
|
358 |
void C64Display::NewPrefs(Prefs *prefs) |
359 |
{ |
360 |
} |
361 |
|
362 |
|
363 |
void C64Display::Update(void) |
364 |
{ |
365 |
int i, state; |
366 |
int *ic; |
367 |
|
368 |
// Do a redraw of the emulator window |
369 |
TheC64->TheWIMP->UpdateEmuWindow(); |
370 |
|
371 |
// Update the LEDs if necessary |
372 |
for (i=0; i<4; i++) |
373 |
{ |
374 |
if ((state = led_state[i]) != old_led_state[i]) |
375 |
{ |
376 |
ic = (int*)TheC64->TheWIMP->EmuPane->GetIcon(LEDtoIcon[i]); |
377 |
switch(state) |
378 |
{ |
379 |
case LED_OFF: |
380 |
case LED_ERROR_OFF: |
381 |
sprintf((char*)ic[5],"led_off"); break; |
382 |
case LED_ON: |
383 |
sprintf((char*)ic[5],"led_on"); break; |
384 |
case LED_ERROR_ON: |
385 |
sprintf((char*)ic[5],"led_error"); break; |
386 |
} |
387 |
TheC64->TheWIMP->EmuPane->UpdateIcon(LEDtoIcon[i]); // update, not force-redraw! |
388 |
old_led_state[i] = state; |
389 |
} |
390 |
} |
391 |
} |
392 |
|
393 |
|
394 |
unsigned int *C64Display::GetColourTable(void) |
395 |
{ |
396 |
return (mode_cols); |
397 |
} |
398 |
|
399 |
|
400 |
// Check whether unpause-key (copy) is pressed |
401 |
bool C64Display::CheckForUnpause(bool CheckLastState) |
402 |
{ |
403 |
int scan_from = IntKey_MinCode, code; |
404 |
uint32 newkeys[8]; |
405 |
uint32 lastpause; |
406 |
|
407 |
for (code=0; code<8; code++) {newkeys[code] = 0;} |
408 |
|
409 |
while (scan_from <= IntKey_MaxCode) |
410 |
{ |
411 |
if ((code = ScanKeys(scan_from)) != 0xff) |
412 |
{ |
413 |
newkeys[code >> 5] |= (1 << (code & 0x1f)); |
414 |
} |
415 |
scan_from = code+1; |
416 |
} |
417 |
lastpause = lastkeys[IntKey_Copy >> 5] & (1 << (IntKey_Copy & 0x1f)); |
418 |
for (code=0; code<8; code++) {lastkeys[code] = newkeys[code];} |
419 |
// unpause-key pressed? |
420 |
if ((newkeys[IntKey_Copy >> 5] & (1 << (IntKey_Copy & 0x1f))) != 0) |
421 |
{ |
422 |
if ((lastpause == 0) || !CheckLastState) |
423 |
{ |
424 |
TheC64->Resume(); |
425 |
TheC64->TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Pause,PANE_TEXT_PAUSE); |
426 |
return(true); |
427 |
} |
428 |
} |
429 |
return(false); |
430 |
} |
431 |
|
432 |
|
433 |
// Requester dialogue box |
434 |
long ShowRequester(const char *str, const char *button1, const char *button2) |
435 |
{ |
436 |
_kernel_oserror myerr; |
437 |
|
438 |
myerr.errnum = 0x0; strcpy(myerr.errmess,str); |
439 |
Wimp_ReportError(&myerr,1,TASKNAME); // always provide an OK box |
440 |
return(1); |
441 |
} |