1 /*
2  *  Display_Be.i - C64 graphics display, emulator window handling,
3  *                 Be specific stuff
4  *
5  *  Frodo (C) 1994-1997,2002 Christian Bauer
6  *  GameKit stuff by Tinic Urou
7  */
8 
9 #include <AppKit.h>
10 #include <InterfaceKit.h>
11 #include <GameKit.h>
12 #include <string.h>
13 
14 #include "C64.h"
15 #include "main.h"
16 
17 
18 // Window thread messages
19 const uint32 MSG_REDRAW = 1;
20 
21 
22 // C64 display and window frame
23 const BRect DisplayFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1);
24 const BRect WindowFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1 + 16);
25 
26 
27 // Background color
28 const rgb_color fill_gray = {208, 208, 208, 0};
29 const rgb_color shine_gray = {232, 232, 232, 0};
30 const rgb_color shadow_gray = {152, 152, 152, 0};
31 
32 
33 /*
34   C64 keyboard matrix:
35 
36     Bit 7   6   5   4   3   2   1   0
37   0    CUD  F5  F3  F1  F7 CLR RET DEL
38   1    SHL  E   S   Z   4   A   W   3
39   2     X   T   F   C   6   D   R   5
40   3     V   U   H   B   8   G   Y   7
41   4     N   O   K   M   0   J   I   9
42   5     ,   @   :   .   -   L   P   +
43   6     /   ^   =  SHR HOM  ;   *   �
44   7    R/S  Q   C= SPC  2  CTL  <-  1
45 */
46 
47 
48 /*
49   Tables for key translation
50   Bit 0..2: row/column in C64 keyboard matrix
51   Bit 3   : implicit shift
52   Bit 5   : joystick emulation (bit 0..4: mask)
53 */
54 
55 const int key_byte[128] = {
56 	  -1,   7,   0,8+0,   0,8+0,  0, 8+0,
57 	   0, 8+0,  -1, -1,  -1, -1, -1,  -1,
58 
59 	   7,   7,   7,  7,   1,  1,  2,   2,
60 	   3,   3,   4,  4,   5,  5,  0, 8+0,
61 
62 	   6,   6,  -1, -1,  -1, -1, -1,   7,
63 	   1,   1,   2,  2,   3,  3,  4,   4,
64 
65 	   5,   5,   6,  6,   0,  6,  6,0x25,
66 	0x21,0x29,  -1,  1,   1,  1,  2,   2,
67 
68 	   3,   3,   4,  4,   5,  5,  6,   0,
69 	0x24,0x30,0x28,  1,   1,  2,  2,   3,
70 
71 	   3,   4,   4,  5,   5,  6,  6, 8+0,
72 	0x26,0x22,0x2a,  0,   7, -1,  7,  -1,
73 
74 	   7, 8+0,   0,  0,0x30, -1,  7,   7,
75 	  -1,  -1,  -1, -1,  -1, -1, -1,  -1,
76 
77 	  -1,  -1,  -1, -1,  -1, -1, -1,  -1,
78 	  -1,  -1,  -1, -1,  -1, -1, -1,  -1
79 };
80 
81 const int key_bit[128] = {
82 	-1,  7,  4,  4,  5,  5,  6,  6,
83 	 3,  3, -1, -1, -1, -1, -1, -1,
84 
85 	 7,  1,  0,  3,  0,  3,  0,  3,
86 	 0,  3,  0,  3,  0,  3,  0,  0,
87 
88 	 3,  0, -1, -1, -1, -1, -1,  6,
89 	 1,  6,  1,  6,  1,  6,  1,  6,
90 
91 	 1,  6,  1,  6,  0,  0,  5, -1,
92 	-1, -1, -1,  7,  2,  5,  2,  5,
93 
94 	 2,  5,  2,  5,  2,  5,  2,  1,
95 	-1, -1, -1,  7,  4,  7,  4,  7,
96 
97 	 4,  7,  4,  7,  4,  7,  4,  7,
98 	-1, -1, -1,  1,  2, -1,  4, -1,
99 
100 	 5,  2,  7,  2, -1, -1,  5,  5,
101 	-1, -1, -1, -1, -1, -1, -1, -1,
102 
103 	-1, -1, -1, -1, -1, -1, -1, -1,
104 	-1, -1, -1, -1, -1, -1, -1, -1
105 };
106 
107 
108 /*
109  *  A simple view class for blitting a bitmap on the screen
110  */
111 
112 class BitmapView : public BView {
113 public:
114 	BitmapView(BRect frame, BBitmap *bitmap);
115 	virtual void Draw(BRect update);
116 	virtual void KeyDown(const char *bytes, int32 numBytes);
117 	void ChangeBitmap(BBitmap *bitmap);
118 
119 private:
120 	BBitmap *the_bitmap;
121 };
122 
123 
124 /*
125  *  Class for the main C64 display window
126  */
127 
128 class SpeedoView;
129 class LEDView;
130 
131 class C64Window : public BWindow {
132 public:
133 	C64Window();
134 
135 	virtual bool QuitRequested(void);
136 	virtual void MessageReceived(BMessage *msg);
137 
138 	BBitmap *TheBitmap[2];
139 	SpeedoView *Speedometer;
140 	LEDView *LED[4];
141 
142 private:
143 	BitmapView *main_view;
144 };
145 
146 
147 /*
148  *  Class for the main C64 display using the GameKit
149  */
150 
151 class C64Screen : public BWindowScreen {
152 public:
C64Screen(C64Display * display)153 	C64Screen(C64Display *display) : BWindowScreen("Frodo", B_8_BIT_640x480, &error), the_display(display)
154 	{
155 		Lock();
156 		BitmapView *main_view = new BitmapView(Bounds(), NULL);
157 		AddChild(main_view);
158 		main_view->MakeFocus();
159 		Connected = false;
160 		Unlock();
161 	}
162 
163 	virtual void ScreenConnected(bool active);
164 	virtual void DispatchMessage(BMessage *msg, BHandler *handler);
165 	void DrawLED(int i, int state);
166 	void DrawSpeedometer(void);
167 	void FillRect(int x1, int y1, int x2, int y2, int color);
168 
169 	bool Connected;			// Flag: screen connected
170 	int Speed;
171 	char SpeedoStr[16];		// Speedometer value converted to a string
172 
173 private:
174 	C64Display *the_display;
175 	status_t error;
176 };
177 
178 
179 /*
180  *  Class for speedometer
181  */
182 
183 class SpeedoView : public BView {
184 public:
185 	SpeedoView(BRect frame);
186 	virtual void Draw(BRect update);
187 	virtual void Pulse(void);
188 	void SetValue(int percent);
189 
190 private:
191 	char speedostr[16];		// Speedometer value converted to a string
192 	BRect bounds;
193 };
194 
195 
196 /*
197  *  Class for drive LED
198  */
199 
200 class LEDView : public BView {
201 public:
202 	LEDView(BRect frame, const char *label);
203 	virtual void Draw(BRect update);
204 	virtual void Pulse(void);
205 	void DrawLED(void);
206 	void SetState(int state);
207 
208 private:
209 	int current_state;
210 	const char *the_label;
211 	BRect bounds;
212 };
213 
214 
215 /*
216  *  Display constructor: Create window/screen
217  */
218 
C64Display(C64 * the_c64)219 C64Display::C64Display(C64 *the_c64) : TheC64(the_c64)
220 {
221 	// LEDs off
222 	for (int i=0; i<4; i++)
223 		led_state[i] = old_led_state[i] = LED_OFF;
224 
225 	// Open window/screen
226 	draw_bitmap = 1;
227 	if (ThePrefs.DisplayType == DISPTYPE_SCREEN) {
228 		using_screen = true;
229 		the_screen = new C64Screen(this);
230 		the_screen->Show();
231 		while (!the_screen->Connected)
232 			snooze(20000);
233 	} else {
234 		using_screen = false;
235 		the_window = new C64Window();
236 		the_window->Show();
237 	}
238 
239 	// Prepare key_info buffer
240 	get_key_info(&old_key_info);
241 }
242 
243 
244 /*
245  *  Display destructor
246  */
247 
~C64Display()248 C64Display::~C64Display()
249 {
250 	if (using_screen) {
251 		the_screen->Lock();
252 		the_screen->Quit();
253 	} else {
254 		the_window->Lock();
255 		the_window->Quit();
256 	}
257 }
258 
259 
260 /*
261  *  Prefs may have changed
262  */
263 
NewPrefs(Prefs * prefs)264 void C64Display::NewPrefs(Prefs *prefs)
265 {
266 	if (prefs->DisplayType == DISPTYPE_SCREEN) {
267 		if (!using_screen) {
268 			// Switch to full screen display
269 			using_screen = true;
270 			the_window->Lock();
271 			the_window->Quit();
272 			the_screen = new C64Screen(this);
273 			the_screen->Show();
274 			while (!the_screen->Connected)
275 				snooze(20000);
276 		}
277 	} else {
278 		if (using_screen) {
279 			// Switch to window display
280 			using_screen = false;
281 			the_screen->Lock();
282 			the_screen->Quit();
283 			the_window = new C64Window();
284 			the_window->Show();
285 		}
286 	}
287 }
288 
289 
290 /*
291  *  Redraw bitmap (let the window thread do it)
292  */
293 
Update(void)294 void C64Display::Update(void)
295 {
296 	if (using_screen) {
297 
298 		// Update LEDs/speedometer
299 		for (int i=0; i<4; i++)
300 			the_screen->DrawLED(i, led_state[i]);
301 		the_screen->DrawSpeedometer();
302 
303 	} else {
304 
305 		// Update C64 display
306 		BMessage msg(MSG_REDRAW);
307 		msg.AddInt32("bitmap", draw_bitmap);
308 		the_window->PostMessage(&msg);
309 		draw_bitmap ^= 1;
310 
311 		// Update LEDs
312 		for (int i=0; i<4; i++)
313 			if (led_state[i] != old_led_state[i]) {
314 				the_window->LED[i]->SetState(led_state[i]);
315 				old_led_state[i] = led_state[i];
316 			}
317 	}
318 }
319 
320 
321 /*
322  *  Set value displayed by the speedometer
323  */
324 
Speedometer(int speed)325 void C64Display::Speedometer(int speed)
326 {
327 	if (using_screen) {
328 		the_screen->Speed = speed;
329 		sprintf(the_screen->SpeedoStr, "%3d%%", speed);
330 	} else
331 		the_window->Speedometer->SetValue(speed);
332 }
333 
334 
335 /*
336  *  Return pointer to bitmap data
337  */
338 
BitmapBase(void)339 uint8 *C64Display::BitmapBase(void)
340 {
341 	if (using_screen)
342 		return (uint8 *)the_screen->CardInfo()->frame_buffer;
343 	else
344 		return (uint8 *)the_window->TheBitmap[draw_bitmap]->Bits();
345 }
346 
347 
348 /*
349  *  Return number of bytes per row
350  */
351 
BitmapXMod(void)352 int C64Display::BitmapXMod(void)
353 {
354 	if (using_screen)
355 		return the_screen->CardInfo()->bytes_per_row;
356 	else
357 		return the_window->TheBitmap[draw_bitmap]->BytesPerRow();
358 }
359 
360 
361 /*
362  *  Poll the keyboard
363  */
364 
PollKeyboard(uint8 * key_matrix,uint8 * rev_matrix,uint8 * joystick)365 void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
366 {
367 	key_info the_key_info;
368 	int be_code, be_byte, be_bit, c64_byte, c64_bit;
369 	bool shifted;
370 
371 	// Window must be active, command key must be up
372 	if (using_screen) {
373 		if (!the_screen->Connected)
374 			return;
375 	} else
376 		if (!the_window->IsActive())
377 			return;
378 	if (!(modifiers() & B_COMMAND_KEY)) {
379 
380 		// Read the state of all keys
381 		get_key_info(&the_key_info);
382 
383 		// Did anything change at all?
384 		if (!memcmp(&old_key_info, &the_key_info, sizeof(key_info)))
385 			return;
386 
387 		// Loop to convert BeOS keymap to C64 keymap
388 		for (be_code=0; be_code<0x68; be_code++) {
389 			be_byte = be_code >> 3;
390 			be_bit = 1 << (~be_code & 7);
391 
392 			// Key state changed?
393 			if ((the_key_info.key_states[be_byte] & be_bit)
394 			     != (old_key_info.key_states[be_byte] & be_bit)) {
395 
396 				c64_byte = key_byte[be_code];
397 				c64_bit = key_bit[be_code];
398 				if (c64_byte != -1) {
399 					if (!(c64_byte & 0x20)) {
400 
401 						// Normal keys
402 						shifted = c64_byte & 8;
403 						c64_byte &= 7;
404 						if (the_key_info.key_states[be_byte] & be_bit) {
405 
406 							// Key pressed
407 							if (shifted) {
408 								key_matrix[6] &= 0xef;
409 								rev_matrix[4] &= 0xbf;
410 							}
411 							key_matrix[c64_byte] &= ~(1 << c64_bit);
412 							rev_matrix[c64_bit] &= ~(1 << c64_byte);
413 						} else {
414 
415 							// Key released
416 							if (shifted) {
417 								key_matrix[6] |= 0x10;
418 								rev_matrix[4] |= 0x40;
419 							}
420 							key_matrix[c64_byte] |= (1 << c64_bit);
421 							rev_matrix[c64_bit] |= (1 << c64_byte);
422 						}
423 					} else {
424 
425 						// Joystick emulation
426 						c64_byte &= 0x1f;
427 						if (the_key_info.key_states[be_byte] & be_bit)
428 							*joystick &= ~c64_byte;
429 						else
430 							*joystick |= c64_byte;
431 					}
432 				}
433 			}
434 		}
435 
436 		old_key_info = the_key_info;
437 	}
438 }
439 
440 
441 /*
442  *  Check if NumLock is down (for switching the joystick keyboard emulation)
443  */
444 
NumLock(void)445 bool C64Display::NumLock(void)
446 {
447 	return modifiers() & B_NUM_LOCK;
448 }
449 
450 
451 /*
452  *  Allocate C64 colors
453  */
454 
InitColors(uint8 * colors)455 void C64Display::InitColors(uint8 *colors)
456 {
457 	BScreen scr(using_screen ? (BWindow *)the_screen : the_window);
458 	for (int i=0; i<256; i++)
459 		colors[i] = scr.IndexForColor(palette_red[i & 0x0f], palette_green[i & 0x0f], palette_blue[i & 0x0f]);
460 }
461 
462 
463 /*
464  *  Pause display (GameKit only)
465  */
466 
Pause(void)467 void C64Display::Pause(void)
468 {
469 	if (using_screen)
470 		the_screen->Hide();
471 }
472 
473 
474 /*
475  *  Resume display (GameKit only)
476  */
477 
Resume(void)478 void C64Display::Resume(void)
479 {
480 	if (using_screen)
481 		the_screen->Show();
482 }
483 
484 
485 /*
486  *  Window constructor
487  */
488 
C64Window()489 C64Window::C64Window() : BWindow(WindowFrame, "Frodo", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE)
490 {
491 	// Move window to right position
492 	Lock();
493 	MoveTo(80, 60);
494 
495 	// Set up menus
496 	BMenuBar *bar = new BMenuBar(Bounds(), "");
497 	BMenu *menu = new BMenu("Frodo");
498 	menu->AddItem(new BMenuItem("About Frodo" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)));
499 	menu->AddItem(new BSeparatorItem);
500 	menu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MSG_PREFS), 'P'));
501 	menu->AddItem(new BSeparatorItem);
502 	menu->AddItem(new BMenuItem("Reset C64", new BMessage(MSG_RESET)));
503 	menu->AddItem(new BMenuItem("Insert next disk", new BMessage(MSG_NEXTDISK), 'D'));
504 	menu->AddItem(new BMenuItem("SAM" B_UTF8_ELLIPSIS, new BMessage(MSG_SAM), 'M'));
505 	menu->AddItem(new BSeparatorItem);
506 	menu->AddItem(new BMenuItem("Load snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_OPEN_SNAPSHOT), 'O'));
507 	menu->AddItem(new BMenuItem("Save snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_SAVE_SNAPSHOT), 'S'));
508 	menu->AddItem(new BSeparatorItem);
509 	menu->AddItem(new BMenuItem("Quit Frodo", new BMessage(B_QUIT_REQUESTED), 'Q'));
510 	menu->SetTargetForItems(be_app);
511 	bar->AddItem(menu);
512 	AddChild(bar);
513 	SetKeyMenuBar(bar);
514 	int mbar_height = bar->Frame().bottom + 1;
515 
516 	// Resize window to fit menu bar
517 	ResizeBy(0, mbar_height);
518 
519 	// Allocate bitmaps
520 	TheBitmap[0] = new BBitmap(DisplayFrame, B_COLOR_8_BIT);
521 	TheBitmap[1] = new BBitmap(DisplayFrame, B_COLOR_8_BIT);
522 
523 	// Create top view
524 	BRect b = Bounds();
525 	BView *top = new BView(BRect(0, mbar_height, b.right, b.bottom), "top", B_FOLLOW_NONE, 0);
526 	AddChild(top);
527 
528 	// Create bitmap view
529 	main_view = new BitmapView(DisplayFrame, TheBitmap[0]);
530 	top->AddChild(main_view);
531 	main_view->MakeFocus();
532 
533 	// Create speedometer
534 	Speedometer = new SpeedoView(BRect(0, DISPLAY_Y, DISPLAY_X/5-1, DISPLAY_Y+15));
535 	top->AddChild(Speedometer);
536 
537 	// Create drive LEDs
538 	LED[0] = new LEDView(BRect(DISPLAY_X/5, DISPLAY_Y, DISPLAY_X*2/5-1, DISPLAY_Y+15), "Drive 8");
539 	top->AddChild(LED[0]);
540 	LED[1] = new LEDView(BRect(DISPLAY_X*2/5, DISPLAY_Y, DISPLAY_X*3/5-1, DISPLAY_Y+15), "Drive 9");
541 	top->AddChild(LED[1]);
542 	LED[2] = new LEDView(BRect(DISPLAY_X*3/5, DISPLAY_Y, DISPLAY_X*4/5-1, DISPLAY_Y+15), "Drive 10");
543 	top->AddChild(LED[2]);
544 	LED[3] = new LEDView(BRect(DISPLAY_X*4/5, DISPLAY_Y, DISPLAY_X-1, DISPLAY_Y+15), "Drive 11");
545 	top->AddChild(LED[3]);
546 
547 	// Set pulse rate to 0.4 seconds for blinking drive LEDs
548 	SetPulseRate(400000);
549 	Unlock();
550 }
551 
552 
553 /*
554  *  Closing the window quits Frodo
555  */
556 
QuitRequested(void)557 bool C64Window::QuitRequested(void)
558 {
559 	be_app->PostMessage(B_QUIT_REQUESTED);
560 	return false;
561 }
562 
563 
564 /*
565  *  Handles redraw messages
566  */
567 
MessageReceived(BMessage * msg)568 void C64Window::MessageReceived(BMessage *msg)
569 {
570 	BMessage *msg2;
571 
572 	switch (msg->what) {
573 		case MSG_REDRAW:  // Redraw bitmap
574 			MessageQueue()->Lock();
575 			while ((msg2 = MessageQueue()->FindMessage(MSG_REDRAW, 0)) != NULL)
576 				MessageQueue()->RemoveMessage(msg2);
577 			MessageQueue()->Unlock();
578 			main_view->ChangeBitmap(TheBitmap[msg->FindInt32("bitmap")]);
579 			Lock();
580 			main_view->Draw(DisplayFrame);
581 			Unlock();
582 			break;
583 
584 		default:
585 			BWindow::MessageReceived(msg);
586 	}
587 }
588 
589 
590 /*
591  *  Workspace activated/deactivated
592  */
593 
ScreenConnected(bool active)594 void C64Screen::ScreenConnected(bool active)
595 {
596 	if (active) {
597 		FillRect(0, 0, 639, 479, 0);	// Clear screen
598 		the_display->TheC64->Resume();
599 		Connected = true;
600 	} else {
601 		the_display->TheC64->Pause();
602 		Connected = false;
603 	}
604 	BWindowScreen::ScreenConnected(active);
605 }
606 
607 
608 /*
609  *  Simulate menu commands
610  */
611 
DispatchMessage(BMessage * msg,BHandler * handler)612 void C64Screen::DispatchMessage(BMessage *msg, BHandler *handler)
613 {
614 	switch (msg->what) {
615 		case B_KEY_DOWN: {
616 			uint32 mods = msg->FindInt32("modifiers");
617 			if (mods & B_COMMAND_KEY) {
618 				uint32 key = msg->FindInt32("raw_char");
619 				switch (key) {
620 					case 'p':
621 						be_app->PostMessage(MSG_PREFS);
622 						break;
623 					case 'd':
624 						be_app->PostMessage(MSG_NEXTDISK);
625 						break;
626 					case 'm':
627 						be_app->PostMessage(MSG_SAM);
628 						break;
629 				}
630 			}
631 			BWindowScreen::DispatchMessage(msg, handler);
632 			break;
633 		}
634 
635 		default:
636 			BWindowScreen::DispatchMessage(msg, handler);
637 	}
638 }
639 
640 
641 /*
642  *  Draw drive LEDs
643  */
644 
DrawLED(int i,int state)645 void C64Screen::DrawLED(int i, int state)
646 {
647 	switch (state) {
648 		case LED_ON:
649 			FillRect(10+i*20, DISPLAY_Y-20, 20+i*20, DISPLAY_Y-12, 54);
650 			break;
651 		case LED_ERROR_ON:
652 			FillRect(10+i*20, DISPLAY_Y-20, 20+i*20, DISPLAY_Y-12, 44);
653 			break;
654 	}
655 }
656 
657 
658 /*
659  *  Draw speedometer
660  */
661 
662 static const int8 Digits[11][8] = {	// Digit images
663 	{0x3c, 0x66, 0x6e, 0x76, 0x66, 0x66, 0x3c, 0x00},
664 	{0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00},
665 	{0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00},
666 	{0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00},
667 	{0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00},
668 	{0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00},
669 	{0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00},
670 	{0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00},
671 	{0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00},
672 	{0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00},
673 	{0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00},
674 };
675 
DrawSpeedometer()676 void C64Screen::DrawSpeedometer()
677 {
678 	// Don't display speedometer if we're running at about 100%
679 	if (Speed >= 99 && Speed <= 101)
680 		return;
681 
682 	char *s = SpeedoStr;
683 	char c;
684 	long xmod = CardInfo()->bytes_per_row;
685 	uint8 *p = (uint8 *)CardInfo()->frame_buffer + DISPLAY_X - 8*8 + (DISPLAY_Y-20) * xmod;
686 	while (c = *s++) {
687 		if (c == ' ')
688 			continue;
689 		if (c == '%')
690 			c = 10;
691 		else
692 			c -= '0';
693 		uint8 *q = p;
694 		for (int y=0; y<8; y++) {
695 			uint8 data = Digits[c][y];
696 			for (int x=0; x<8; x++) {
697 				if (data & (1 << (7-x)))
698 					q[x] = 255;
699 				else
700 					q[x] = 0;
701 			}
702 			q += xmod;
703 		}
704 		p += 8;
705 	}
706 }
707 
708 
709 /*
710  *  Fill rectangle
711  */
712 
FillRect(int x1,int y1,int x2,int y2,int color)713 void C64Screen::FillRect(int x1, int y1, int x2, int y2, int color)
714 {
715 	long xmod = CardInfo()->bytes_per_row;
716 	uint8 *p = (uint8 *)CardInfo()->frame_buffer + y1 * xmod + x1;
717 	int n = x2 - x1 + 1;
718 	for(int y=y1; y<=y2; y++) {
719 		memset_nc(p, color, n);
720 		p += xmod;
721 	}
722 }
723 
724 
725 /*
726  *  Bitmap view constructor
727  */
728 
BitmapView(BRect frame,BBitmap * bitmap)729 BitmapView::BitmapView(BRect frame, BBitmap *bitmap) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW)
730 {
731 	ChangeBitmap(bitmap);
732 }
733 
734 
735 /*
736  *  Blit the bitmap
737  */
738 
Draw(BRect update)739 void BitmapView::Draw(BRect update)
740 {
741 	if (the_bitmap != NULL)
742 		DrawBitmapAsync(the_bitmap, update, update);
743 }
744 
745 
746 /*
747  *  Receive special key-down events (main C64 keyboard handling is done in PollKeyboard)
748  */
749 
KeyDown(const char * bytes,int32 numBytes)750 void BitmapView::KeyDown(const char *bytes, int32 numBytes)
751 {
752 	if (bytes[0] == B_FUNCTION_KEY || bytes[0] == '+' || bytes[0] == '-' || bytes[0] == '*' || bytes[0] == '/') {
753 		BMessage *msg = Window()->CurrentMessage();
754 		long key;
755 		if (msg->FindInt32("key", &key) == B_NO_ERROR) {
756 			switch (key) {
757 
758 				case B_F11_KEY:	// F11: NMI (Restore)
759 					be_app->PostMessage(MSG_NMI);
760 					break;
761 
762 				case B_F12_KEY:	// F12: Reset
763 					be_app->PostMessage(MSG_RESET);
764 					break;
765 
766 				case 0x3a:		// '+' on keypad: Increase SkipFrames
767 					ThePrefs.SkipFrames++;
768 					break;
769 
770 				case 0x25:		// '-' on keypad: Decrease SkipFrames
771 					if (ThePrefs.SkipFrames > 1)
772 						ThePrefs.SkipFrames--;
773 					break;
774 
775 				case 0x24:		// '*' on keypad: Toggle speed limiter
776 					ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed;
777 					break;
778 
779 				case 0x23:		// '/' on keypad: Toggle processor-level 1541 emulation
780 					be_app->PostMessage(MSG_TOGGLE_1541);
781 					break;
782 			}
783 		}
784 	}
785 }
786 
787 
788 /*
789  *  Change view bitmap
790  */
791 
ChangeBitmap(BBitmap * bitmap)792 void BitmapView::ChangeBitmap(BBitmap *bitmap)
793 {
794 	the_bitmap = bitmap;
795 }
796 
797 
798 /*
799  *  Speedometer constructor
800  */
801 
SpeedoView(BRect frame)802 SpeedoView::SpeedoView(BRect frame) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED)
803 {
804 	speedostr[0] = 0;
805 	bounds = Bounds();
806 	SetViewColor(fill_gray);
807 	SetFont(be_plain_font);
808 }
809 
810 
811 /*
812  *  Draw speedometer
813  */
814 
Draw(BRect update)815 void SpeedoView::Draw(BRect update)
816 {
817 	// Draw bevelled border
818 	SetHighColor(shine_gray);
819 	StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0));
820 	StrokeLine(BPoint(bounds.right, 0));
821 	SetHighColor(shadow_gray);
822 	StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1));
823 
824 	// Draw text
825 	SetHighColor(0, 0, 0);
826 	DrawString(speedostr, BPoint(24, 12));
827 }
828 
829 
830 /*
831  *  Update speedometer at regular intervals
832  */
833 
Pulse(void)834 void SpeedoView::Pulse(void)
835 {
836 	Invalidate(BRect(1, 1, bounds.right-1, 15));
837 }
838 
839 
840 /*
841  *  Set new speedometer value
842  */
843 
SetValue(int speed)844 void SpeedoView::SetValue(int speed)
845 {
846 	sprintf(speedostr, "%d%%", speed);
847 }
848 
849 
850 /*
851  *  LED view constructor
852  */
853 
LEDView(BRect frame,const char * label)854 LEDView::LEDView(BRect frame, const char *label) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED)
855 {
856 	current_state = 0;
857 	the_label = label;
858 	bounds = Bounds();
859 	SetViewColor(fill_gray);
860 	SetFont(be_plain_font);
861 }
862 
863 
864 /*
865  *  Draw drive LED
866  */
867 
Draw(BRect update)868 void LEDView::Draw(BRect update)
869 {
870 	// Draw bevelled border
871 	SetHighColor(shine_gray);
872 	StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0));
873 	StrokeLine(BPoint(bounds.right, 0));
874 	SetHighColor(shadow_gray);
875 	StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1));
876 
877 	// Draw label
878 	SetHighColor(0, 0, 0);
879 	SetLowColor(fill_gray);
880 	DrawString(the_label, BPoint(8, 12));
881 
882 	// Draw LED
883 	SetHighColor(shadow_gray);
884 	StrokeLine(BPoint(bounds.right-24, 12), BPoint(bounds.right-24, 4));
885 	StrokeLine(BPoint(bounds.right-8, 4));
886 	SetHighColor(shine_gray);
887 	StrokeLine(BPoint(bounds.right-23, 12), BPoint(bounds.right-8, 12));
888 	StrokeLine(BPoint(bounds.right-8, 5));
889 	DrawLED();
890 }
891 
892 
893 /*
894  *  Redraw just the LED
895  */
896 
DrawLED(void)897 void LEDView::DrawLED(void)
898 {
899 	Window()->Lock();
900 	switch (current_state) {
901 		case LED_OFF:
902 		case LED_ERROR_OFF:
903 			SetHighColor(32, 32, 32);
904 			break;
905 		case LED_ON:
906 			SetHighColor(0, 240, 0);
907 			break;
908 		case LED_ERROR_ON:
909 			SetHighColor(240, 0, 0);
910 			break;
911 	}
912 	FillRect(BRect(bounds.right-23, 5, bounds.right-9, 11));
913 	Window()->Unlock();
914 }
915 
916 
917 /*
918  *  Set LED state
919  */
920 
SetState(int state)921 void LEDView::SetState(int state)
922 {
923 	if (state != current_state) {
924 		current_state = state;
925 		DrawLED();
926 	}
927 }
928 
929 
930 /*
931  *  Toggle red error LED
932  */
933 
Pulse(void)934 void LEDView::Pulse(void)
935 {
936 	switch (current_state) {
937 		case LED_ERROR_ON:
938 			current_state = LED_ERROR_OFF;
939 			DrawLED();
940 			break;
941 		case LED_ERROR_OFF:
942 			current_state = LED_ERROR_ON;
943 			DrawLED();
944 			break;
945 	}
946 }
947 
948 
949 /*
950  *  Show a requester
951  */
952 
ShowRequester(char * str,char * button1,char * button2)953 long ShowRequester(char *str, char *button1, char *button2)
954 {
955 	BAlert *the_alert;
956 
957 	the_alert = new BAlert("", str, button1, button2, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
958 	return the_alert->Go();
959 }
960