mirror of
https://github.com/Oibaf66/frodo-wii.git
synced 2024-11-29 06:54:22 +01:00
1182 lines
25 KiB
C++
1182 lines
25 KiB
C++
/*
|
|
* Display_Be.h - C64 graphics display, emulator window handling,
|
|
* Be specific stuff
|
|
*
|
|
* Frodo (C) 1994-1997,2002-2009 Christian Bauer
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <AppKit.h>
|
|
#include <InterfaceKit.h>
|
|
#include <GameKit.h>
|
|
#include <string.h>
|
|
|
|
#include "C64.h"
|
|
#include "main.h"
|
|
|
|
|
|
#ifndef BIT_BANG
|
|
#define BIT_BANG 0
|
|
#endif
|
|
#ifndef MGA_HACK
|
|
#define MGA_HACK 0
|
|
#endif
|
|
|
|
|
|
// Window thread messages
|
|
const uint32 MSG_REDRAW = 1;
|
|
|
|
|
|
// C64 display and window frame
|
|
const BRect DisplayFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1);
|
|
const BRect WindowFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1 + 16);
|
|
|
|
|
|
// Background color
|
|
const rgb_color fill_gray = {208, 208, 208, 0};
|
|
const rgb_color shine_gray = {232, 232, 232, 0};
|
|
const rgb_color shadow_gray = {152, 152, 152, 0};
|
|
|
|
|
|
/*
|
|
C64 keyboard matrix:
|
|
|
|
Bit 7 6 5 4 3 2 1 0
|
|
0 CUD F5 F3 F1 F7 CLR RET DEL
|
|
1 SHL E S Z 4 A W 3
|
|
2 X T F C 6 D R 5
|
|
3 V U H B 8 G Y 7
|
|
4 N O K M 0 J I 9
|
|
5 , @ : . - L P +
|
|
6 / ^ = SHR HOM ; * £
|
|
7 R/S Q C= SPC 2 CTL <- 1
|
|
*/
|
|
|
|
|
|
/*
|
|
Tables for key translation
|
|
Bit 0..2: row/column in C64 keyboard matrix
|
|
Bit 3 : implicit shift
|
|
Bit 5 : joystick emulation (bit 0..4: mask)
|
|
*/
|
|
|
|
const int key_byte[128] = {
|
|
-1, 7, 0,8+0, 0,8+0, 0, 8+0,
|
|
0, 8+0, -1, -1, -1, -1, -1, -1,
|
|
|
|
7, 7, 7, 7, 1, 1, 2, 2,
|
|
3, 3, 4, 4, 5, 5, 0, 8+0,
|
|
|
|
6, 6, -1, -1, -1, -1, -1, 7,
|
|
1, 1, 2, 2, 3, 3, 4, 4,
|
|
|
|
5, 5, 6, 6, 0, 6, 6,0x25,
|
|
0x21,0x29, -1, 1, 1, 1, 2, 2,
|
|
|
|
3, 3, 4, 4, 5, 5, 6, 0,
|
|
0x24,0x30,0x28, 1, 1, 2, 2, 3,
|
|
|
|
3, 4, 4, 5, 5, 6, 6, 8+0,
|
|
0x26,0x22,0x2a, 0, 7, -1, 7, -1,
|
|
|
|
0x30, 8+0, 0, 0,0x30, -1, 7, 7,
|
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
|
|
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
const int key_bit[128] = {
|
|
-1, 7, 4, 4, 5, 5, 6, 6,
|
|
3, 3, -1, -1, -1, -1, -1, -1,
|
|
|
|
7, 1, 0, 3, 0, 3, 0, 3,
|
|
0, 3, 0, 3, 0, 3, 0, 0,
|
|
|
|
3, 0, -1, -1, -1, -1, -1, 6,
|
|
1, 6, 1, 6, 1, 6, 1, 6,
|
|
|
|
1, 6, 1, 6, 0, 0, 5, -1,
|
|
-1, -1, -1, 7, 2, 5, 2, 5,
|
|
|
|
2, 5, 2, 5, 2, 5, 2, 1,
|
|
-1, -1, -1, 7, 4, 7, 4, 7,
|
|
|
|
4, 7, 4, 7, 4, 7, 4, 7,
|
|
-1, -1, -1, 1, 2, -1, 4, -1,
|
|
|
|
5, 2, 7, 2, -1, -1, 5, 5,
|
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
|
|
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
|
|
/*
|
|
* A simple view class for blitting a bitmap on the screen
|
|
*/
|
|
|
|
class BitmapView : public BView {
|
|
public:
|
|
BitmapView(BRect frame, BBitmap *bitmap);
|
|
virtual void Draw(BRect update);
|
|
virtual void KeyDown(const char *bytes, int32 numBytes);
|
|
void ChangeBitmap(BBitmap *bitmap);
|
|
|
|
private:
|
|
BBitmap *the_bitmap;
|
|
};
|
|
|
|
|
|
/*
|
|
* Class for the main C64 display window
|
|
*/
|
|
|
|
class SpeedoView;
|
|
class LEDView;
|
|
|
|
class C64Window : public BDirectWindow {
|
|
public:
|
|
C64Window();
|
|
|
|
virtual bool QuitRequested(void);
|
|
virtual void MessageReceived(BMessage *msg);
|
|
virtual void DirectConnected(direct_buffer_info *info);
|
|
|
|
BBitmap *TheBitmap[2];
|
|
SpeedoView *Speedometer;
|
|
LEDView *LED[4];
|
|
|
|
#if BIT_BANG
|
|
uint8 *bits;
|
|
int bytes_per_row;
|
|
#endif
|
|
|
|
private:
|
|
BitmapView *main_view;
|
|
};
|
|
|
|
|
|
/*
|
|
* Class for the main C64 display using the GameKit
|
|
*/
|
|
|
|
class C64Screen : public BWindowScreen {
|
|
public:
|
|
C64Screen(C64Display *display) : BWindowScreen("Frodo", B_8_BIT_640x480, &error), the_display(display)
|
|
{
|
|
Lock();
|
|
TheBitmap = new BBitmap(DisplayFrame, B_COLOR_8_BIT);
|
|
main_view = new BitmapView(Bounds(), TheBitmap);
|
|
AddChild(main_view);
|
|
main_view->MakeFocus();
|
|
Connected = false;
|
|
first_connected = true;
|
|
#if MGA_HACK
|
|
mga_ready = false;
|
|
#endif
|
|
Unlock();
|
|
}
|
|
~C64Screen()
|
|
{
|
|
delete TheBitmap;
|
|
}
|
|
|
|
virtual void ScreenConnected(bool active);
|
|
virtual void DispatchMessage(BMessage *msg, BHandler *handler);
|
|
void DrawLED(int i, int state);
|
|
void DrawSpeedometer(void);
|
|
void FillRect(int x1, int y1, int x2, int y2, int color);
|
|
|
|
bool Connected; // Flag: screen connected
|
|
int Speed;
|
|
char SpeedoStr[16]; // Speedometer value converted to a string
|
|
BBitmap *TheBitmap;
|
|
|
|
private:
|
|
C64Display *the_display;
|
|
BitmapView *main_view;
|
|
bool first_connected;
|
|
status_t error;
|
|
|
|
#if MGA_HACK
|
|
area_id mga_clone_area;
|
|
volatile uint8 *isa_io;
|
|
bool mga_ready;
|
|
|
|
void CRTC_out(int reg, uint8 val) {isa_io[0x3d4] = reg; __eieio(); isa_io[0x3d5] = val; __eieio();}
|
|
uint8 CRTC_in(int reg) {isa_io[0x3d4] = reg; __eieio(); return isa_io[0x3d5];}
|
|
void SEQ_out(int reg, uint8 val) {isa_io[0x3c4] = reg; __eieio(); isa_io[0x3c5] = val; __eieio();}
|
|
uint8 SEQ_in(int reg) {isa_io[0x3c4] = reg; __eieio(); return isa_io[0x3c5];}
|
|
void GDC_out(int reg, uint8 val) {isa_io[0x3ce] = reg; __eieio(); isa_io[0x3cf] = val; __eieio();}
|
|
uint8 GDC_in(int reg) {isa_io[0x3ce] = reg; __eieio(); return isa_io[0x3cf];}
|
|
void ATC_out(int reg, uint8 val) {isa_io[0x3c0] = reg; __eieio(); isa_io[0x3c0] = val; __eieio();}
|
|
#endif
|
|
};
|
|
|
|
|
|
/*
|
|
* Class for speedometer
|
|
*/
|
|
|
|
class SpeedoView : public BView {
|
|
public:
|
|
SpeedoView(BRect frame);
|
|
virtual void Draw(BRect update);
|
|
virtual void Pulse(void);
|
|
void SetValue(int percent);
|
|
|
|
private:
|
|
char speedostr[16]; // Speedometer value converted to a string
|
|
BRect bounds;
|
|
};
|
|
|
|
|
|
/*
|
|
* Class for drive LED
|
|
*/
|
|
|
|
class LEDView : public BView {
|
|
public:
|
|
LEDView(BRect frame, const char *label);
|
|
virtual void Draw(BRect update);
|
|
virtual void Pulse(void);
|
|
void DrawLED(void);
|
|
void SetState(int state);
|
|
|
|
private:
|
|
int current_state;
|
|
const char *the_label;
|
|
BRect bounds;
|
|
};
|
|
|
|
|
|
/*
|
|
* Display constructor: Create window/screen
|
|
*/
|
|
|
|
C64Display::C64Display(C64 *the_c64) : TheC64(the_c64)
|
|
{
|
|
// LEDs off
|
|
for (int i=0; i<4; i++)
|
|
led_state[i] = old_led_state[i] = LED_OFF;
|
|
|
|
// Open window/screen
|
|
draw_bitmap = 1;
|
|
if (ThePrefs.DisplayType == DISPTYPE_SCREEN) {
|
|
using_screen = true;
|
|
the_screen = new C64Screen(this);
|
|
the_screen->Show();
|
|
while (!the_screen->Connected)
|
|
snooze(20000);
|
|
} else {
|
|
using_screen = false;
|
|
the_window = new C64Window();
|
|
the_window->Show();
|
|
}
|
|
|
|
// Prepare key_info buffer
|
|
get_key_info(&old_key_info);
|
|
}
|
|
|
|
|
|
/*
|
|
* Display destructor
|
|
*/
|
|
|
|
C64Display::~C64Display()
|
|
{
|
|
if (using_screen) {
|
|
the_screen->Lock();
|
|
the_screen->Quit();
|
|
} else {
|
|
the_window->Lock();
|
|
the_window->Quit();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Prefs may have changed
|
|
*/
|
|
|
|
void C64Display::NewPrefs(Prefs *prefs)
|
|
{
|
|
if (prefs->DisplayType == DISPTYPE_SCREEN) {
|
|
if (!using_screen) {
|
|
// Switch to full screen display
|
|
using_screen = true;
|
|
the_window->Lock();
|
|
the_window->Quit();
|
|
the_screen = new C64Screen(this);
|
|
the_screen->Show();
|
|
while (!the_screen->Connected)
|
|
snooze(20000);
|
|
}
|
|
} else {
|
|
if (using_screen) {
|
|
// Switch to window display
|
|
using_screen = false;
|
|
the_screen->Lock();
|
|
the_screen->Quit();
|
|
the_window = new C64Window();
|
|
the_window->Show();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Redraw bitmap (let the window thread do it)
|
|
*/
|
|
|
|
void C64Display::Update(void)
|
|
{
|
|
if (using_screen) {
|
|
|
|
// Update LEDs/speedometer
|
|
for (int i=0; i<4; i++)
|
|
the_screen->DrawLED(i, led_state[i]);
|
|
the_screen->DrawSpeedometer();
|
|
|
|
// Update C64 display in dobule scan mode
|
|
if (ThePrefs.DoubleScan) {
|
|
uint8 *src = (uint8 *)the_screen->TheBitmap->Bits();
|
|
uint32 src_xmod = the_screen->TheBitmap->BytesPerRow();
|
|
src += src_xmod * 16 + 32;
|
|
uint8 *dest = (uint8 *)the_screen->CardInfo()->frame_buffer;
|
|
uint32 dest_xmod = the_screen->CardInfo()->bytes_per_row;
|
|
#ifdef __POWERPC__
|
|
double tmp[1];
|
|
for (int y=0; y<240; y++) {
|
|
uint32 *p = (uint32 *)src - 1;
|
|
double *q1 = (double *)dest - 1;
|
|
double *q2 = q1 + dest_xmod / sizeof(double);
|
|
for (int x=0; x<80; x++) {
|
|
uint32 val = *(++p);
|
|
uint8 *r = (uint8 *)&tmp[1];
|
|
*(--r) = val;
|
|
*(--r) = val;
|
|
val >>= 8;
|
|
*(--r) = val;
|
|
*(--r) = val;
|
|
val >>= 8;
|
|
*(--r) = val;
|
|
*(--r) = val;
|
|
val >>= 8;
|
|
*(--r) = val;
|
|
*(--r) = val;
|
|
double tmp2 = tmp[0];
|
|
*(++q1) = tmp2;
|
|
*(++q2) = tmp2;
|
|
}
|
|
src += src_xmod;
|
|
dest += dest_xmod * 2;
|
|
}
|
|
#else
|
|
for (int y=0; y<240; y++) {
|
|
uint32 *p = (uint32 *)src;
|
|
uint32 *q1 = (uint32 *)dest;
|
|
uint32 *q2 = q1 + dest_xmod / sizeof(uint32);
|
|
for (int x=0; x<80; x++) {
|
|
uint32 val = *p++;
|
|
uint32 tmp = val & 0x000000ff;
|
|
tmp |= (val << 8) & 0x0000ff00;
|
|
tmp |= (val << 8) & 0x00ff0000;
|
|
tmp |= (val << 16) & 0xff000000;
|
|
*q1++ = tmp;
|
|
*q2++ = tmp;
|
|
tmp = (val >> 16) & 0x000000ff;
|
|
tmp |= (val >> 8) & 0x0000ff00;
|
|
tmp |= (val >> 8) & 0x00ff0000;
|
|
tmp |= val & 0xff000000;
|
|
*q1++ = tmp;
|
|
*q2++ = tmp;
|
|
}
|
|
src += src_xmod;
|
|
dest += dest_xmod * 2;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
|
|
#if !BIT_BANG
|
|
// Update C64 display
|
|
BMessage msg(MSG_REDRAW);
|
|
msg.AddInt32("bitmap", draw_bitmap);
|
|
the_window->PostMessage(&msg);
|
|
draw_bitmap ^= 1;
|
|
#endif
|
|
|
|
// Update LEDs
|
|
for (int i=0; i<4; i++)
|
|
if (led_state[i] != old_led_state[i]) {
|
|
the_window->LED[i]->SetState(led_state[i]);
|
|
old_led_state[i] = led_state[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set value displayed by the speedometer
|
|
*/
|
|
|
|
void C64Display::Speedometer(int speed)
|
|
{
|
|
if (using_screen) {
|
|
the_screen->Speed = speed;
|
|
sprintf(the_screen->SpeedoStr, "%3d%%", speed);
|
|
} else
|
|
the_window->Speedometer->SetValue(speed);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return pointer to bitmap data
|
|
*/
|
|
|
|
uint8 *C64Display::BitmapBase(void)
|
|
{
|
|
if (using_screen) {
|
|
if (ThePrefs.DoubleScan)
|
|
return (uint8 *)the_screen->TheBitmap->Bits();
|
|
else
|
|
return (uint8 *)the_screen->CardInfo()->frame_buffer;
|
|
} else
|
|
#if BIT_BANG
|
|
return (uint8 *)the_window->bits;
|
|
#else
|
|
return (uint8 *)the_window->TheBitmap[draw_bitmap]->Bits();
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Return number of bytes per row
|
|
*/
|
|
|
|
int C64Display::BitmapXMod(void)
|
|
{
|
|
if (using_screen) {
|
|
if (ThePrefs.DoubleScan)
|
|
return the_screen->TheBitmap->BytesPerRow();
|
|
else
|
|
return the_screen->CardInfo()->bytes_per_row;
|
|
} else
|
|
#if BIT_BANG
|
|
return the_window->bytes_per_row;
|
|
#else
|
|
return the_window->TheBitmap[draw_bitmap]->BytesPerRow();
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Poll the keyboard
|
|
*/
|
|
|
|
void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
|
|
{
|
|
key_info the_key_info;
|
|
int be_code, be_byte, be_bit, c64_byte, c64_bit;
|
|
bool shifted;
|
|
|
|
// Window must be active, command key must be up
|
|
if (using_screen) {
|
|
if (!the_screen->Connected)
|
|
return;
|
|
} else
|
|
if (!the_window->IsActive())
|
|
return;
|
|
if (!(modifiers() & B_COMMAND_KEY)) {
|
|
|
|
// Read the state of all keys
|
|
get_key_info(&the_key_info);
|
|
|
|
// Did anything change at all?
|
|
if (!memcmp(&old_key_info, &the_key_info, sizeof(key_info)))
|
|
return;
|
|
|
|
// Loop to convert BeOS keymap to C64 keymap
|
|
for (be_code=0; be_code<0x68; be_code++) {
|
|
be_byte = be_code >> 3;
|
|
be_bit = 1 << (~be_code & 7);
|
|
|
|
// Key state changed?
|
|
if ((the_key_info.key_states[be_byte] & be_bit)
|
|
!= (old_key_info.key_states[be_byte] & be_bit)) {
|
|
|
|
c64_byte = key_byte[be_code];
|
|
c64_bit = key_bit[be_code];
|
|
if (c64_byte != -1) {
|
|
if (!(c64_byte & 0x20)) {
|
|
|
|
// Normal keys
|
|
shifted = c64_byte & 8;
|
|
c64_byte &= 7;
|
|
if (the_key_info.key_states[be_byte] & be_bit) {
|
|
|
|
// Key pressed
|
|
if (shifted) {
|
|
key_matrix[6] &= 0xef;
|
|
rev_matrix[4] &= 0xbf;
|
|
}
|
|
key_matrix[c64_byte] &= ~(1 << c64_bit);
|
|
rev_matrix[c64_bit] &= ~(1 << c64_byte);
|
|
} else {
|
|
|
|
// Key released
|
|
if (shifted) {
|
|
key_matrix[6] |= 0x10;
|
|
rev_matrix[4] |= 0x40;
|
|
}
|
|
key_matrix[c64_byte] |= (1 << c64_bit);
|
|
rev_matrix[c64_bit] |= (1 << c64_byte);
|
|
}
|
|
} else {
|
|
|
|
// Joystick emulation
|
|
c64_byte &= 0x1f;
|
|
if (the_key_info.key_states[be_byte] & be_bit)
|
|
*joystick &= ~c64_byte;
|
|
else
|
|
*joystick |= c64_byte;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
old_key_info = the_key_info;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if NumLock is down (for switching the joystick keyboard emulation)
|
|
*/
|
|
|
|
bool C64Display::NumLock(void)
|
|
{
|
|
return modifiers() & B_NUM_LOCK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate C64 colors
|
|
*/
|
|
|
|
void C64Display::InitColors(uint8 *colors)
|
|
{
|
|
BScreen scr(using_screen ? (BWindow *)the_screen : the_window);
|
|
for (int i=0; i<256; i++)
|
|
colors[i] = scr.IndexForColor(palette_red[i & 0x0f], palette_green[i & 0x0f], palette_blue[i & 0x0f]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Pause display (GameKit only)
|
|
*/
|
|
|
|
void C64Display::Pause(void)
|
|
{
|
|
if (using_screen)
|
|
the_screen->Hide();
|
|
}
|
|
|
|
|
|
/*
|
|
* Resume display (GameKit only)
|
|
*/
|
|
|
|
void C64Display::Resume(void)
|
|
{
|
|
if (using_screen)
|
|
the_screen->Show();
|
|
}
|
|
|
|
|
|
/*
|
|
* Window constructor
|
|
*/
|
|
|
|
C64Window::C64Window() : BDirectWindow(WindowFrame, "Frodo", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE)
|
|
{
|
|
// Move window to right position
|
|
Lock();
|
|
MoveTo(80, 60);
|
|
|
|
// Set up menus
|
|
BMenuBar *bar = new BMenuBar(Bounds(), "");
|
|
BMenu *menu = new BMenu("Frodo");
|
|
menu->AddItem(new BMenuItem("About Frodo" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)));
|
|
menu->AddItem(new BSeparatorItem);
|
|
menu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MSG_PREFS), 'P'));
|
|
menu->AddItem(new BSeparatorItem);
|
|
menu->AddItem(new BMenuItem("Reset C64", new BMessage(MSG_RESET)));
|
|
menu->AddItem(new BMenuItem("Insert next disk", new BMessage(MSG_NEXTDISK), 'D'));
|
|
menu->AddItem(new BMenuItem("SAM" B_UTF8_ELLIPSIS, new BMessage(MSG_SAM), 'M'));
|
|
menu->AddItem(new BSeparatorItem);
|
|
menu->AddItem(new BMenuItem("Load snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_OPEN_SNAPSHOT), 'O'));
|
|
menu->AddItem(new BMenuItem("Save snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_SAVE_SNAPSHOT), 'S'));
|
|
menu->AddItem(new BSeparatorItem);
|
|
menu->AddItem(new BMenuItem("Quit Frodo", new BMessage(B_QUIT_REQUESTED), 'Q'));
|
|
menu->SetTargetForItems(be_app);
|
|
bar->AddItem(menu);
|
|
AddChild(bar);
|
|
SetKeyMenuBar(bar);
|
|
int mbar_height = int(bar->Frame().bottom) + 1;
|
|
|
|
// Resize window to fit menu bar
|
|
ResizeBy(0, mbar_height);
|
|
|
|
// Allocate bitmaps
|
|
TheBitmap[0] = new BBitmap(DisplayFrame, B_COLOR_8_BIT);
|
|
TheBitmap[1] = new BBitmap(DisplayFrame, B_COLOR_8_BIT);
|
|
|
|
// Create top view
|
|
BRect b = Bounds();
|
|
BView *top = new BView(BRect(0, mbar_height, b.right, b.bottom), "top", B_FOLLOW_NONE, 0);
|
|
AddChild(top);
|
|
|
|
// Create bitmap view
|
|
main_view = new BitmapView(DisplayFrame, TheBitmap[0]);
|
|
top->AddChild(main_view);
|
|
main_view->MakeFocus();
|
|
|
|
// Create speedometer
|
|
Speedometer = new SpeedoView(BRect(0, DISPLAY_Y, DISPLAY_X/5-1, DISPLAY_Y+15));
|
|
top->AddChild(Speedometer);
|
|
|
|
// Create drive LEDs
|
|
LED[0] = new LEDView(BRect(DISPLAY_X/5, DISPLAY_Y, DISPLAY_X*2/5-1, DISPLAY_Y+15), "Drive 8");
|
|
top->AddChild(LED[0]);
|
|
LED[1] = new LEDView(BRect(DISPLAY_X*2/5, DISPLAY_Y, DISPLAY_X*3/5-1, DISPLAY_Y+15), "Drive 9");
|
|
top->AddChild(LED[1]);
|
|
LED[2] = new LEDView(BRect(DISPLAY_X*3/5, DISPLAY_Y, DISPLAY_X*4/5-1, DISPLAY_Y+15), "Drive 10");
|
|
top->AddChild(LED[2]);
|
|
LED[3] = new LEDView(BRect(DISPLAY_X*4/5, DISPLAY_Y, DISPLAY_X-1, DISPLAY_Y+15), "Drive 11");
|
|
top->AddChild(LED[3]);
|
|
|
|
// Set pulse rate to 0.4 seconds for blinking drive LEDs
|
|
SetPulseRate(400000);
|
|
Unlock();
|
|
}
|
|
|
|
|
|
/*
|
|
* Closing the window quits Frodo
|
|
*/
|
|
|
|
bool C64Window::QuitRequested(void)
|
|
{
|
|
be_app->PostMessage(B_QUIT_REQUESTED);
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handles redraw messages
|
|
*/
|
|
|
|
void C64Window::MessageReceived(BMessage *msg)
|
|
{
|
|
BMessage *msg2;
|
|
|
|
switch (msg->what) {
|
|
case MSG_REDRAW: // Redraw bitmap
|
|
MessageQueue()->Lock();
|
|
while ((msg2 = MessageQueue()->FindMessage(MSG_REDRAW, 0)) != NULL) {
|
|
MessageQueue()->RemoveMessage(msg2);
|
|
delete msg2;
|
|
}
|
|
MessageQueue()->Unlock();
|
|
main_view->ChangeBitmap(TheBitmap[msg->FindInt32("bitmap")]);
|
|
Lock();
|
|
main_view->Draw(DisplayFrame);
|
|
Unlock();
|
|
break;
|
|
|
|
default:
|
|
BWindow::MessageReceived(msg);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Window connected/disconnected
|
|
*/
|
|
|
|
void C64Window::DirectConnected(direct_buffer_info *info)
|
|
{
|
|
#if BIT_BANG
|
|
switch (info->buffer_state & B_DIRECT_MODE_MASK) {
|
|
case B_DIRECT_STOP:
|
|
// acquire_sem(drawing_sem);
|
|
break;
|
|
case B_DIRECT_MODIFY:
|
|
// acquire_sem(drawing_sem);
|
|
case B_DIRECT_START:
|
|
bits = ((uint8 *)info->bits + info->window_bounds.top * info->bytes_per_row + info->window_bounds.left * info->bits_per_pixel / 8);
|
|
bytes_per_row = info->bytes_per_row;
|
|
// release_sem(drawing_sem);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Workspace activated/deactivated
|
|
*/
|
|
|
|
void C64Screen::ScreenConnected(bool active)
|
|
{
|
|
if (active) {
|
|
if (first_connected) {
|
|
first_connected = false;
|
|
|
|
#if MGA_HACK
|
|
mga_clone_area = -1;
|
|
|
|
// Construct register area name
|
|
char mga_area_name[64];
|
|
int bus = 0, device = 13, function = 0;
|
|
sprintf(mga_area_name, "102B_0519_%02X%02X%02X regs", bus, device, function);
|
|
|
|
// Find MGA register area
|
|
area_id mga_area = find_area(mga_area_name);
|
|
if (mga_area > 0) {
|
|
|
|
// Clone area, remove write protection
|
|
volatile uint8 *mga_io;
|
|
mga_clone_area = clone_area("mga registers", (void **)&mga_io, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, mga_area);
|
|
if (mga_clone_area > 0) {
|
|
isa_io = mga_io + 0x1c00;
|
|
mga_ready = true;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if MGA_HACK
|
|
if (mga_ready) {
|
|
CRTC_out(0x09, 1); // Enable double scan
|
|
int a = 4 * 640;
|
|
CRTC_out(0x0c, a >> 8); // Center screen vertically
|
|
CRTC_out(0x0d, a);
|
|
// defaults:
|
|
// total 0x67
|
|
// display end 0x4f
|
|
// blank start 0x4f
|
|
// blank end 2b
|
|
// sync start 0x53
|
|
// sync end 1f
|
|
CRTC_out(0x00, 0x3f); // Horizontal timing
|
|
CRTC_out(0x01, 0x2f);
|
|
CRTC_out(0x02, 0x2f);
|
|
CRTC_out(0x03, 0x83);
|
|
CRTC_out(0x04, 0x32);
|
|
CRTC_out(0x05, 0x1a);
|
|
}
|
|
#endif
|
|
|
|
FillRect(0, 0, 639, 479, 0); // Clear screen
|
|
the_display->TheC64->Resume();
|
|
Connected = true;
|
|
} else {
|
|
the_display->TheC64->Pause();
|
|
Connected = false;
|
|
}
|
|
BWindowScreen::ScreenConnected(active);
|
|
}
|
|
|
|
|
|
/*
|
|
* Simulate menu commands
|
|
*/
|
|
|
|
void C64Screen::DispatchMessage(BMessage *msg, BHandler *handler)
|
|
{
|
|
switch (msg->what) {
|
|
case B_KEY_DOWN: {
|
|
uint32 mods = msg->FindInt32("modifiers");
|
|
if (mods & B_COMMAND_KEY) {
|
|
uint32 key = msg->FindInt32("raw_char");
|
|
switch (key) {
|
|
case 'p':
|
|
be_app->PostMessage(MSG_PREFS);
|
|
break;
|
|
case 'd':
|
|
be_app->PostMessage(MSG_NEXTDISK);
|
|
break;
|
|
case 'm':
|
|
be_app->PostMessage(MSG_SAM);
|
|
break;
|
|
}
|
|
}
|
|
BWindowScreen::DispatchMessage(msg, handler);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BWindowScreen::DispatchMessage(msg, handler);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Draw drive LEDs
|
|
*/
|
|
|
|
void C64Screen::DrawLED(int i, int state)
|
|
{
|
|
int maxy;
|
|
if (ThePrefs.DoubleScan)
|
|
maxy = 480;
|
|
else
|
|
maxy = DISPLAY_Y;
|
|
|
|
switch (state) {
|
|
case LED_ON:
|
|
FillRect(10+i*20, maxy-20, 20+i*20, maxy-12, 54);
|
|
break;
|
|
case LED_ERROR_ON:
|
|
FillRect(10+i*20, maxy-20, 20+i*20, maxy-12, 44);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Draw speedometer
|
|
*/
|
|
|
|
static const int8 Digits[11][8] = { // Digit images
|
|
{0x3c, 0x66, 0x6e, 0x76, 0x66, 0x66, 0x3c, 0x00},
|
|
{0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00},
|
|
{0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00},
|
|
{0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00},
|
|
{0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00},
|
|
{0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00},
|
|
{0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00},
|
|
{0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00},
|
|
{0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00},
|
|
{0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00},
|
|
{0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00},
|
|
};
|
|
|
|
void C64Screen::DrawSpeedometer()
|
|
{
|
|
// Don't display speedometer if we're running at about 100%
|
|
if (Speed >= 50 && Speed <= 101)
|
|
return;
|
|
|
|
int maxx, maxy;
|
|
if (ThePrefs.DoubleScan) {
|
|
maxx = 640;
|
|
maxy = 480;
|
|
} else {
|
|
maxx = DISPLAY_X;
|
|
maxy = DISPLAY_Y;
|
|
}
|
|
|
|
char *s = SpeedoStr;
|
|
char c;
|
|
long xmod = CardInfo()->bytes_per_row;
|
|
uint8 *p = (uint8 *)CardInfo()->frame_buffer + maxx - 8*8 + (maxy-20) * xmod;
|
|
while ((c = *s++) != 0) {
|
|
if (c == ' ')
|
|
continue;
|
|
if (c == '%')
|
|
c = 10;
|
|
else
|
|
c -= '0';
|
|
uint8 *q = p;
|
|
for (int y=0; y<8; y++) {
|
|
uint8 data = Digits[c][y];
|
|
for (int x=0; x<8; x++) {
|
|
if (data & (1 << (7-x)))
|
|
q[x] = 255;
|
|
else
|
|
q[x] = 0;
|
|
}
|
|
q += xmod;
|
|
}
|
|
p += 8;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill rectangle
|
|
*/
|
|
|
|
void C64Screen::FillRect(int x1, int y1, int x2, int y2, int color)
|
|
{
|
|
long xmod = CardInfo()->bytes_per_row;
|
|
uint8 *p = (uint8 *)CardInfo()->frame_buffer + y1 * xmod + x1;
|
|
int n = x2 - x1 + 1;
|
|
for(int y=y1; y<=y2; y++) {
|
|
#ifdef __POWERPC__
|
|
memset_nc(p, color, n);
|
|
#else
|
|
memset(p, color, n);
|
|
#endif
|
|
p += xmod;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Bitmap view constructor
|
|
*/
|
|
|
|
BitmapView::BitmapView(BRect frame, BBitmap *bitmap) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW)
|
|
{
|
|
ChangeBitmap(bitmap);
|
|
}
|
|
|
|
|
|
/*
|
|
* Blit the bitmap
|
|
*/
|
|
|
|
void BitmapView::Draw(BRect update)
|
|
{
|
|
if (the_bitmap != NULL)
|
|
DrawBitmapAsync(the_bitmap, update, update);
|
|
}
|
|
|
|
|
|
/*
|
|
* Receive special key-down events (main C64 keyboard handling is done in PollKeyboard)
|
|
*/
|
|
|
|
void BitmapView::KeyDown(const char *bytes, int32 numBytes)
|
|
{
|
|
if (bytes[0] == B_FUNCTION_KEY || bytes[0] == '+' || bytes[0] == '-' || bytes[0] == '*' || bytes[0] == '/') {
|
|
BMessage *msg = Window()->CurrentMessage();
|
|
long key;
|
|
if (msg->FindInt32("key", &key) == B_NO_ERROR) {
|
|
switch (key) {
|
|
|
|
case B_F11_KEY: // F11: NMI (Restore)
|
|
be_app->PostMessage(MSG_NMI);
|
|
break;
|
|
|
|
case B_F12_KEY: // F12: Reset
|
|
be_app->PostMessage(MSG_RESET);
|
|
break;
|
|
|
|
case 0x3a: // '+' on keypad: Increase SkipFrames
|
|
ThePrefs.SkipFrames++;
|
|
break;
|
|
|
|
case 0x25: // '-' on keypad: Decrease SkipFrames
|
|
if (ThePrefs.SkipFrames > 1)
|
|
ThePrefs.SkipFrames--;
|
|
break;
|
|
|
|
case 0x24: // '*' on keypad: Toggle speed limiter
|
|
ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed;
|
|
break;
|
|
|
|
case 0x23: // '/' on keypad: Toggle processor-level 1541 emulation
|
|
be_app->PostMessage(MSG_TOGGLE_1541);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Change view bitmap
|
|
*/
|
|
|
|
void BitmapView::ChangeBitmap(BBitmap *bitmap)
|
|
{
|
|
the_bitmap = bitmap;
|
|
}
|
|
|
|
|
|
/*
|
|
* Speedometer constructor
|
|
*/
|
|
|
|
SpeedoView::SpeedoView(BRect frame) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED)
|
|
{
|
|
speedostr[0] = 0;
|
|
bounds = Bounds();
|
|
SetViewColor(fill_gray);
|
|
SetLowColor(fill_gray);
|
|
SetFont(be_plain_font);
|
|
}
|
|
|
|
|
|
/*
|
|
* Draw speedometer
|
|
*/
|
|
|
|
void SpeedoView::Draw(BRect update)
|
|
{
|
|
// Draw bevelled border
|
|
SetHighColor(shine_gray);
|
|
StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0));
|
|
StrokeLine(BPoint(bounds.right, 0));
|
|
SetHighColor(shadow_gray);
|
|
StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1));
|
|
|
|
// Draw text
|
|
SetHighColor(0, 0, 0);
|
|
DrawString(speedostr, BPoint(24, 12));
|
|
}
|
|
|
|
|
|
/*
|
|
* Update speedometer at regular intervals
|
|
*/
|
|
|
|
void SpeedoView::Pulse(void)
|
|
{
|
|
Invalidate(BRect(1, 1, bounds.right-1, 15));
|
|
}
|
|
|
|
|
|
/*
|
|
* Set new speedometer value
|
|
*/
|
|
|
|
void SpeedoView::SetValue(int speed)
|
|
{
|
|
sprintf(speedostr, "%d%%", speed);
|
|
}
|
|
|
|
|
|
/*
|
|
* LED view constructor
|
|
*/
|
|
|
|
LEDView::LEDView(BRect frame, const char *label) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED)
|
|
{
|
|
current_state = 0;
|
|
the_label = label;
|
|
bounds = Bounds();
|
|
SetViewColor(fill_gray);
|
|
SetFont(be_plain_font);
|
|
}
|
|
|
|
|
|
/*
|
|
* Draw drive LED
|
|
*/
|
|
|
|
void LEDView::Draw(BRect update)
|
|
{
|
|
// Draw bevelled border
|
|
SetHighColor(shine_gray);
|
|
StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0));
|
|
StrokeLine(BPoint(bounds.right, 0));
|
|
SetHighColor(shadow_gray);
|
|
StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1));
|
|
|
|
// Draw label
|
|
SetHighColor(0, 0, 0);
|
|
SetLowColor(fill_gray);
|
|
DrawString(the_label, BPoint(8, 12));
|
|
|
|
// Draw LED
|
|
SetHighColor(shadow_gray);
|
|
StrokeLine(BPoint(bounds.right-24, 12), BPoint(bounds.right-24, 4));
|
|
StrokeLine(BPoint(bounds.right-8, 4));
|
|
SetHighColor(shine_gray);
|
|
StrokeLine(BPoint(bounds.right-23, 12), BPoint(bounds.right-8, 12));
|
|
StrokeLine(BPoint(bounds.right-8, 5));
|
|
DrawLED();
|
|
}
|
|
|
|
|
|
/*
|
|
* Redraw just the LED
|
|
*/
|
|
|
|
void LEDView::DrawLED(void)
|
|
{
|
|
Window()->Lock();
|
|
switch (current_state) {
|
|
case LED_OFF:
|
|
case LED_ERROR_OFF:
|
|
SetHighColor(32, 32, 32);
|
|
break;
|
|
case LED_ON:
|
|
SetHighColor(0, 240, 0);
|
|
break;
|
|
case LED_ERROR_ON:
|
|
SetHighColor(240, 0, 0);
|
|
break;
|
|
}
|
|
FillRect(BRect(bounds.right-23, 5, bounds.right-9, 11));
|
|
Window()->Unlock();
|
|
}
|
|
|
|
|
|
/*
|
|
* Set LED state
|
|
*/
|
|
|
|
void LEDView::SetState(int state)
|
|
{
|
|
if (state != current_state) {
|
|
current_state = state;
|
|
DrawLED();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Toggle red error LED
|
|
*/
|
|
|
|
void LEDView::Pulse(void)
|
|
{
|
|
switch (current_state) {
|
|
case LED_ERROR_ON:
|
|
current_state = LED_ERROR_OFF;
|
|
DrawLED();
|
|
break;
|
|
case LED_ERROR_OFF:
|
|
current_state = LED_ERROR_ON;
|
|
DrawLED();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Show a requester
|
|
*/
|
|
|
|
long ShowRequester(const char *str, const char *button1, const char *button2)
|
|
{
|
|
BAlert *the_alert;
|
|
|
|
the_alert = new BAlert("", str, button1, button2, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
|
return the_alert->Go();
|
|
}
|