2017-05-16 09:07:55 +01:00
|
|
|
#include "StdAfx.h"
|
|
|
|
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
#include "Common.h"
|
|
|
|
#include "Disk.h"
|
|
|
|
#include "Video.h"
|
2017-05-23 17:52:41 +01:00
|
|
|
#include "CPU.h"
|
|
|
|
#include "Log.h"
|
2017-05-16 09:07:55 +01:00
|
|
|
#include "DiskImageHelper.h"
|
|
|
|
#include "Memory.h"
|
|
|
|
|
|
|
|
#include "linux/interface.h"
|
|
|
|
|
2017-05-23 20:55:31 +01:00
|
|
|
#include "frontends/ncurses/nframe.h"
|
2017-05-18 21:53:58 +01:00
|
|
|
#include "frontends/ncurses/colors.h"
|
2017-06-01 21:36:42 +01:00
|
|
|
#include "frontends/ncurses/asciiart.h"
|
2017-06-10 20:01:55 +01:00
|
|
|
#include "frontends/ncurses/input.h"
|
2017-05-18 21:53:58 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
2017-05-20 21:31:17 +01:00
|
|
|
std::shared_ptr<Frame> frame;
|
2017-06-01 20:52:11 +01:00
|
|
|
std::shared_ptr<GraphicsColors> colors;
|
2017-06-01 21:36:42 +01:00
|
|
|
std::shared_ptr<ASCIIArt> asciiArt;
|
2017-06-10 20:01:55 +01:00
|
|
|
std::shared_ptr<Input> input;
|
2017-05-18 21:53:58 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
int g_nTrackDrive1 = -1;
|
|
|
|
int g_nTrackDrive2 = -1;
|
|
|
|
int g_nSectorDrive1 = -1;
|
|
|
|
int g_nSectorDrive2 = -1;
|
|
|
|
TCHAR g_sTrackDrive1 [8] = TEXT("??");
|
|
|
|
TCHAR g_sTrackDrive2 [8] = TEXT("??");
|
|
|
|
TCHAR g_sSectorDrive1[8] = TEXT("??");
|
|
|
|
TCHAR g_sSectorDrive2[8] = TEXT("??");
|
|
|
|
Disk_Status_e g_eStatusDrive1 = DISK_STATUS_OFF;
|
|
|
|
Disk_Status_e g_eStatusDrive2 = DISK_STATUS_OFF;
|
|
|
|
|
|
|
|
LPBYTE g_pTextBank1; // Aux
|
|
|
|
LPBYTE g_pTextBank0; // Main
|
|
|
|
LPBYTE g_pHiresBank1;
|
|
|
|
LPBYTE g_pHiresBank0;
|
|
|
|
|
|
|
|
#define SW_80COL (g_uVideoMode & VF_80COL)
|
|
|
|
#define SW_DHIRES (g_uVideoMode & VF_DHIRES)
|
|
|
|
#define SW_HIRES (g_uVideoMode & VF_HIRES)
|
|
|
|
#define SW_80STORE (g_uVideoMode & VF_80STORE)
|
|
|
|
#define SW_MIXED (g_uVideoMode & VF_MIXED)
|
|
|
|
#define SW_PAGE2 (g_uVideoMode & VF_PAGE2)
|
|
|
|
#define SW_TEXT (g_uVideoMode & VF_TEXT)
|
|
|
|
|
|
|
|
BYTE nextKey = 0;
|
|
|
|
bool keyReady = false;
|
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
bool g_bTextFlashState = false;
|
2017-06-10 20:01:55 +01:00
|
|
|
unsigned __int64 g_nJoyCntrResetCycle = 0; // Abs cycle that joystick counters were reset
|
|
|
|
const double PDL_CNTR_INTERVAL = 2816.0 / 255.0; // 11.04 (From KEGS)
|
2017-05-20 20:37:18 +01:00
|
|
|
|
2017-05-23 17:52:41 +01:00
|
|
|
double alpha = 10.0;
|
|
|
|
double F = 0;
|
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
void sig_handler(int signo)
|
|
|
|
{
|
2017-06-04 19:05:14 +01:00
|
|
|
// Ctrl-C
|
|
|
|
// is there a race condition here?
|
|
|
|
// is it a problem?
|
|
|
|
nextKey = 0x83;
|
|
|
|
keyReady = true;
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
chtype mapCharacter(BYTE ch)
|
|
|
|
{
|
2017-05-20 20:37:18 +01:00
|
|
|
const char low = ch & 0x7f;
|
|
|
|
const char high = ch & 0x80;
|
2017-05-16 09:07:55 +01:00
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
chtype result = low;
|
2017-05-16 09:07:55 +01:00
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
const int code = low >> 5;
|
|
|
|
switch (code)
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-20 20:37:18 +01:00
|
|
|
case 0: // 00 - 1F
|
|
|
|
result += 0x40; // UPPERCASE
|
|
|
|
break;
|
|
|
|
case 1: // 20 - 3F
|
|
|
|
// SPECIAL CHARACTER
|
|
|
|
break;
|
|
|
|
case 2: // 40 - 5F
|
|
|
|
// UPPERCASE
|
|
|
|
break;
|
|
|
|
case 3: // 60 - 7F
|
|
|
|
// LOWERCASE
|
|
|
|
if (high == 0 && g_nAltCharSetOffset == 0)
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-20 20:37:18 +01:00
|
|
|
result -= 0x40;
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
2017-05-20 20:37:18 +01:00
|
|
|
break;
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
if (result == 0x7f)
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-20 20:37:18 +01:00
|
|
|
result = ACS_CKBOARD;
|
|
|
|
}
|
|
|
|
|
|
|
|
result != A_BLINK;
|
|
|
|
|
|
|
|
if (!high)
|
|
|
|
{
|
|
|
|
if ((g_nAltCharSetOffset == 0) && (low >= 0x40))
|
|
|
|
{
|
|
|
|
// result |= A_BLINK; // does not work on my terminal
|
|
|
|
if (g_bTextFlashState)
|
|
|
|
{
|
|
|
|
result |= A_REVERSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result |= A_REVERSE;
|
|
|
|
}
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
void VideoUpdateFlash()
|
|
|
|
{
|
|
|
|
static UINT nTextFlashCnt = 0;
|
|
|
|
|
|
|
|
++nTextFlashCnt;
|
|
|
|
|
|
|
|
if (nTextFlashCnt == 16) // Flash rate = 0.5 * 60 / 16 Hz (as we need 2 changes for a period)
|
|
|
|
{
|
|
|
|
nTextFlashCnt = 0;
|
|
|
|
g_bTextFlashState = !g_bTextFlashState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-23 17:52:41 +01:00
|
|
|
ULONG lastUpdate = 0;
|
|
|
|
|
|
|
|
void updateSpeaker()
|
|
|
|
{
|
|
|
|
const ULONG dCycles = g_nCumulativeCycles - lastUpdate;
|
|
|
|
const double dt = dCycles / CLK_6502;
|
|
|
|
const double coeff = exp(- alpha * dt);
|
|
|
|
F = F * coeff;
|
|
|
|
lastUpdate = g_nCumulativeCycles;
|
|
|
|
}
|
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
2017-06-24 19:07:20 +01:00
|
|
|
double g_relativeSpeed = 1.0;
|
2017-06-10 20:17:53 +01:00
|
|
|
|
|
|
|
void output(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
|
|
|
WINDOW * win = frame->getBuffer();
|
|
|
|
|
|
|
|
vwprintw(win, fmt, args);
|
|
|
|
wrefresh(win);
|
|
|
|
}
|
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
bool Update40ColCell (int x, int y, int xpixel, int ypixel, int offset)
|
|
|
|
{
|
2017-06-24 19:01:08 +01:00
|
|
|
frame->init(24, 40);
|
|
|
|
asciiArt->init(1, 1);
|
2017-05-20 21:31:17 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
BYTE ch = *(g_pTextBank0+offset);
|
|
|
|
|
2017-05-20 21:31:17 +01:00
|
|
|
WINDOW * win = frame->getWindow();
|
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
const chtype ch2 = mapCharacter(ch);
|
2017-05-20 21:31:17 +01:00
|
|
|
mvwaddch(win, 1 + y, 1 + x, ch2);
|
2017-05-16 09:07:55 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Update80ColCell (int x, int y, int xpixel, int ypixel, int offset)
|
|
|
|
{
|
2017-06-24 19:01:08 +01:00
|
|
|
frame->init(24, 80);
|
|
|
|
asciiArt->init(1, 2);
|
2017-05-20 21:31:17 +01:00
|
|
|
|
|
|
|
BYTE ch1 = *(g_pTextBank1+offset);
|
|
|
|
BYTE ch2 = *(g_pTextBank0+offset);
|
|
|
|
|
|
|
|
WINDOW * win = frame->getWindow();
|
|
|
|
|
|
|
|
const chtype ch12 = mapCharacter(ch1);
|
|
|
|
mvwaddch(win, 1 + y, 1 + 2 * x, ch12);
|
|
|
|
|
|
|
|
const chtype ch22 = mapCharacter(ch2);
|
|
|
|
mvwaddch(win, 1 + y, 1 + 2 * x + 1, ch22);
|
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdateLoResCell (int x, int y, int xpixel, int ypixel, int offset)
|
|
|
|
{
|
2017-05-18 21:53:58 +01:00
|
|
|
BYTE val = *(g_pTextBank0+offset);
|
|
|
|
|
2017-06-01 20:52:11 +01:00
|
|
|
const int pair = colors->getPair(val);
|
2017-05-18 21:53:58 +01:00
|
|
|
|
2017-05-20 21:31:17 +01:00
|
|
|
WINDOW * win = frame->getWindow();
|
|
|
|
|
2017-06-01 20:52:11 +01:00
|
|
|
wcolor_set(win, pair, NULL);
|
2017-05-20 21:31:17 +01:00
|
|
|
if (frame->getColumns() == 40)
|
|
|
|
{
|
|
|
|
mvwaddstr(win, 1 + y, 1 + x, "\u2580");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mvwaddstr(win, 1 + y, 1 + 2 * x, "\u2580\u2580");
|
|
|
|
}
|
2017-06-01 20:52:11 +01:00
|
|
|
wcolor_set(win, 0, NULL);
|
2017-05-18 21:53:58 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdateDLoResCell (int x, int y, int xpixel, int ypixel, int offset)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdateHiResCell (int x, int y, int xpixel, int ypixel, int offset)
|
|
|
|
{
|
2017-06-01 21:36:42 +01:00
|
|
|
const BYTE * base = g_pHiresBank0 + offset;
|
|
|
|
|
2017-06-24 19:01:08 +01:00
|
|
|
const ASCIIArt::array_char_t & chs = asciiArt->getCharacters(base);
|
2017-06-01 21:36:42 +01:00
|
|
|
|
2017-06-24 19:01:08 +01:00
|
|
|
const auto shape = chs.shape();
|
|
|
|
const size_t rows = shape[0];
|
|
|
|
const size_t cols = shape[1];
|
2017-06-01 21:36:42 +01:00
|
|
|
|
2017-06-24 19:01:08 +01:00
|
|
|
frame->init(24 * rows, 40 * cols);
|
|
|
|
WINDOW * win = frame->getWindow();
|
2017-06-01 21:36:42 +01:00
|
|
|
|
2017-06-24 19:01:08 +01:00
|
|
|
for (size_t i = 0; i < rows; ++i)
|
|
|
|
{
|
|
|
|
for (size_t j = 0; j < cols; ++j)
|
|
|
|
{
|
|
|
|
const int pair = colors->getGrey(chs[i][j].foreground, chs[i][j].background);
|
|
|
|
|
|
|
|
wcolor_set(win, pair, NULL);
|
|
|
|
mvwaddstr(win, 1 + rows * y + i, 1 + cols * x + j, chs[i][j].c);
|
|
|
|
}
|
|
|
|
}
|
2017-06-01 21:36:42 +01:00
|
|
|
wcolor_set(win, 0, NULL);
|
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdateDHiResCell (int x, int y, int xpixel, int ypixel, int offset)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FrameRefresh()
|
|
|
|
{
|
2017-05-23 17:52:41 +01:00
|
|
|
WINDOW * status = frame->getStatus();
|
|
|
|
|
|
|
|
mvwprintw(status, 1, 2, "D1: %d, %s, %s", g_eStatusDrive1, g_sTrackDrive1, g_sSectorDrive1);
|
|
|
|
mvwprintw(status, 2, 2, "D2: %d, %s, %s", g_eStatusDrive2, g_sTrackDrive2, g_sSectorDrive2);
|
|
|
|
|
|
|
|
// approximate
|
|
|
|
const double frequency = 0.5 * alpha * F;
|
|
|
|
mvwprintw(status, 1, 20, "%5.fHz", frequency);
|
2017-06-10 20:17:53 +01:00
|
|
|
mvwprintw(status, 2, 20, "%5.1f%%", 100 * g_relativeSpeed);
|
2017-05-23 17:52:41 +01:00
|
|
|
|
|
|
|
wrefresh(status);
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrameDrawDiskLEDS(HDC x)
|
|
|
|
{
|
2017-05-23 17:52:41 +01:00
|
|
|
DiskGetLightStatus(&g_eStatusDrive1, &g_eStatusDrive2);
|
2017-05-16 09:07:55 +01:00
|
|
|
FrameRefresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameDrawDiskStatus(HDC x)
|
|
|
|
{
|
|
|
|
if (mem == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We use the actual drive since probing from memory doesn't tell us anything we don't already know.
|
|
|
|
// DOS3.3 ProDOS
|
|
|
|
// Drive $B7EA $BE3D
|
|
|
|
// Track $B7EC LC1 $D356
|
|
|
|
// Sector $B7ED LC1 $D357
|
|
|
|
// RWTS LC1 $D300
|
|
|
|
int nActiveFloppy = DiskGetCurrentDrive();
|
|
|
|
|
|
|
|
int nDisk1Track = DiskGetTrack(0);
|
|
|
|
int nDisk2Track = DiskGetTrack(1);
|
|
|
|
|
|
|
|
// Probe known OS's for Track/Sector
|
|
|
|
int isProDOS = mem[ 0xBF00 ] == 0x4C;
|
|
|
|
bool isValid = true;
|
|
|
|
|
|
|
|
// Try DOS3.3 Sector
|
|
|
|
if ( !isProDOS )
|
|
|
|
{
|
|
|
|
int nDOS33track = mem[ 0xB7EC ];
|
|
|
|
int nDOS33sector = mem[ 0xB7ED ];
|
|
|
|
|
|
|
|
if ((nDOS33track >= 0 && nDOS33track < 40)
|
|
|
|
&& (nDOS33sector >= 0 && nDOS33sector < 16))
|
|
|
|
{
|
|
|
|
|
|
|
|
/**/ if (nActiveFloppy == 0) g_nSectorDrive1 = nDOS33sector;
|
|
|
|
else if (nActiveFloppy == 1) g_nSectorDrive2 = nDOS33sector;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
isValid = false;
|
|
|
|
}
|
|
|
|
else // isProDOS
|
|
|
|
{
|
|
|
|
// we can't just read from mem[ 0xD357 ] since it might be bank-switched from ROM
|
|
|
|
// and we need the Language Card RAM
|
|
|
|
// memrom[ 0xD350 ] = " ERROR\x07\x00" Applesoft error message
|
|
|
|
// T S
|
|
|
|
int nProDOStrack = *MemGetMainPtr( 0xC356 ); // LC1 $D356
|
|
|
|
int nProDOSsector = *MemGetMainPtr( 0xC357 ); // LC1 $D357
|
|
|
|
|
|
|
|
if ((nProDOStrack >= 0 && nProDOStrack < 40)
|
|
|
|
&& (nProDOSsector >= 0 && nProDOSsector < 16))
|
|
|
|
{
|
|
|
|
/**/ if (nActiveFloppy == 0) g_nSectorDrive1 = nProDOSsector;
|
|
|
|
else if (nActiveFloppy == 1) g_nSectorDrive2 = nProDOSsector;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
isValid = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_nTrackDrive1 = nDisk1Track;
|
|
|
|
g_nTrackDrive2 = nDisk2Track;
|
|
|
|
|
|
|
|
if( !isValid )
|
|
|
|
{
|
|
|
|
if (nActiveFloppy == 0) g_nSectorDrive1 = -1;
|
|
|
|
else g_nSectorDrive2 = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf( g_sTrackDrive1 , sizeof(g_sTrackDrive1 ), "%2d", g_nTrackDrive1 );
|
|
|
|
if (g_nSectorDrive1 < 0) snprintf( g_sSectorDrive1, sizeof(g_sSectorDrive1), "??" );
|
|
|
|
else snprintf( g_sSectorDrive1, sizeof(g_sSectorDrive1), "%2d", g_nSectorDrive1 );
|
|
|
|
|
|
|
|
snprintf( g_sTrackDrive2 , sizeof(g_sTrackDrive2), "%2d", g_nTrackDrive2 );
|
|
|
|
if (g_nSectorDrive2 < 0) snprintf( g_sSectorDrive2, sizeof(g_sSectorDrive2), "??" );
|
|
|
|
else snprintf( g_sSectorDrive2, sizeof(g_sSectorDrive2), "%2d", g_nSectorDrive2 );
|
|
|
|
|
|
|
|
FrameRefresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameRefreshStatus(int x, bool)
|
|
|
|
{
|
|
|
|
// std::cerr << "Status: " << x << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoInitialize()
|
|
|
|
{
|
2017-05-18 21:53:58 +01:00
|
|
|
setlocale(LC_ALL, "");
|
2017-05-16 09:07:55 +01:00
|
|
|
initscr();
|
2017-05-18 21:53:58 +01:00
|
|
|
|
2017-06-01 21:36:42 +01:00
|
|
|
colors.reset(new GraphicsColors(20, 20, 32));
|
2017-05-18 21:53:58 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
curs_set(0);
|
|
|
|
|
|
|
|
noecho();
|
|
|
|
cbreak();
|
|
|
|
set_escdelay(0);
|
|
|
|
|
2017-05-20 21:31:17 +01:00
|
|
|
frame.reset(new Frame());
|
2017-06-01 21:36:42 +01:00
|
|
|
asciiArt.reset(new ASCIIArt());
|
2017-06-10 20:01:55 +01:00
|
|
|
input.reset(new Input("/dev/input/by-id/usb-©Microsoft_Corporation_Controller_1BBE3DB-event-joystick"));
|
2017-05-16 09:07:55 +01:00
|
|
|
|
2017-05-20 21:31:17 +01:00
|
|
|
signal(SIGINT, sig_handler);
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
2017-06-04 19:05:14 +01:00
|
|
|
void VideoUninitialize()
|
|
|
|
{
|
|
|
|
endwin();
|
|
|
|
}
|
|
|
|
|
2017-05-20 20:37:18 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
void VideoRedrawScreen()
|
|
|
|
{
|
2017-05-20 20:37:18 +01:00
|
|
|
VideoUpdateFlash();
|
2017-05-23 17:52:41 +01:00
|
|
|
updateSpeaker();
|
|
|
|
FrameRefresh();
|
2017-05-20 20:37:18 +01:00
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
const int displaypage2 = (SW_PAGE2) == 0 ? 0 : 1;
|
|
|
|
|
|
|
|
g_pHiresBank1 = MemGetAuxPtr (0x2000 << displaypage2);
|
|
|
|
g_pHiresBank0 = MemGetMainPtr(0x2000 << displaypage2);
|
|
|
|
g_pTextBank1 = MemGetAuxPtr (0x400 << displaypage2);
|
|
|
|
g_pTextBank0 = MemGetMainPtr(0x400 << displaypage2);
|
|
|
|
|
|
|
|
VideoUpdateFuncPtr_t update = SW_TEXT
|
|
|
|
? SW_80COL
|
|
|
|
? Update80ColCell
|
|
|
|
: Update40ColCell
|
|
|
|
: SW_HIRES
|
|
|
|
? (SW_DHIRES && SW_80COL)
|
|
|
|
? UpdateDHiResCell
|
|
|
|
: UpdateHiResCell
|
|
|
|
: (SW_DHIRES && SW_80COL)
|
|
|
|
? UpdateDLoResCell
|
|
|
|
: UpdateLoResCell;
|
|
|
|
|
|
|
|
int y = 0;
|
|
|
|
int ypixel = 0;
|
|
|
|
while (y < 20) {
|
|
|
|
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
|
|
|
|
int x = 0;
|
|
|
|
int xpixel = 0;
|
|
|
|
while (x < 40) {
|
|
|
|
update(x, y, xpixel, ypixel, offset + x);
|
|
|
|
++x;
|
|
|
|
xpixel += 14;
|
|
|
|
}
|
|
|
|
++y;
|
|
|
|
ypixel += 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SW_MIXED)
|
|
|
|
update = SW_80COL ? Update80ColCell
|
|
|
|
: Update40ColCell;
|
|
|
|
|
|
|
|
while (y < 24) {
|
|
|
|
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
|
|
|
|
int x = 0;
|
|
|
|
int xpixel = 0;
|
|
|
|
while (x < 40) {
|
|
|
|
update(x, y, xpixel, ypixel, offset + x);
|
|
|
|
++x;
|
|
|
|
xpixel += 14;
|
|
|
|
}
|
|
|
|
++y;
|
|
|
|
ypixel += 16;
|
|
|
|
}
|
|
|
|
|
2017-05-20 21:31:17 +01:00
|
|
|
wrefresh(frame->getWindow());
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
|
2017-06-04 19:05:14 +01:00
|
|
|
int ProcessKeyboard()
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-21 20:23:48 +01:00
|
|
|
const int inch = wgetch(frame->getWindow());
|
|
|
|
|
|
|
|
int ch = ERR;
|
|
|
|
|
|
|
|
switch (inch)
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-21 20:23:48 +01:00
|
|
|
case ERR:
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
ch = 0x0d; // ENTER
|
|
|
|
break;
|
|
|
|
case KEY_BACKSPACE:
|
|
|
|
case KEY_LEFT:
|
|
|
|
ch = 0x08;
|
|
|
|
break;
|
|
|
|
case KEY_RIGHT:
|
|
|
|
ch = 0x15;
|
|
|
|
break;
|
|
|
|
case KEY_UP:
|
|
|
|
ch = 0x0b;
|
|
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
|
|
ch = 0x0a;
|
|
|
|
break;
|
|
|
|
case 0x14a: // DEL
|
|
|
|
ch = 0x7f;
|
|
|
|
break;
|
2017-06-24 19:01:08 +01:00
|
|
|
case 0x221: // ALT - LEFT
|
|
|
|
asciiArt->changeColumns(-1);
|
|
|
|
break;
|
|
|
|
case 0x230: // ALT - RIGHT
|
|
|
|
asciiArt->changeColumns(+1);
|
|
|
|
break;
|
|
|
|
case 0x236:
|
|
|
|
asciiArt->changeRows(-1);
|
|
|
|
break;
|
|
|
|
case 0x20D:
|
|
|
|
asciiArt->changeRows(+1);
|
|
|
|
break;
|
2017-05-21 20:23:48 +01:00
|
|
|
default:
|
|
|
|
if (inch < 0x80)
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-21 20:23:48 +01:00
|
|
|
ch = inch;
|
|
|
|
// Standard for Apple II is Upper case
|
|
|
|
if (ch >= 'A' && ch <= 'Z')
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-21 20:23:48 +01:00
|
|
|
ch += 'a' - 'A';
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
2017-05-21 20:23:48 +01:00
|
|
|
else if (ch >= 'a' && ch <= 'z')
|
2017-05-16 09:07:55 +01:00
|
|
|
{
|
2017-05-21 20:23:48 +01:00
|
|
|
ch -= 'a' - 'A';
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
}
|
2017-05-21 20:23:48 +01:00
|
|
|
}
|
2017-05-16 09:07:55 +01:00
|
|
|
|
2017-05-21 20:23:48 +01:00
|
|
|
if (ch != ERR)
|
|
|
|
{
|
|
|
|
nextKey = ch | 0x80;
|
|
|
|
keyReady = true;
|
2017-06-04 19:05:14 +01:00
|
|
|
return ERR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// pass it back
|
|
|
|
return inch;
|
2017-05-16 09:07:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-10 20:01:55 +01:00
|
|
|
void ProcessInput()
|
|
|
|
{
|
|
|
|
input->poll();
|
|
|
|
}
|
|
|
|
|
2017-05-16 09:07:55 +01:00
|
|
|
BYTE KeybGetKeycode ()
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BYTE __stdcall KeybReadData (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
|
|
|
{
|
|
|
|
return nextKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
BYTE __stdcall KeybReadFlag (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
|
|
|
{
|
|
|
|
BYTE result = keyReady ? nextKey : 0;
|
|
|
|
nextKey = 0;
|
|
|
|
return result;
|
|
|
|
}
|
2017-05-21 20:23:48 +01:00
|
|
|
|
|
|
|
BYTE __stdcall JoyReadButton(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
|
|
|
{
|
|
|
|
addr &= 0xFF;
|
|
|
|
BOOL pressed = 0;
|
|
|
|
|
|
|
|
switch (addr)
|
|
|
|
{
|
|
|
|
case 0x61:
|
2017-06-10 20:01:55 +01:00
|
|
|
pressed = input->getButton(0);
|
2017-05-21 20:23:48 +01:00
|
|
|
break;
|
|
|
|
case 0x62:
|
2017-06-10 20:01:55 +01:00
|
|
|
pressed = input->getButton(1);
|
2017-05-21 20:23:48 +01:00
|
|
|
break;
|
|
|
|
case 0x63:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return MemReadFloatingBus(pressed, nCyclesLeft);
|
|
|
|
}
|
|
|
|
|
2017-06-10 20:01:55 +01:00
|
|
|
BYTE __stdcall JoyReadPosition(WORD pc, WORD address, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
2017-05-21 20:23:48 +01:00
|
|
|
{
|
2017-06-10 20:01:55 +01:00
|
|
|
const int nJoyNum = (address & 2) ? 1 : 0; // $C064..$C067
|
|
|
|
|
|
|
|
CpuCalcCycles(nCyclesLeft);
|
2017-05-21 20:23:48 +01:00
|
|
|
BOOL nPdlCntrActive = 0;
|
2017-06-10 20:01:55 +01:00
|
|
|
|
|
|
|
if (nJoyNum == 0)
|
|
|
|
{
|
|
|
|
int axis = address & 1;
|
|
|
|
int pdl = input->getAxis(axis);
|
|
|
|
// This is from KEGS. It helps games like Championship Lode Runner & Boulderdash
|
|
|
|
if (pdl >= 255)
|
|
|
|
pdl = 280;
|
|
|
|
|
|
|
|
nPdlCntrActive = g_nCumulativeCycles <= (g_nJoyCntrResetCycle + (unsigned __int64) ((double)pdl * PDL_CNTR_INTERVAL));
|
|
|
|
}
|
|
|
|
|
2017-05-21 20:23:48 +01:00
|
|
|
return MemReadFloatingBus(nPdlCntrActive, nCyclesLeft);
|
|
|
|
}
|
|
|
|
|
|
|
|
BYTE __stdcall JoyResetPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
|
|
|
{
|
2017-06-10 20:01:55 +01:00
|
|
|
CpuCalcCycles(nCyclesLeft);
|
|
|
|
g_nJoyCntrResetCycle = g_nCumulativeCycles;
|
|
|
|
|
2017-05-21 20:23:48 +01:00
|
|
|
return MemReadFloatingBus(nCyclesLeft);
|
|
|
|
}
|
2017-05-23 17:52:41 +01:00
|
|
|
|
|
|
|
// Speaker
|
|
|
|
|
|
|
|
BYTE __stdcall SpkrToggle (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
|
|
|
|
{
|
|
|
|
CpuCalcCycles(nCyclesLeft);
|
|
|
|
|
|
|
|
updateSpeaker();
|
|
|
|
F += 1;
|
|
|
|
|
|
|
|
return MemReadFloatingBus(nCyclesLeft);
|
|
|
|
}
|