mirror of
https://github.com/cmosher01/Epple-II.git
synced 2025-02-28 22:29:02 +00:00
make keyboard usable (not fully tested yet); reformat file
This commit is contained in:
parent
13481ab90e
commit
bda6d62294
790
src/emulator.cpp
790
src/emulator.cpp
@ -14,7 +14,7 @@
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
*/
|
||||
#include "emulator.h"
|
||||
#include "configep2.h"
|
||||
#include "e2const.h"
|
||||
@ -23,445 +23,421 @@
|
||||
|
||||
#include <ctime>
|
||||
|
||||
Emulator::Emulator():
|
||||
display(screenImage),
|
||||
videoStatic(display),
|
||||
apple2(keypresses,paddleButtonStates,display,fhyper,buffered,screenImage),
|
||||
timable(0), // No ticked object (NULL pointer)
|
||||
quit(false),
|
||||
repeat(false),
|
||||
keysDown(0),
|
||||
command(false),
|
||||
pendingCommandExit(false)
|
||||
{
|
||||
Emulator::Emulator() :
|
||||
display(screenImage),
|
||||
videoStatic(display),
|
||||
apple2(keypresses, paddleButtonStates, display, fhyper, buffered, screenImage),
|
||||
timable(0), // No ticked object (NULL pointer)
|
||||
quit(false),
|
||||
repeat(false),
|
||||
keysDown(0),
|
||||
command(false),
|
||||
pendingCommandExit(false) {
|
||||
}
|
||||
|
||||
|
||||
Emulator::~Emulator()
|
||||
{
|
||||
Emulator::~Emulator() {
|
||||
}
|
||||
|
||||
void Emulator::toggleComputerPower()
|
||||
{
|
||||
if (this->timable==&this->videoStatic)
|
||||
powerOnComputer();
|
||||
else
|
||||
powerOffComputer();
|
||||
void Emulator::toggleComputerPower() {
|
||||
if (this->timable == &this->videoStatic)
|
||||
powerOnComputer();
|
||||
else
|
||||
powerOffComputer();
|
||||
}
|
||||
|
||||
void Emulator::powerOnComputer()
|
||||
{
|
||||
this->apple2.powerOn();
|
||||
this->screenImage.drawPower(true);
|
||||
this->display.setNoise(false);
|
||||
void Emulator::powerOnComputer() {
|
||||
this->apple2.powerOn();
|
||||
this->screenImage.drawPower(true);
|
||||
this->display.setNoise(false);
|
||||
|
||||
// The apple2 becomes the ticked object
|
||||
this->timable = &this->apple2;
|
||||
// The apple2 becomes the ticked object
|
||||
this->timable = &this->apple2;
|
||||
}
|
||||
|
||||
void Emulator::powerOffComputer()
|
||||
{
|
||||
// TODO Need to ask user if OK to lose any unsaved changes to disks
|
||||
this->apple2.powerOff();
|
||||
this->screenImage.drawPower(false);
|
||||
this->display.setNoise(true);
|
||||
this->videoStatic.powerOn();
|
||||
void Emulator::powerOffComputer() {
|
||||
// TODO Need to ask user if OK to lose any unsaved changes to disks
|
||||
this->apple2.powerOff();
|
||||
this->screenImage.drawPower(false);
|
||||
this->display.setNoise(true);
|
||||
this->videoStatic.powerOn();
|
||||
|
||||
// The video static becomes the ticked object
|
||||
this->timable = &this->videoStatic;
|
||||
// The video static becomes the ticked object
|
||||
this->timable = &this->videoStatic;
|
||||
}
|
||||
|
||||
void Emulator::config(Config& cfg)
|
||||
{
|
||||
cfg.parse(this->apple2.ram,this->apple2.rom,this->apple2.slts,this->apple2.revision,this->screenImage,this->apple2.cassette);
|
||||
void Emulator::config(Config& cfg) {
|
||||
cfg.parse(this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassette);
|
||||
}
|
||||
|
||||
void Emulator::init()
|
||||
{
|
||||
powerOffComputer();
|
||||
this->display.setType(AnalogTV::MONITOR_COLOR);
|
||||
this->display.powerOn(true);
|
||||
void Emulator::init() {
|
||||
powerOffComputer();
|
||||
this->display.setType(AnalogTV::MONITOR_COLOR);
|
||||
this->display.powerOn(true);
|
||||
}
|
||||
|
||||
// How many emulation ticks between asking SDL if there is any new input
|
||||
// from the user or other GUI events.
|
||||
// This is also how often we shall update the estimate of the emulator's
|
||||
// actual speed performance
|
||||
// When the CPU is the object being ticked (each tick is a CPU cycle), then
|
||||
// this is 20.04378892 Hz in emulated seconds time
|
||||
// How many emulation ticks between asking SDL if there is any new input
|
||||
// from the user or other GUI events.
|
||||
// This is also how often we shall update the estimate of the emulator's
|
||||
// actual speed performance
|
||||
// When the CPU is the object being ticked (each tick is a CPU cycle), then
|
||||
// this is 20.04378892 Hz in emulated seconds time
|
||||
#define CHECK_EVERY_CYCLE 51024
|
||||
#define CHECK_CYCLES_K 51024000
|
||||
#define EXPECTED_MS 50
|
||||
|
||||
// U.A.2 p. 7-13: REPT key repeats at 10Hz.
|
||||
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ/10);
|
||||
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ / 10);
|
||||
|
||||
|
||||
// The core of this Apple
|
||||
int Emulator::run()
|
||||
{
|
||||
int skip = CHECK_EVERY_CYCLE;
|
||||
Uint32 prev_ms = SDL_GetTicks();
|
||||
// While the user still wants to run this emulation...
|
||||
while (!this->quit)
|
||||
{
|
||||
// (Obligatory protection against NULL object pointer)
|
||||
if (this->timable)
|
||||
{
|
||||
this->timable->tick();
|
||||
// If the Apple ][ keyboard repeat is on (the REPT key is
|
||||
// down)...
|
||||
if (this->repeat)
|
||||
{
|
||||
// Count our way down to when the timer for the REPT key
|
||||
// fires off: 10Hz in terms of how many CPU cycles have gone
|
||||
// by
|
||||
--this->rept;
|
||||
// If it's time for the REPT key timer to fire (at long
|
||||
// last)...
|
||||
if (this->rept <= 0)
|
||||
{
|
||||
// ...reload the timer for the next firing 1/10 second from
|
||||
// now ( *reset* the timer )
|
||||
this->rept = CYCLES_PER_REPT;
|
||||
// If any other keys are actually being held down...
|
||||
if (this->keysDown > 0)
|
||||
{
|
||||
// ...REPEAT the most recent one that was pressed
|
||||
this->keypresses.push(this->lastKeyDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The core of this Apple
|
||||
|
||||
// People who have too many press releases should be referred to as
|
||||
// keyboards
|
||||
|
||||
--skip;
|
||||
// If skip has been decremented to zero...
|
||||
if (!skip)
|
||||
{
|
||||
// ...then it's time to drain away any piled-up user interaction
|
||||
// events that SDL has stored up for us
|
||||
// Reload the skip quantity
|
||||
skip = CHECK_EVERY_CYCLE;
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
// If SDL is going away...
|
||||
case SDL_QUIT:
|
||||
this->quit = true;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
// If we're collecting a command line for changing any
|
||||
// of the configurables of the emulator...
|
||||
if (this->command)
|
||||
cmdKey(event.key);
|
||||
else
|
||||
// ...else we're collecting keypresses for the keyboard
|
||||
// emulation (and thus the Apple ][ emulation itself)
|
||||
dispatchKeypress(event.key);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
// If we're collecting a command line for changing any
|
||||
// of the configurables of the emulator...
|
||||
if (this->command)
|
||||
{
|
||||
if (this->pendingCommandExit)
|
||||
{
|
||||
this->command = false;
|
||||
this->pendingCommandExit = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...else we're collecting keypresses for the keyboard
|
||||
// emulation (and thus the Apple ][ emulation itself)
|
||||
dispatchKeyUp(event.key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we're trying to run as slow as a real Apple ][...
|
||||
if (!this->fhyper.isHyper())
|
||||
{
|
||||
const int delta_ms = EXPECTED_MS-(SDL_GetTicks()-prev_ms);
|
||||
if (0 < delta_ms && delta_ms <= EXPECTED_MS)
|
||||
{
|
||||
SDL_Delay(delta_ms);
|
||||
}
|
||||
|
||||
}
|
||||
// Display the current estimate of the emulator's actual speed
|
||||
// performance
|
||||
this->screenImage.displayHz(CHECK_CYCLES_K/(SDL_GetTicks()-prev_ms));
|
||||
prev_ms = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent)
|
||||
{
|
||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||
SDL_Keymod mod = (SDL_Keymod)keyEvent.keysym.mod;
|
||||
unsigned char key = (unsigned char)(sym & 0x7F);
|
||||
|
||||
// printf("key UP: %d sym: %d mod: %04X scn: %d\n",key,sym,mod,scancode);
|
||||
|
||||
if ((sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) &&
|
||||
!(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) &&
|
||||
!(sym == ']' && mod&KMOD_SHIFT))
|
||||
{
|
||||
--this->keysDown;
|
||||
}
|
||||
// ...else if this is the emulated REPT key on the Apple keyboard...
|
||||
else if (sym == SDLK_F10)
|
||||
{
|
||||
// ...stop repeating. The key has been released
|
||||
this->repeat = false;
|
||||
this->rept = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Take real-world keystrokes from SDL and filter them to emulate the
|
||||
// Apple ][ or Apple ][ plus keyboard
|
||||
void Emulator::dispatchKeypress(const SDL_KeyboardEvent& keyEvent)
|
||||
{
|
||||
if (keyEvent.repeat)
|
||||
{
|
||||
return;
|
||||
int Emulator::run() {
|
||||
int skip = CHECK_EVERY_CYCLE;
|
||||
Uint32 prev_ms = SDL_GetTicks();
|
||||
// While the user still wants to run this emulation...
|
||||
while (!this->quit) {
|
||||
// (Obligatory protection against NULL object pointer)
|
||||
if (this->timable) {
|
||||
this->timable->tick();
|
||||
// If the Apple ][ keyboard repeat is on (the REPT key is
|
||||
// down)...
|
||||
if (this->repeat) {
|
||||
// Count our way down to when the timer for the REPT key
|
||||
// fires off: 10Hz in terms of how many CPU cycles have gone
|
||||
// by
|
||||
--this->rept;
|
||||
// If it's time for the REPT key timer to fire (at long
|
||||
// last)...
|
||||
if (this->rept <= 0) {
|
||||
// ...reload the timer for the next firing 1/10 second from
|
||||
// now ( *reset* the timer )
|
||||
this->rept = CYCLES_PER_REPT;
|
||||
// If any other keys are actually being held down...
|
||||
if (this->keysDown > 0) {
|
||||
// ...REPEAT the most recent one that was pressed
|
||||
this->keypresses.push(this->lastKeyDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||
SDL_Keymod mod = (SDL_Keymod)keyEvent.keysym.mod;
|
||||
unsigned char key = (unsigned char)(sym & 0x7F);
|
||||
// People who have too many press releases should be referred to as
|
||||
// keyboards
|
||||
|
||||
// printf("key DN: %d sym: %d mod: %04X scn: %d\n",key,sym,mod,scancode);
|
||||
--skip;
|
||||
// If skip has been decremented to zero...
|
||||
if (!skip) {
|
||||
// ...then it's time to drain away any piled-up user interaction
|
||||
// events that SDL has stored up for us
|
||||
// Reload the skip quantity
|
||||
skip = CHECK_EVERY_CYCLE;
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
// If SDL is going away...
|
||||
case SDL_QUIT:
|
||||
this->quit = true;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
// If we're collecting a command line for changing any
|
||||
// of the configurables of the emulator...
|
||||
if (this->command)
|
||||
cmdKey(event.key);
|
||||
else
|
||||
// ...else we're collecting keypresses for the keyboard
|
||||
// emulation (and thus the Apple ][ emulation itself)
|
||||
dispatchKeypress(event.key);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
// If we're collecting a command line for changing any
|
||||
// of the configurables of the emulator...
|
||||
if (this->command) {
|
||||
if (this->pendingCommandExit) {
|
||||
this->command = false;
|
||||
this->pendingCommandExit = false;
|
||||
}
|
||||
} else {
|
||||
// ...else we're collecting keypresses for the keyboard
|
||||
// emulation (and thus the Apple ][ emulation itself)
|
||||
dispatchKeyUp(event.key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we're trying to run as slow as a real Apple ][...
|
||||
if (!this->fhyper.isHyper()) {
|
||||
const int delta_ms = EXPECTED_MS - (SDL_GetTicks() - prev_ms);
|
||||
if (0 < delta_ms && delta_ms <= EXPECTED_MS) {
|
||||
SDL_Delay(delta_ms);
|
||||
}
|
||||
|
||||
if ((sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) &&
|
||||
!(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) &&
|
||||
!(sym == ']' && mod&KMOD_SHIFT))
|
||||
{
|
||||
++this->keysDown;
|
||||
}
|
||||
|
||||
if (sym == SDLK_LEFT)
|
||||
{
|
||||
key = 8;
|
||||
}
|
||||
else if (sym == SDLK_RIGHT)
|
||||
{
|
||||
key = 21;
|
||||
}
|
||||
else if (sym == SDLK_PAUSE)
|
||||
{
|
||||
this->apple2.reset();
|
||||
return;
|
||||
}
|
||||
else if (sym == SDLK_INSERT)
|
||||
{
|
||||
// Feed input from the clipboard to the Apple keyboard
|
||||
std::string s = this->clip.getText();
|
||||
for (unsigned int i = 0; i < s.length(); ++i)
|
||||
{
|
||||
key = s[i];
|
||||
// TODO fix pasting line-endings
|
||||
if (key == '\n')
|
||||
key = '\r';
|
||||
if ('a' <= key && key <= 'z')
|
||||
{
|
||||
key -= 32;
|
||||
}
|
||||
this->keypresses.push(key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// ...else if this is the emulated REPT key on the Apple keyboard...
|
||||
else if (sym == SDLK_F10)
|
||||
{
|
||||
// ...start auto-repeat
|
||||
this->repeat = true;
|
||||
this->rept = CYCLES_PER_REPT;
|
||||
return;
|
||||
}
|
||||
// ...else if the user wants to run at full speed instead of emulating
|
||||
// the Apple's speed...
|
||||
else if (sym == SDLK_F11)
|
||||
{
|
||||
this->fhyper.toggleHyper();
|
||||
this->screenImage.toggleHyperLabel();
|
||||
return;
|
||||
}
|
||||
else if (sym == SDLK_F12)
|
||||
{
|
||||
this->buffered.toggleBuffered();
|
||||
this->screenImage.toggleKdbBufferLabel();
|
||||
return;
|
||||
}
|
||||
// ...else if the user has hit the rocker switch on the back of the Apple...
|
||||
else if (sym == SDLK_F1)
|
||||
{
|
||||
toggleComputerPower();
|
||||
return;
|
||||
}
|
||||
// ...else if the user wants to look at a different video display medium...
|
||||
else if (sym == SDLK_F2)
|
||||
{
|
||||
this->display.cycleType();
|
||||
this->screenImage.cycleDisplayLabel();
|
||||
return;
|
||||
}
|
||||
// ...else if the user wants to switch to/from full screen and an
|
||||
// individual application window...
|
||||
else if (sym == SDLK_F3)
|
||||
{
|
||||
this->screenImage.toggleFullScreen();
|
||||
this->screenImage.drawPower(this->timable==&this->apple2);
|
||||
return;
|
||||
}
|
||||
// ...else if the user wants to switch between the interlaced extension
|
||||
// of the display and the non-interlaced historically correct display...
|
||||
else if (sym == SDLK_F4)
|
||||
{
|
||||
this->display.toggleBleedDown();
|
||||
this->screenImage.toggleFillLinesLabel();
|
||||
return;
|
||||
}
|
||||
// ...else initiate command line entry at the bottom of the emulator window
|
||||
else if (sym == SDLK_F5)
|
||||
{
|
||||
this->command = true;
|
||||
this->screenImage.enterCommandMode();
|
||||
}
|
||||
// ...else exit the entire emulation
|
||||
else if (sym == SDLK_END)
|
||||
{
|
||||
this->quit = true;
|
||||
return;
|
||||
}
|
||||
// ...else save a screen shot
|
||||
else if (sym == SDLK_PRINTSCREEN)
|
||||
{
|
||||
this->screenImage.saveBMP();
|
||||
}
|
||||
|
||||
// The unmodified Apple ][ hardware keyboard only generates upper-case
|
||||
if ('a' <= key && key <= 'z')
|
||||
{
|
||||
key -= 32;
|
||||
}
|
||||
|
||||
if ((mod&KMOD_SHIFT) && (mod&KMOD_CTRL) && sym == '2')
|
||||
{
|
||||
// Ctrl-Shift-2 == Ctrl-@ == NUL == ASCII: 0
|
||||
key = 0;
|
||||
}
|
||||
else if ((mod&KMOD_SHIFT) && (mod&KMOD_CTRL) && sym == ' ')
|
||||
{
|
||||
// Ctrl-Shift-Space is the same as Space
|
||||
key = ' ';
|
||||
}
|
||||
else if ((mod&KMOD_CTRL) && !(mod&KMOD_SHIFT) && SDLK_KP_0 <= sym && sym <= SDLK_KP_9)
|
||||
{
|
||||
// Control-only numeric keypad keys are converted to regular digit keys
|
||||
key = sym-SDLK_KP_0+'0';
|
||||
}
|
||||
else if ((mod&KMOD_CTRL) && !(mod&KMOD_SHIFT) && (('0' <= sym && sym <= '9') || sym == '/' || sym == ' '))
|
||||
{
|
||||
// Control-only upon 0-9, / and space leaves them unchanged, the
|
||||
// same as unmodified
|
||||
key = sym;
|
||||
}
|
||||
else if (sym == ']')
|
||||
{
|
||||
if (mod&KMOD_SHIFT)
|
||||
{
|
||||
// ignore '}' (shift ']')
|
||||
return;
|
||||
}
|
||||
if (mod&KMOD_CTRL)
|
||||
{
|
||||
// Ctrl-] == ASCII: $1D
|
||||
key = 29;
|
||||
}
|
||||
}
|
||||
// ...else if this is one of the keys that can't be typed on an Apple ][
|
||||
// keyboard...
|
||||
else if (key == 0 || sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// printf(" sending to apple as ascii------------------------------>%02X (%02X)\n",key,key|0x80);
|
||||
this->keypresses.push(key);
|
||||
this->lastKeyDown = key;
|
||||
}
|
||||
// Display the current estimate of the emulator's actual speed
|
||||
// performance
|
||||
this->screenImage.displayHz(CHECK_CYCLES_K / (SDL_GetTicks() - prev_ms));
|
||||
prev_ms = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Collect and edit a command line typed at the bottom of the emulator
|
||||
// window
|
||||
void Emulator::cmdKey(const SDL_KeyboardEvent& keyEvent)
|
||||
{
|
||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||
SDL_Keymod mod = (SDL_Keymod)keyEvent.keysym.mod;
|
||||
unsigned char key = (unsigned char)(sym & 0x7F);
|
||||
void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) {
|
||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||
SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod;
|
||||
unsigned char key = (unsigned char) (sym & 0x7F);
|
||||
|
||||
if (sym == SDLK_RETURN)
|
||||
{
|
||||
processCommand();
|
||||
}
|
||||
else if (sym == SDLK_ESCAPE)
|
||||
{
|
||||
cmdline.erase(cmdline.begin(),cmdline.end());
|
||||
processCommand();
|
||||
}
|
||||
else if (sym == SDLK_BACKSPACE)
|
||||
{
|
||||
if (cmdline.length())
|
||||
{
|
||||
cmdline.erase(cmdline.end()-1);
|
||||
this->screenImage.backspaceCommand();
|
||||
}
|
||||
}
|
||||
else if (sym == SDLK_INSERT)
|
||||
{
|
||||
std::string s = this->clip.getText();
|
||||
for (unsigned int i = 0; i < s.length(); ++i)
|
||||
{
|
||||
key = s[i];
|
||||
if (key == '\n' || key == '\r')
|
||||
{
|
||||
processCommand();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdline += key;
|
||||
this->screenImage.addkeyCommand(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key)
|
||||
{
|
||||
cmdline += key;
|
||||
this->screenImage.addkeyCommand(key);
|
||||
}
|
||||
if ((sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) &&
|
||||
!(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) &&
|
||||
!(sym == ']' && mod & KMOD_SHIFT)) {
|
||||
--this->keysDown;
|
||||
}// ...else if this is the emulated REPT key on the Apple keyboard...
|
||||
else if (sym == SDLK_F10) {
|
||||
// ...stop repeating. The key has been released
|
||||
this->repeat = false;
|
||||
this->rept = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Process a command line typed at the bottom of the emulator window
|
||||
void Emulator::processCommand()
|
||||
{
|
||||
this->screenImage.exitCommandMode();
|
||||
this->pendingCommandExit = true;
|
||||
static bool translateKeysToAppleModernized(SDL_Keycode keycode, SDL_Keymod modifiers, unsigned char* key) {
|
||||
if (keycode >= 0x100) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmdline.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
*key = (unsigned char) (keycode & 0x7F);
|
||||
|
||||
Config::parseLine(cmdline,this->apple2.ram,this->apple2.rom,this->apple2.slts,this->apple2.revision,this->screenImage,this->apple2.cassette);
|
||||
cmdline.erase(cmdline.begin(),cmdline.end());
|
||||
// The Apple ][ hardware keyboard only generates upper-case
|
||||
if ('a' <= *key && *key <= 'z') {
|
||||
*key -= 32;
|
||||
}
|
||||
|
||||
// from SDL 1.2 to 2.0, we can't use UNICODE so we need to
|
||||
// apply shift and control modifiers ourselves
|
||||
if (modifiers & KMOD_SHIFT) {
|
||||
if (keycode == SDLK_BACKQUOTE) *key = '~';
|
||||
else if (keycode == SDLK_1) *key = '!';
|
||||
else if (keycode == SDLK_2) *key = '@';
|
||||
else if (keycode == SDLK_3) *key = '#';
|
||||
else if (keycode == SDLK_4) *key = '$';
|
||||
else if (keycode == SDLK_5) *key = '%';
|
||||
else if (keycode == SDLK_6) *key = '^';
|
||||
else if (keycode == SDLK_7) *key = '&';
|
||||
else if (keycode == SDLK_8) *key = '*';
|
||||
else if (keycode == SDLK_9) *key = '(';
|
||||
else if (keycode == SDLK_0) *key = ')';
|
||||
else if (keycode == SDLK_MINUS) *key = '_';
|
||||
else if (keycode == SDLK_EQUALS) *key = '+';
|
||||
|
||||
else if (keycode == SDLK_SEMICOLON) *key = ':';
|
||||
else if (keycode == SDLK_QUOTE) *key = '\"';
|
||||
|
||||
else if (keycode == SDLK_COMMA) *key = '<';
|
||||
else if (keycode == SDLK_PERIOD) *key = '>';
|
||||
else if (keycode == SDLK_SLASH) *key = '?';
|
||||
}
|
||||
|
||||
if (modifiers & KMOD_CTRL) {
|
||||
if ('A' <= *key && *key <= 'Z') {
|
||||
*key -= 64;
|
||||
}
|
||||
}
|
||||
|
||||
if (keycode == SDLK_LEFT) {
|
||||
*key = 8;
|
||||
} else if (keycode == SDLK_RIGHT) {
|
||||
*key = 21;
|
||||
}
|
||||
|
||||
|
||||
if ((modifiers & KMOD_SHIFT) && (modifiers & KMOD_CTRL) && keycode == '2') {
|
||||
// Ctrl-Shift-2 == Ctrl-@ == NUL == ASCII: 0
|
||||
*key = 0;
|
||||
} else if ((modifiers & KMOD_SHIFT) && (modifiers & KMOD_CTRL) && keycode == ' ') {
|
||||
// Ctrl-Shift-Space is the same as Space
|
||||
*key = ' ';
|
||||
} else if ((modifiers & KMOD_CTRL) && !(modifiers & KMOD_SHIFT) && SDLK_KP_0 <= keycode && keycode <= SDLK_KP_9) {
|
||||
// Control-only numeric *keypad *keys are converted to regular digit *keys
|
||||
*key = keycode - SDLK_KP_0 + '0';
|
||||
} else if ((modifiers & KMOD_CTRL) && !(modifiers & KMOD_SHIFT) && (('0' <= keycode && keycode <= '9') || keycode == '/' || keycode == ' ')) {
|
||||
// Control-only upon 0-9, / and space leaves them unchanged, the
|
||||
// same as unmodified
|
||||
*key = keycode;
|
||||
} else if (keycode == ']') {
|
||||
if (modifiers & KMOD_SHIFT) {
|
||||
// ignore '}' (shift ']')
|
||||
return false;
|
||||
}
|
||||
if (modifiers & KMOD_CTRL) {
|
||||
// Ctrl-] == ASCII: $1D
|
||||
*key = 29;
|
||||
}
|
||||
}// ...else if this is one of the *keys that can't be typed on an Apple ][
|
||||
// *keyboard...
|
||||
else if (*key == 0 || keycode == SDLK_TAB || keycode == SDLK_BACKQUOTE || keycode == '[' || keycode == '\\' || keycode == SDLK_DELETE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Take real-world keystrokes from SDL and filter them to emulate the
|
||||
// Apple ][ or Apple ][ plus keyboard
|
||||
|
||||
void Emulator::dispatchKeypress(const SDL_KeyboardEvent& keyEvent) {
|
||||
if (keyEvent.repeat) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||
SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod;
|
||||
SDL_Scancode scan = keyEvent.keysym.scancode;
|
||||
|
||||
printf("keydown: mod: %04X sym: %08X scan:%04X name:%s\n", mod, sym, scan, SDL_GetKeyName(sym));
|
||||
|
||||
if ((sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) &&
|
||||
!(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) &&
|
||||
!(sym == ']' && mod & KMOD_SHIFT)) {
|
||||
++this->keysDown;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* First handle the key presses that are for commands, as opposed to
|
||||
* simple key-presses that we send to the apple.
|
||||
*/
|
||||
|
||||
if (sym == SDLK_PAUSE) {
|
||||
this->apple2.reset();
|
||||
return;
|
||||
} else if (sym == SDLK_INSERT) {
|
||||
// Feed input from the clipboard to the Apple keyboard
|
||||
std::string s = this->clip.getText();
|
||||
for (unsigned int i = 0; i < s.length(); ++i) {
|
||||
unsigned char key = s[i];
|
||||
// TODO fix pasting line-endings
|
||||
if (key == '\n')
|
||||
key = '\r';
|
||||
if ('a' <= key && key <= 'z') {
|
||||
key -= 32;
|
||||
}
|
||||
this->keypresses.push(key);
|
||||
}
|
||||
return;
|
||||
}// ...else if this is the emulated REPT key on the Apple keyboard...
|
||||
else if (sym == SDLK_F10) {
|
||||
// ...start auto-repeat
|
||||
this->repeat = true;
|
||||
this->rept = CYCLES_PER_REPT;
|
||||
return;
|
||||
}// ...else if the user wants to run at full speed instead of emulating
|
||||
// the Apple's speed...
|
||||
else if (sym == SDLK_F11) {
|
||||
this->fhyper.toggleHyper();
|
||||
this->screenImage.toggleHyperLabel();
|
||||
return;
|
||||
} else if (sym == SDLK_F12) {
|
||||
this->buffered.toggleBuffered();
|
||||
this->screenImage.toggleKdbBufferLabel();
|
||||
return;
|
||||
}// ...else if the user has hit the rocker switch on the back of the Apple...
|
||||
else if (sym == SDLK_F1) {
|
||||
toggleComputerPower();
|
||||
return;
|
||||
}// ...else if the user wants to look at a different video display medium...
|
||||
else if (sym == SDLK_F2) {
|
||||
this->display.cycleType();
|
||||
this->screenImage.cycleDisplayLabel();
|
||||
return;
|
||||
}// ...else if the user wants to switch to/from full screen and an
|
||||
// individual application window...
|
||||
else if (sym == SDLK_F3) {
|
||||
this->screenImage.toggleFullScreen();
|
||||
this->screenImage.drawPower(this->timable == &this->apple2);
|
||||
return;
|
||||
}// ...else if the user wants to switch between the interlaced extension
|
||||
// of the display and the non-interlaced historically correct display...
|
||||
else if (sym == SDLK_F4) {
|
||||
this->display.toggleBleedDown();
|
||||
this->screenImage.toggleFillLinesLabel();
|
||||
return;
|
||||
}// ...else initiate command line entry at the bottom of the emulator window
|
||||
else if (sym == SDLK_F5) {
|
||||
this->command = true;
|
||||
this->screenImage.enterCommandMode();
|
||||
return;
|
||||
}// ...else exit the entire emulation
|
||||
else if (sym == SDLK_END) {
|
||||
this->quit = true;
|
||||
return;
|
||||
}// ...else save a screen shot
|
||||
else if (sym == SDLK_PRINTSCREEN) {
|
||||
this->screenImage.saveBMP();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unsigned char key;
|
||||
const bool sendKey = translateKeysToAppleModernized(sym, mod, &key);
|
||||
|
||||
|
||||
|
||||
if (sendKey) {
|
||||
printf(" sending to apple as ASCII ------------------------------> %02X (%02X)\n", key, key | 0x80);
|
||||
this->keypresses.push(key);
|
||||
this->lastKeyDown = key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Collect and edit a command line typed at the bottom of the emulator
|
||||
// window
|
||||
|
||||
void Emulator::cmdKey(const SDL_KeyboardEvent& keyEvent) {
|
||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||
SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod;
|
||||
unsigned char key = (unsigned char) (sym & 0x7F);
|
||||
|
||||
if (sym == SDLK_RETURN) {
|
||||
processCommand();
|
||||
} else if (sym == SDLK_ESCAPE) {
|
||||
cmdline.erase(cmdline.begin(), cmdline.end());
|
||||
processCommand();
|
||||
} else if (sym == SDLK_BACKSPACE) {
|
||||
if (cmdline.length()) {
|
||||
cmdline.erase(cmdline.end() - 1);
|
||||
this->screenImage.backspaceCommand();
|
||||
}
|
||||
} else if (sym == SDLK_INSERT) {
|
||||
std::string s = this->clip.getText();
|
||||
for (unsigned int i = 0; i < s.length(); ++i) {
|
||||
key = s[i];
|
||||
if (key == '\n' || key == '\r') {
|
||||
processCommand();
|
||||
break;
|
||||
} else {
|
||||
cmdline += key;
|
||||
this->screenImage.addkeyCommand(key);
|
||||
}
|
||||
}
|
||||
} else if (key) {
|
||||
cmdline += key;
|
||||
this->screenImage.addkeyCommand(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Process a command line typed at the bottom of the emulator window
|
||||
|
||||
void Emulator::processCommand() {
|
||||
this->screenImage.exitCommandMode();
|
||||
this->pendingCommandExit = true;
|
||||
|
||||
if (cmdline.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Config::parseLine(cmdline, this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassette);
|
||||
cmdline.erase(cmdline.begin(), cmdline.end());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user