Browse Source

emul/hw/sms: add A/B ports and a Genesis pad

This makes the emulator suitable to run the base SMS recipe.
pull/94/head
Virgil Dupras 4 years ago
parent
commit
e1e0676191
8 changed files with 238 additions and 2 deletions
  1. +7
    -0
      emul/emul.h
  2. +1
    -1
      emul/hw/sms/Makefile
  3. +25
    -0
      emul/hw/sms/README.md
  4. +30
    -0
      emul/hw/sms/pad.c
  5. +23
    -0
      emul/hw/sms/pad.h
  6. +70
    -0
      emul/hw/sms/port.c
  7. +21
    -0
      emul/hw/sms/port.h
  8. +61
    -1
      emul/hw/sms/sms.c

+ 7
- 0
emul/emul.h View File

@@ -1,3 +1,4 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "libz80/z80.h"
@@ -19,6 +20,12 @@ typedef struct {
IOWR iowr[0x100];
} Machine;

typedef enum {
TRI_HIGH,
TRI_LOW,
TRI_HIGHZ
} Tristate;

Machine* emul_init();
bool emul_step();
bool emul_steps(unsigned int steps);


+ 1
- 1
emul/hw/sms/Makefile View File

@@ -1,5 +1,5 @@
EXTOBJS = ../../emul.o ../../libz80/libz80.o
OBJS = sms.o vdp.o
OBJS = sms.o vdp.o port.o pad.o
TARGET = sms
CFLAGS += `pkg-config --cflags xcb`
LDFLAGS += `pkg-config --libs xcb`


+ 25
- 0
emul/hw/sms/README.md View File

@@ -0,0 +1,25 @@
# Sega Master System emulator

This emulates a Sega Master system with a monochrome screen and a Genesis pad
hooked to port A.

## Build

You need `xcb` and `pkg-config` to build this. If you have them, run `make`.
You'll get a `sms` executable.

## Usage

Launch the emulator with `./sms /path/to/rom` (you can use the binary from the
`sms` recipe.

This will show a window with the screen's content on it. The mappings to the
pad are:

* Arrows
* Z --> A
* X --> B
* C --> C
* S --> Start

Press ESC to quit.

+ 30
- 0
emul/hw/sms/pad.c View File

@@ -0,0 +1,30 @@
#include "pad.h"

void pad_init(Pad *pad, Tristate *TH)
{
pad->pressed = 0xff;
pad->TH = TH;
}

void pad_setbtn(Pad *pad, PAD_BTN btn, bool pressed)
{
if (pressed) {
pad->pressed &= ~(1 << btn);
} else {
pad->pressed |= (1 << btn);
}
}

uint8_t pad_rd(Pad *pad)
{
uint8_t res;
if (*pad->TH == TRI_LOW) { // TH selected
// A and START shifted in from bits 7:6 into 5:4
res = (pad->pressed & 0xf) | ((pad->pressed & 0xc0) >> 2);
} else {
res = pad->pressed & 0x3f;
}
// Bits 7:6 are always high
res |= 0b11000000;
return res;
}

+ 23
- 0
emul/hw/sms/pad.h View File

@@ -0,0 +1,23 @@
#include <stdint.h>
#include <stdbool.h>
#include "port.h"

typedef enum {
PAD_BTN_UP = 0,
PAD_BTN_DOWN = 1,
PAD_BTN_LEFT = 2,
PAD_BTN_RIGHT = 3,
PAD_BTN_B = 4,
PAD_BTN_C = 5,
PAD_BTN_A = 6,
PAD_BTN_START = 7
} PAD_BTN;

typedef struct {
uint8_t pressed;
Tristate *TH;
} Pad;

void pad_init(Pad *pad, Tristate *TH);
void pad_setbtn(Pad *pad, PAD_BTN btn, bool pressed);
uint8_t pad_rd(Pad *pad);

+ 70
- 0
emul/hw/sms/port.c View File

@@ -0,0 +1,70 @@
#include "port.h"

void ports_init(Ports *ports)
{
ports->ctl = 0xff;
ports->TRA = TRI_HIGHZ;
ports->THA = TRI_HIGHZ;
ports->TRB = TRI_HIGHZ;
ports->THB = TRI_HIGHZ;
}

uint8_t ports_ctl_rd(Ports *ports)
{
return ports->ctl;
}

void ports_ctl_wr(Ports *ports, uint8_t val)
{
ports->ctl = val;
ports->TRA = TRI_HIGHZ;
ports->THA = TRI_HIGHZ;
ports->TRB = TRI_HIGHZ;
ports->THB = TRI_HIGHZ;
if ((val & 0x01) == 0) {
ports->TRA = ((val & 0x10) == 0) ? TRI_LOW : TRI_HIGH;
}
if ((val & 0x02) == 0) {
ports->THA = ((val & 0x20) == 0) ? TRI_LOW : TRI_HIGH;
}
if ((val & 0x04) == 0) {
ports->TRB = ((val & 0x40) == 0) ? TRI_LOW : TRI_HIGH;
}
if ((val & 0x08) == 0) {
ports->THB = ((val & 0x80) == 0) ? TRI_LOW : TRI_HIGH;
}
}

uint8_t ports_A_rd(Ports *ports)
{
// Bits 7:6 are port B's Down/Up
// Bits 5:0 are port A's TR/TL/R/L/D/U
uint8_t res = 0xff;
if (ports->portA_rd != NULL) {
res &= ports->portA_rd() | 0b11000000;
}
if (ports->portB_rd != NULL) {
res &= (ports->portB_rd() << 6) | 0b00111111;
}
return res;

}

uint8_t ports_B_rd(Ports *ports)
{
// Bit 7: Port B's TH
// Bit 6: Port A's TH
// Bit 5: unused
// Bit 4: unused (reset button)
// Bits 3:0 are port B's TR/TL/R/L
uint8_t res = 0xff;
if (ports->portA_rd != NULL) {
res &= ports->portA_rd() | 0b10111111;
}
if (ports->portB_rd != NULL) {
uint8_t portb = ports->portB_rd();
res &= (portb << 1) | 0b01111111; // TH
res &= (portb >> 2) | 0b11110000; // TR/TL/R/L
}
return res;
}

+ 21
- 0
emul/hw/sms/port.h View File

@@ -0,0 +1,21 @@
#pragma once
#include "../../emul.h"

// Each port is a bitmask of each pin's status. 1 means high.
// From Bit 0 to 6: up, down, left, right, TL, TR, TH

typedef struct {
uint8_t ctl;
Tristate TRA;
Tristate THA;
Tristate TRB;
Tristate THB;
IORD portA_rd;
IORD portB_rd;
} Ports;

void ports_init(Ports *ports);
uint8_t ports_ctl_rd(Ports *ports);
void ports_ctl_wr(Ports *ports, uint8_t val);
uint8_t ports_A_rd(Ports *ports);
uint8_t ports_B_rd(Ports *ports);

+ 61
- 1
emul/hw/sms/sms.c View File

@@ -6,10 +6,15 @@

#include "../../emul.h"
#include "vdp.h"
#include "port.h"
#include "pad.h"

#define RAMSTART 0xc000
#define VDP_CMD_PORT 0xbf
#define VDP_DATA_PORT 0xbe
#define PORTS_CTL_PORT 0x3f
#define PORTS_IO1_PORT 0xdc
#define PORTS_IO2_PORT 0xdd
#define MAX_ROMSIZE 0x8000

static xcb_connection_t *conn;
@@ -26,6 +31,8 @@ static xcb_rectangle_t rectangles[VDP_SCREENW*VDP_SCREENH];
static Machine *m;
static VDP vdp;
static bool vdp_changed;
static Ports ports;
static Pad pad;

static uint8_t iord_vdp_cmd()
{
@@ -37,6 +44,21 @@ static uint8_t iord_vdp_data()
return vdp_data_rd(&vdp);
}

static uint8_t iord_ports_io1()
{
return ports_A_rd(&ports);
}

static uint8_t iord_ports_io2()
{
return ports_B_rd(&ports);
}

static uint8_t iord_pad()
{
return pad_rd(&pad);
}

static void iowr_vdp_cmd(uint8_t val)
{
vdp_cmd_wr(&vdp, val);
@@ -48,6 +70,11 @@ static void iowr_vdp_data(uint8_t val)
vdp_data_wr(&vdp, val);
}

static void iowr_ports_ctl(uint8_t val)
{
ports_ctl_wr(&ports, val);
}

void create_window()
{
uint32_t mask;
@@ -150,7 +177,34 @@ void event_loop()
case XCB_KEY_RELEASE:
case XCB_KEY_PRESS: {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
if (ev->detail == 0x09) return;
bool ispressed = e->response_type == XCB_KEY_PRESS;
switch (ev->detail) {
case 0x09: return; // ESC
case 0x27: // S
pad_setbtn(&pad, PAD_BTN_START, ispressed);
break;
case 0x34: // Z
pad_setbtn(&pad, PAD_BTN_A, ispressed);
break;
case 0x35: // X
pad_setbtn(&pad, PAD_BTN_B, ispressed);
break;
case 0x36: // C
pad_setbtn(&pad, PAD_BTN_C, ispressed);
break;
case 0x62:
pad_setbtn(&pad, PAD_BTN_UP, ispressed);
break;
case 0x64:
pad_setbtn(&pad, PAD_BTN_LEFT, ispressed);
break;
case 0x66:
pad_setbtn(&pad, PAD_BTN_RIGHT, ispressed);
break;
case 0x68:
pad_setbtn(&pad, PAD_BTN_DOWN, ispressed);
break;
}
break;
}
case XCB_EXPOSE: {
@@ -190,10 +244,16 @@ int main(int argc, char *argv[])
}
vdp_init(&vdp);
vdp_changed = false;
ports_init(&ports);
ports.portA_rd = iord_pad;
pad_init(&pad, &ports.THA);
m->iord[VDP_CMD_PORT] = iord_vdp_cmd;
m->iord[VDP_DATA_PORT] = iord_vdp_data;
m->iord[PORTS_IO1_PORT] = iord_ports_io1;
m->iord[PORTS_IO2_PORT] = iord_ports_io2;
m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd;
m->iowr[VDP_DATA_PORT] = iowr_vdp_data;
m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl;
conn = xcb_connect(NULL, NULL);
screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
create_window();


Loading…
Cancel
Save