mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-27 12:28:06 +11:00
emul/sms: add PS/2 keyboard emulation
This commit is contained in:
parent
cc8068f8ab
commit
8cecd54410
@ -193,3 +193,6 @@ void emul_printdebug()
|
|||||||
fprintf(stderr, "Min SP: %04x\n", m.minsp);
|
fprintf(stderr, "Min SP: %04x\n", m.minsp);
|
||||||
fprintf(stderr, "Max IX: %04x\n", m.maxix);
|
fprintf(stderr, "Max IX: %04x\n", m.maxix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte iord_noop() { return 0; }
|
||||||
|
void iowr_noop(byte val) {}
|
||||||
|
@ -37,3 +37,6 @@ void emul_trace(ushort addr);
|
|||||||
void emul_memdump();
|
void emul_memdump();
|
||||||
void emul_debugstr(char *s);
|
void emul_debugstr(char *s);
|
||||||
void emul_printdebug();
|
void emul_printdebug();
|
||||||
|
// use when a port is a NOOP, but it's not an error to access it.
|
||||||
|
byte iord_noop();
|
||||||
|
void iowr_noop(byte val);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
EXTOBJS = ../../emul.o ../../libz80/libz80.o
|
EXTOBJS = ../../emul.o ../../libz80/libz80.o
|
||||||
OBJS = sms.o vdp.o port.o pad.o
|
OBJS = sms.o vdp.o port.o pad.o kbd.o
|
||||||
TARGET = sms
|
TARGET = sms
|
||||||
CFLAGS += `pkg-config --cflags xcb`
|
CFLAGS += `pkg-config --cflags xcb`
|
||||||
LDFLAGS += `pkg-config --libs xcb`
|
LDFLAGS += `pkg-config --libs xcb`
|
||||||
|
@ -25,4 +25,9 @@ pad are:
|
|||||||
* K --> C
|
* K --> C
|
||||||
* L --> Start
|
* L --> Start
|
||||||
|
|
||||||
|
If your ROM is configured with PS/2 keyboard input, run this emulator with the
|
||||||
|
`-k` flag to replace SMS pad emulation with keyboard emulation.
|
||||||
|
|
||||||
|
In both cases (pad or keyboard), only port A emulation is supported.
|
||||||
|
|
||||||
Press ESC to quit.
|
Press ESC to quit.
|
||||||
|
101
emul/hw/sms/kbd.c
Normal file
101
emul/hw/sms/kbd.c
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include "kbd.h"
|
||||||
|
|
||||||
|
void kbd_init(Kbd *kbd, Tristate *TH)
|
||||||
|
{
|
||||||
|
kbd->kc = 0;
|
||||||
|
kbd->breaking = false;
|
||||||
|
kbd->TH = TH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kbd_pressshift(Kbd *kbd, bool ispressed)
|
||||||
|
{
|
||||||
|
kbd->kc = 0x12; // shift keycode
|
||||||
|
kbd->breaking = !ispressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kbd_presskey(Kbd *kbd, uint8_t key)
|
||||||
|
{
|
||||||
|
kbd->kc = 0;
|
||||||
|
switch (key) {
|
||||||
|
case '\t': kbd->kc = 0xd; break;
|
||||||
|
case '`': kbd->kc = 0xe; break;
|
||||||
|
case 'q': kbd->kc = 0x15; break;
|
||||||
|
case '1': kbd->kc = 0x16; break;
|
||||||
|
case 'z': kbd->kc = 0x1a; break;
|
||||||
|
case 's': kbd->kc = 0x1b; break;
|
||||||
|
case 'a': kbd->kc = 0x1c; break;
|
||||||
|
case 'w': kbd->kc = 0x1d; break;
|
||||||
|
case '2': kbd->kc = 0x1e; break;
|
||||||
|
case 'c': kbd->kc = 0x21; break;
|
||||||
|
case 'x': kbd->kc = 0x22; break;
|
||||||
|
case 'd': kbd->kc = 0x23; break;
|
||||||
|
case 'e': kbd->kc = 0x24; break;
|
||||||
|
case '4': kbd->kc = 0x25; break;
|
||||||
|
case '3': kbd->kc = 0x26; break;
|
||||||
|
case ' ': kbd->kc = 0x29; break;
|
||||||
|
case 'v': kbd->kc = 0x2a; break;
|
||||||
|
case 'f': kbd->kc = 0x2b; break;
|
||||||
|
case 't': kbd->kc = 0x2c; break;
|
||||||
|
case 'r': kbd->kc = 0x2d; break;
|
||||||
|
case '5': kbd->kc = 0x2e; break;
|
||||||
|
case 'n': kbd->kc = 0x31; break;
|
||||||
|
case 'b': kbd->kc = 0x32; break;
|
||||||
|
case 'h': kbd->kc = 0x33; break;
|
||||||
|
case 'g': kbd->kc = 0x34; break;
|
||||||
|
case 'y': kbd->kc = 0x35; break;
|
||||||
|
case '6': kbd->kc = 0x36; break;
|
||||||
|
case 'm': kbd->kc = 0x3a; break;
|
||||||
|
case 'j': kbd->kc = 0x3b; break;
|
||||||
|
case 'u': kbd->kc = 0x3c; break;
|
||||||
|
case '7': kbd->kc = 0x3d; break;
|
||||||
|
case '8': kbd->kc = 0x3e; break;
|
||||||
|
case ',': kbd->kc = 0x41; break;
|
||||||
|
case 'k': kbd->kc = 0x42; break;
|
||||||
|
case 'i': kbd->kc = 0x43; break;
|
||||||
|
case 'o': kbd->kc = 0x44; break;
|
||||||
|
case '0': kbd->kc = 0x45; break;
|
||||||
|
case '9': kbd->kc = 0x46; break;
|
||||||
|
case '.': kbd->kc = 0x49; break;
|
||||||
|
case '/': kbd->kc = 0x4a; break;
|
||||||
|
case 'l': kbd->kc = 0x4b; break;
|
||||||
|
case ';': kbd->kc = 0x4c; break;
|
||||||
|
case 'p': kbd->kc = 0x4d; break;
|
||||||
|
case '-': kbd->kc = 0x4e; break;
|
||||||
|
case '\'': kbd->kc = 0x52; break;
|
||||||
|
case '[': kbd->kc = 0x54; break;
|
||||||
|
case '=': kbd->kc = 0x55; break;
|
||||||
|
case '\r': kbd->kc = 0x5a; break;
|
||||||
|
case ']': kbd->kc = 0x5b; break;
|
||||||
|
case '\\': kbd->kc = 0x5d; break;
|
||||||
|
case '\b': kbd->kc = 0x66; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t kbd_rd(Kbd *kbd)
|
||||||
|
{
|
||||||
|
// There are 3 modes for reading the kbd:
|
||||||
|
// 1. TH = highz: we're polling the the existence of a KC
|
||||||
|
// 2. TH = low: return low nibble
|
||||||
|
// 3. TH = high: return high nibble and reset KC
|
||||||
|
uint8_t res = 0;
|
||||||
|
uint8_t tosend = kbd->kc;
|
||||||
|
if (kbd->breaking) {
|
||||||
|
tosend = 0xf0;
|
||||||
|
}
|
||||||
|
if (*kbd->TH == TRI_HIGHZ) { // polling
|
||||||
|
if (!kbd->kc) {
|
||||||
|
res = 0x10; // indicate KC absence;
|
||||||
|
}
|
||||||
|
} else if (*kbd->TH == TRI_LOW) { // TH selected
|
||||||
|
res = tosend & 0xf;
|
||||||
|
} else {
|
||||||
|
res = tosend >> 4;
|
||||||
|
if (kbd->breaking) {
|
||||||
|
kbd->breaking = false;
|
||||||
|
} else {
|
||||||
|
kbd->kc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
16
emul/hw/sms/kbd.h
Normal file
16
emul/hw/sms/kbd.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
#define KBD_BUFSZ 0x10
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t kc; // last keycode to be pressed. 0 means none.
|
||||||
|
bool breaking; // whether we should send 0xf0 before kc
|
||||||
|
Tristate *TH;
|
||||||
|
} Kbd;
|
||||||
|
|
||||||
|
void kbd_init(Kbd *kbd, Tristate *TH);
|
||||||
|
void kbd_pressshift(Kbd *kbd, bool ispressed);
|
||||||
|
void kbd_presskey(Kbd *kbd, uint8_t keycode);
|
||||||
|
uint8_t kbd_rd(Kbd *kbd);
|
@ -11,6 +11,7 @@
|
|||||||
#include "vdp.h"
|
#include "vdp.h"
|
||||||
#include "port.h"
|
#include "port.h"
|
||||||
#include "pad.h"
|
#include "pad.h"
|
||||||
|
#include "kbd.h"
|
||||||
|
|
||||||
#define RAMSTART 0xc000
|
#define RAMSTART 0xc000
|
||||||
#define VDP_CMD_PORT 0xbf
|
#define VDP_CMD_PORT 0xbf
|
||||||
@ -36,6 +37,8 @@ static VDP vdp;
|
|||||||
static bool vdp_changed;
|
static bool vdp_changed;
|
||||||
static Ports ports;
|
static Ports ports;
|
||||||
static Pad pad;
|
static Pad pad;
|
||||||
|
static Kbd kbd;
|
||||||
|
static bool use_kbd = false;
|
||||||
|
|
||||||
static uint8_t iord_vdp_cmd()
|
static uint8_t iord_vdp_cmd()
|
||||||
{
|
{
|
||||||
@ -62,6 +65,11 @@ static uint8_t iord_pad()
|
|||||||
return pad_rd(&pad);
|
return pad_rd(&pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t iord_kbd()
|
||||||
|
{
|
||||||
|
return kbd_rd(&kbd);
|
||||||
|
}
|
||||||
|
|
||||||
static void iowr_vdp_cmd(uint8_t val)
|
static void iowr_vdp_cmd(uint8_t val)
|
||||||
{
|
{
|
||||||
vdp_cmd_wr(&vdp, val);
|
vdp_cmd_wr(&vdp, val);
|
||||||
@ -159,38 +167,49 @@ void draw_pixels()
|
|||||||
static bool _handle_keypress(xcb_generic_event_t *e)
|
static bool _handle_keypress(xcb_generic_event_t *e)
|
||||||
{
|
{
|
||||||
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
|
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
|
||||||
|
if (ev->detail == 0x09) { // ESC
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool ispressed = e->response_type == XCB_KEY_PRESS;
|
bool ispressed = e->response_type == XCB_KEY_PRESS;
|
||||||
// change keycode into symbol
|
// change keycode into symbol
|
||||||
xcb_get_keyboard_mapping_reply_t* km = xcb_get_keyboard_mapping_reply(
|
xcb_get_keyboard_mapping_reply_t* km = xcb_get_keyboard_mapping_reply(
|
||||||
conn, xcb_get_keyboard_mapping(conn, ev->detail, 1), NULL);
|
conn, xcb_get_keyboard_mapping(conn, ev->detail, 1), NULL);
|
||||||
if (km->length) {
|
if (km->length) {
|
||||||
xcb_keysym_t* keysyms = (xcb_keysym_t*)(km + 1);
|
xcb_keysym_t* keysyms = (xcb_keysym_t*)(km + 1);
|
||||||
switch (keysyms[0]) {
|
if (use_kbd) {
|
||||||
case XK_Escape: free(km); return true;
|
if ((keysyms[0] == XK_Shift_L) || (keysyms[0] == XK_Shift_R)) {
|
||||||
case 'w':
|
kbd_pressshift(&kbd, ispressed);
|
||||||
pad_setbtn(&pad, PAD_BTN_UP, ispressed);
|
} else if (ispressed) {
|
||||||
break;
|
fprintf(stderr, "pressing %x\n", keysyms[0]);
|
||||||
case 'a':
|
kbd_presskey(&kbd, keysyms[0]);
|
||||||
pad_setbtn(&pad, PAD_BTN_LEFT, ispressed);
|
}
|
||||||
break;
|
} else { // pad
|
||||||
case 's':
|
switch (keysyms[0]) {
|
||||||
pad_setbtn(&pad, PAD_BTN_DOWN, ispressed);
|
case 'w':
|
||||||
break;
|
pad_setbtn(&pad, PAD_BTN_UP, ispressed);
|
||||||
case 'd':
|
break;
|
||||||
pad_setbtn(&pad, PAD_BTN_RIGHT, ispressed);
|
case 'a':
|
||||||
break;
|
pad_setbtn(&pad, PAD_BTN_LEFT, ispressed);
|
||||||
case 'h':
|
break;
|
||||||
pad_setbtn(&pad, PAD_BTN_A, ispressed);
|
case 's':
|
||||||
break;
|
pad_setbtn(&pad, PAD_BTN_DOWN, ispressed);
|
||||||
case 'j':
|
break;
|
||||||
pad_setbtn(&pad, PAD_BTN_B, ispressed);
|
case 'd':
|
||||||
break;
|
pad_setbtn(&pad, PAD_BTN_RIGHT, ispressed);
|
||||||
case 'k':
|
break;
|
||||||
pad_setbtn(&pad, PAD_BTN_C, ispressed);
|
case 'h':
|
||||||
break;
|
pad_setbtn(&pad, PAD_BTN_A, ispressed);
|
||||||
case 'l':
|
break;
|
||||||
pad_setbtn(&pad, PAD_BTN_START, ispressed);
|
case 'j':
|
||||||
break;
|
pad_setbtn(&pad, PAD_BTN_B, ispressed);
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
pad_setbtn(&pad, PAD_BTN_C, ispressed);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
pad_setbtn(&pad, PAD_BTN_START, ispressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(km);
|
free(km);
|
||||||
@ -242,13 +261,30 @@ void event_loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: ./sms [-k] /path/to/rom\n");
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc != 2) {
|
if (argc < 2) {
|
||||||
fprintf(stderr, "Usage: ./sms /path/to/rom\n");
|
usage();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
FILE *fp = fopen(argv[1], "r");
|
int ch;
|
||||||
|
while ((ch = getopt(argc, argv, "k")) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'k':
|
||||||
|
use_kbd = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (optind != argc-1) {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
FILE *fp = fopen(argv[optind], "r");
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
fprintf(stderr, "Can't open %s\n", argv[1]);
|
fprintf(stderr, "Can't open %s\n", argv[1]);
|
||||||
return 1;
|
return 1;
|
||||||
@ -268,12 +304,18 @@ int main(int argc, char *argv[])
|
|||||||
vdp_init(&vdp);
|
vdp_init(&vdp);
|
||||||
vdp_changed = false;
|
vdp_changed = false;
|
||||||
ports_init(&ports);
|
ports_init(&ports);
|
||||||
ports.portA_rd = iord_pad;
|
|
||||||
pad_init(&pad, &ports.THA);
|
pad_init(&pad, &ports.THA);
|
||||||
|
kbd_init(&kbd, &ports.THA);
|
||||||
|
if (use_kbd) {
|
||||||
|
ports.portA_rd = iord_kbd;
|
||||||
|
} else {
|
||||||
|
ports.portA_rd = iord_pad;
|
||||||
|
}
|
||||||
m->iord[VDP_CMD_PORT] = iord_vdp_cmd;
|
m->iord[VDP_CMD_PORT] = iord_vdp_cmd;
|
||||||
m->iord[VDP_DATA_PORT] = iord_vdp_data;
|
m->iord[VDP_DATA_PORT] = iord_vdp_data;
|
||||||
m->iord[PORTS_IO1_PORT] = iord_ports_io1;
|
m->iord[PORTS_IO1_PORT] = iord_ports_io1;
|
||||||
m->iord[PORTS_IO2_PORT] = iord_ports_io2;
|
m->iord[PORTS_IO2_PORT] = iord_ports_io2;
|
||||||
|
m->iord[PORTS_CTL_PORT] = iord_noop;
|
||||||
m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd;
|
m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd;
|
||||||
m->iowr[VDP_DATA_PORT] = iowr_vdp_data;
|
m->iowr[VDP_DATA_PORT] = iowr_vdp_data;
|
||||||
m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl;
|
m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl;
|
||||||
|
@ -105,7 +105,7 @@ to TH (and also the A/B on the '157). Q is hooked to PB0 and TL.
|
|||||||
We start with the base recipe and add a few things:
|
We start with the base recipe and add a few things:
|
||||||
|
|
||||||
1. at the top: `SYSVARS 0x72 + CONSTANT PS2_MEM`
|
1. at the top: `SYSVARS 0x72 + CONSTANT PS2_MEM`
|
||||||
2. After VDP load: `641 LOAD : (ps2kc) (ps2kcB) ;` (that binds us to port B)
|
2. After VDP load: `621 LOAD : (ps2kc) (ps2kcB) ;` (that binds us to port B)
|
||||||
3. Right after: `411 414 LOADR` (that gives us `(key)`)
|
3. Right after: `411 414 LOADR` (that gives us `(key)`)
|
||||||
4. After `VDP$`: `PS2$`.
|
4. After `VDP$`: `PS2$`.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user