diff --git a/Makefile b/Makefile index 6bee4b5..b666753 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,14 @@ -CXXFLAGS = -g -O -Wall --std=c++11 +INCFLAGS += -I/opt/local/include -I/opt/local/include/freetype2 +CXXFLAGS += $(INCFLAGS) -g -Wall --std=c++11 # -O +LDFLAGS += -L/opt/local/lib +LDLIBS += -lglfw -lglew -lfreeimageplus -lfreetype -framework OpenGL -framework Cocoa -framework IOkit -OBJECTS = apple2e.o keyboard.o dis6502.o fake6502.o +OBJECTS = apple2e.o keyboard.o dis6502.o fake6502.o interface.o all: apple2e apple2e: $(OBJECTS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(INCFLAGS) clean: rm $(OBJECTS) diff --git a/apple2e.cpp b/apple2e.cpp index 1cb6556..fe8670f 100644 --- a/apple2e.cpp +++ b/apple2e.cpp @@ -8,8 +8,10 @@ #include #include #include +#include #include + #include "fake6502.h" const int rom_image_size = 0x3000; @@ -19,6 +21,7 @@ using namespace std; #include "emulator.h" #include "keyboard.h" #include "dis6502.h" +#include "interface.h" const unsigned int DEBUG_ERROR = 0x01; const unsigned int DEBUG_WARN = 0x02; @@ -1514,6 +1517,119 @@ string read_bus_and_disassemble(bus_controller &bus, int pc) int millis_per_slice = 16; +struct key_to_ascii +{ + unsigned char no_shift_no_control; + unsigned char yes_shift_no_control; + unsigned char no_shift_yes_control; + unsigned char yes_shift_yes_control; +}; + +map interface_key_to_apple2e = +{ + {'A', {97, 65, 1, 1}}, + {'B', {98, 66, 2, 2}}, + {'C', {99, 67, 3, 3}}, + {'D', {100, 68, 4, 4}}, + {'E', {101, 69, 5, 5}}, + {'F', {102, 70, 6, 6}}, + {'G', {103, 71, 7, 7}}, + {'H', {104, 72, 8, 8}}, + {'I', {105, 73, 9, 9}}, + {'J', {106, 74, 10, 10}}, + {'K', {107, 75, 11, 11}}, + {'L', {108, 76, 12, 12}}, + {'M', {109, 77, 13, 13}}, + {'N', {110, 78, 14, 14}}, + {'O', {111, 79, 15, 15}}, + {'P', {112, 80, 16, 16}}, + {'Q', {113, 81, 17, 17}}, + {'R', {114, 82, 18, 18}}, + {'S', {115, 83, 19, 19}}, + {'T', {116, 84, 20, 20}}, + {'U', {117, 85, 21, 21}}, + {'V', {118, 86, 22, 22}}, + {'W', {119, 87, 23, 23}}, + {'X', {120, 88, 24, 24}}, + {'Y', {121, 89, 25, 25}}, + {'Z', {122, 90, 26, 26}}, + {'1', {'1', '!', 0, 0}}, + {'2', {'2', '@', 0, 0}}, + {'3', {'3', '#', 0, 0}}, + {'4', {'4', '$', 0, 0}}, + {'5', {'5', '%', 0, 0}}, + {'6', {'6', '^', 0, 0}}, + {'7', {'7', '&', 0, 0}}, + {'8', {'8', '*', 0, 0}}, + {'9', {'9', '(', 0, 0}}, + {'0', {'0', ')', 0, 0}}, + {'-', {'-', '_', 0, 0}}, + {'=', {'=', '+', 0, 0}}, + {'[', {'[', '{', 0, 0}}, + {']', {']', '}', 0, 0}}, + {'\\', {'\\', '|', 0, 0}}, + {';', {';', ':', 0, 0}}, + {'\'', {'\'', '"', 0, 0}}, + {',', {',', '<', 0, 0}}, + {'.', {'.', '>', 0, 0}}, + {'/', {'/', '?', 0, 0}}, + {'`', {'`', '~', 0, 0}}, + {' ', {' ', ' ', 0, 0}}, +}; + +void keyboard_to_mainboard(MAINboard *board) +{ + static bool shift_down = false; + static bool control_down = false; + // skip CAPS for now + + if(interface_event_waiting()) { + event e = interface_dequeue_event(); + if(e.type == event::KEYDOWN) { + if((e.value == event::LEFT_SHIFT) || (e.value == event::RIGHT_SHIFT)) + shift_down = true; + else if((e.value == event::LEFT_CONTROL) || (e.value == event::RIGHT_CONTROL)) + control_down = true; + else if(e.value == event::ENTER) { + board->enqueue_key(141 - 128); + } else if(e.value == event::TAB) { + board->enqueue_key(' '); + } else if(e.value == event::ESCAPE) { + board->enqueue_key(''); + } else if(e.value == event::DELETE) { + board->enqueue_key(255 - 128); + } else if(e.value == event::RIGHT) { + board->enqueue_key(149 - 128); + } else if(e.value == event::LEFT) { + board->enqueue_key(136 - 128); + } else if(e.value == event::DOWN) { + board->enqueue_key(138 - 128); + } else if(e.value == event::UP) { + board->enqueue_key(139 - 128); + } else { + auto it = interface_key_to_apple2e.find(e.value); + if(it != interface_key_to_apple2e.end()) { + const key_to_ascii& k = (*it).second; + if(!shift_down && !control_down) + board->enqueue_key(k.no_shift_no_control); + else if(shift_down && !control_down) + board->enqueue_key(k.yes_shift_no_control); + else if(!shift_down && control_down) + board->enqueue_key(k.no_shift_yes_control); + else if(shift_down && control_down) + board->enqueue_key(k.yes_shift_yes_control); + } + } + } else if(e.type == event::KEYUP) { + if((e.value == event::LEFT_SHIFT) || (e.value == event::RIGHT_SHIFT)) + shift_down = false; + else if((e.value == event::LEFT_CONTROL) || (e.value == event::RIGHT_CONTROL)) + control_down = false; + } + } +} + + int main(int argc, char **argv) { char *progname = argv[0]; @@ -1579,6 +1695,8 @@ int main(int argc, char **argv) if(use_fake6502) reset6502(); + interface_start(); + while(1) { if(!debugging) { poll_keyboard(); @@ -1586,6 +1704,8 @@ int main(int argc, char **argv) char key; bool have_key = peek_key(&key); + keyboard_to_mainboard(mainboard); + if(have_key) { if(key == '') { debugging = true; @@ -1613,6 +1733,7 @@ int main(int argc, char **argv) textport_display(); textport_changed = false; } + interface_iterate(); chrono::time_point now; auto elapsed_millis = chrono::duration_cast(now - then); @@ -1662,6 +1783,9 @@ int main(int argc, char **argv) textport_display(); textport_changed = false; } + interface_iterate(); } } + + interface_shutdown(); } diff --git a/genkeys.py b/genkeys.py new file mode 100644 index 0000000..5aae12c --- /dev/null +++ b/genkeys.py @@ -0,0 +1,9 @@ +for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": + a = ord(c) + print " {'%c', {%d, %d, %d, %d}}," % (a, a + 32, a, a - 64, a - 64) + +for c in "1234567890-=[]\;',./`": + a = ord(c) + print " {'%c', {'%c', '?', 0, 0}}," % (a, a) + + # {'A', {'a', 'A', '', ''}} diff --git a/interface.cpp b/interface.cpp new file mode 100644 index 0000000..a667141 --- /dev/null +++ b/interface.cpp @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include + +#include "interface.h" + +using namespace std; + +static int gWindowWidth; +static int gWindowHeight; + +// to handle https://github.com/glfw/glfw/issues/161 +static double gMotionReported = false; + +static double gOldMouseX, gOldMouseY; +static int gButtonPressed = -1; + +deque event_queue; + +bool interface_event_waiting() +{ + return event_queue.size() > 0; +} + +event interface_dequeue_event() +{ + if(interface_event_waiting()) { + event e = event_queue.front(); + event_queue.pop_back(); + return e; + } else + return {event::NONE, 0}; +} + +using namespace std; + +float aspectRatio = 1; + +void initialize_gl(void) +{ + glClearColor(0, 0, 0, 1); +} + +#define CHECK_OPENGL(l) {int _glerr ; if((_glerr = glGetError()) != GL_NO_ERROR) printf("GL Error: %04X at %d\n", _glerr, l); } + +static void redraw(GLFWwindow *window) +{ + CHECK_OPENGL(__LINE__); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, gWindowWidth - 1, 0, gWindowHeight - 1, -1, 1); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glPushMatrix(); + + glPopMatrix(); + CHECK_OPENGL(__LINE__); +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "GLFW: %s\n", description); +} + +static void key(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + printf("key %d(%c), scancode %d, action %d, mods %02X\n", key, isprint(key) ? key : '?', scancode, action, mods); + if(action == GLFW_PRESS) { + event_queue.push_back({event::KEYDOWN, key}); + } else if(action == GLFW_RELEASE) { + event_queue.push_back({event::KEYUP, key}); + } +} + +static void resize(GLFWwindow *window, int x, int y) +{ + glfwGetFramebufferSize(window, &gWindowWidth, &gWindowHeight); + glViewport(0, 0, gWindowWidth, gWindowWidth); +} + +static void button(GLFWwindow *window, int b, int action, int mods) +{ + double x, y; + glfwGetCursorPos(window, &x, &y); + + if(b == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) { + gButtonPressed = 1; + gOldMouseX = x; + gOldMouseY = y; + } else { + gButtonPressed = -1; + } +} + +static void motion(GLFWwindow *window, double x, double y) +{ + // to handle https://github.com/glfw/glfw/issues/161 + // If no motion has been reported yet, we catch the first motion + // reported and store the current location + if(!gMotionReported) { + gMotionReported = true; + gOldMouseX = x; + gOldMouseY = y; + } + + double dx, dy; + + dx = x - gOldMouseX; + dy = y - gOldMouseY; + + gOldMouseX = x; + gOldMouseY = y; + + if(gButtonPressed == 1) { + // mouse does nothing + } +} + +static void scroll(GLFWwindow *window, double dx, double dy) +{ +} + +static GLFWwindow* window; + +void interface_start() +{ + glfwSetErrorCallback(error_callback); + + if(!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_SAMPLES, 4); + window = glfwCreateWindow(gWindowWidth = 280 * 4, gWindowHeight = 192 * 4, "Apple //e", NULL, NULL); + if (!window) { + glfwTerminate(); + fprintf(stdout, "Couldn't open main window\n"); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + + GLenum err = glewInit(); + if (GLEW_OK != err) { + fprintf(stderr, "Error: %s\n", glewGetErrorString(err)); + exit(EXIT_FAILURE); + } + fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); + + initialize_gl(); + + glfwSetKeyCallback(window, key); + glfwSetMouseButtonCallback(window, button); + glfwSetCursorPosCallback(window, motion); + glfwSetScrollCallback(window, scroll); + glfwSetFramebufferSizeCallback(window, resize); + glfwSetWindowRefreshCallback(window, redraw); + +} + +void interface_iterate() +{ + if(glfwWindowShouldClose(window)) { + // set to quit + } + + redraw(window); + + glfwSwapBuffers(window); + + glfwPollEvents(); +} + +void interface_shutdown() +{ + glfwTerminate(); +} diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..8ec945a --- /dev/null +++ b/interface.h @@ -0,0 +1,39 @@ +#include + +struct event { + enum EventType {NONE, KEYDOWN, KEYUP, RESET, REBOOT} type; + static const int LEFT_SHIFT = 340; + static const int LEFT_CONTROL = 341; + static const int LEFT_ALT = 342; + static const int LEFT_SUPER = 343; + static const int RIGHT_SHIFT = 344; + static const int RIGHT_CONTROL = 345; + static const int RIGHT_ALT = 346; + static const int RIGHT_SUPER = 347; + static const int ESCAPE = 256; + static const int ENTER = 257; + static const int TAB = 258; + static const int BACKSPACE = 259; + static const int INSERT = 260; + static const int DELETE = 261; + static const int RIGHT = 262; + static const int LEFT = 263; + static const int DOWN = 264; + static const int UP = 265; + static const int PAGE_UP = 266; + static const int PAGE_DOWN = 267; + static const int HOME = 268; + static const int END = 269; + static const int CAPS_LOCK = 280; + int value; + event(EventType type_, int value_) : + type(type_), + value(value_) + {} +}; + +bool interface_event_waiting(); +event interface_dequeue_event(); +void interface_start(); +void interface_iterate(); +void interface_shutdown();