Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ When starting `x16emu` without arguments, it will pick up the system ROM (`rom.b
* `-sound <device>` can be used to specify the output sound device. If 'none', no audio is generated.
* `-abufs` can be used to specify the number of audio buffers (defaults to 8 when using the SD card, 32 when using HostFS). If you're experiencing stuttering in the audio, try increasing this number. This will result in additional audio latency though.
* `-via2` installs the second VIA chip expansion at $9F10.
* `-via2-socket <socket>` Use a socket to emulate VIA2 I/O. Currently only UNIX / POSIX are supported. The emulator opens this socket as a client, so it must be set up in advance. The -via2 option must be specified along with this option. See [Using -via2-socket](#via2-socket) for sample server and client code.
* `-midline-effects` enables mid-scanline raster effects at the cost of vastly increased host CPU usage.
* `-mhz <integer>` sets the emulated CPU's speed. Range is from 1-40. This option is mainly for testing and benchmarking.
* `-enable-ym2151-irq` connects the YM2151's IRQ pin to the system's IRQ line with a modest increase in host CPU usage.
Expand Down Expand Up @@ -450,6 +451,113 @@ When `-debug` is selected the STP instruction (opcode $DB) will break into the d

Keyboard routines only work when the emulator is running normally. Single stepping through keyboard code will not work at present.

<a name="via2-socket"></a>
Using -via2-socket
------------------

Reads from and writes to both `$9F10` and `$9F11` (VIA ports A and B) are forwarded to the socket.

There are no restrictions to the protocols used except that a value of `255` is reserved for "no data yet" when reading in the emulator.

The following example demonstrates a simple bidirectional chat interface implemented using the `-via2-socket` option. It uses a protocol where `0` indicates the end of a line. Note the special handling of `255` in the client.

### Sample server (Python)

Save as `via2.py`:

```python
#!/usr/bin/env python3
import os
import socket
import subprocess
import sys

SOCKET_PATH = "/tmp/via2.sock"
CHAT_EOL = b'\x00'

# Remove old socket if present
try:
os.unlink(SOCKET_PATH)
except FileNotFoundError:
pass

# Create UNIX domain socket
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(SOCKET_PATH)
server.listen(1)

print("Waiting for emulator...")

# Launch emulator AFTER socket is ready
subprocess.Popen([
"./x16emu",
"-scale", "2",
"-via2",
"-via2-socket", SOCKET_PATH,
"-bas", "VIA2.BAS",
"-run"
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

# Accept connection
conn, _ = server.accept()
print("Emulator connected.")

try:
while True:
# ---- RECEIVE STRING ----
buf = bytearray()
while True:
b = conn.recv(1)
if not b:
raise ConnectionError("Socket closed")
if b == CHAT_EOL:
break
buf += b

text = buf.decode(errors="replace")
print("> " + text)

if text == "":
break

# ---- SEND RESPONSE ----
reply = input("MESSAGE? ").upper().encode() + CHAT_EOL
conn.sendall(reply)

finally:
try:
conn.close()
except Exception:
pass
server.close()
try:
os.unlink(SOCKET_PATH)
except FileNotFoundError:
pass
```

### Sample client (Commander X16 BASIC)

Save as `VIA2.BAS`:

```basic
100 REM VIA2 ACTS AS A BYTE-SERIAL DEVICE VIA SOCKET BRIDGE
110 T$ = ""
120 INPUT "MESSAGE (EMPTY LINE TO QUIT)"; T$
130 REM WRITE T$ TO VIA2
140 IF T$ = "" THEN GOTO 160
150 FOR I = 1 TO LEN(T$): POKE $9F10, ASC(MID$(T$, I, 1)): NEXT I
160 POKE $9F10, 0: REM END OF LINE
170 IF T$ = "" THEN END
180 REM READ T$ FROM VIA2
190 T$ = ""
200 B = PEEK($9F10)
210 IF B = 255 THEN FOR I = 1 TO 20: NEXT I: GOTO 200: REM NO DATA YET
220 IF B = 0 THEN GOTO 240: REM END OF LINE
230 T$ = T$ + CHR$(B): GOTO 200
240 PRINT "> "; T$: GOTO 110
```

CRT File Format
---------------

Expand Down
1 change: 1 addition & 0 deletions src/glue.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ extern bool warp_mode;
extern bool grab_mouse;
extern bool testbench;
extern bool has_via2;
extern char *via2_socket;
extern uint32_t host_sample_rate;
extern bool enable_midline;

Expand Down
16 changes: 16 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ bool set_system_time = false;
bool has_serial = false;
bool no_ieee_intercept = false;
bool has_via2 = false;
char *via2_socket = NULL;
gif_recorder_state_t record_gif = RECORD_GIF_DISABLED;
char *gif_path = NULL;
char *wav_path = NULL;
Expand Down Expand Up @@ -514,6 +515,11 @@ usage()
printf("\tSet the real-time-clock to the current system time and date.\n");
printf("-via2\n");
printf("\tInstall the second VIA chip expansion at $9F10\n");
printf("-via2-socket <socket>\n");
printf("\tUse a socket to emulate VIA2 I/O. Currently only UNIX / POSIX are\n");
printf("\tsupported. The emulator opens this socket as a client, so it must be\n");
printf("\tset up in advance. The -via2 option must be specified along with\n");
printf("\tthis option.\n");
printf("-testbench\n");
printf("\tHeadless mode for unit testing with an external test runner\n");
printf("-mhz <integer>\n");
Expand Down Expand Up @@ -1088,6 +1094,15 @@ main(int argc, char **argv)
argc--;
argv++;
has_via2 = true;
} else if (!strcmp(argv[0], "-via2-socket")) {
argc--;
argv++;
if (!argc || argv[0][0] == '-') {
usage();
}
via2_socket = argv[0];
argc--;
argv++;
} else if (!strcmp(argv[0], "-version")){
printf("%s", VER_INFO);
#ifdef GIT_REV
Expand Down Expand Up @@ -1310,6 +1325,7 @@ void main_shutdown() {
cartridge_unload();
}
files_shutdown();
via2_shutdown();

#ifdef PERFSTAT
for (int pc = 0xc000; pc < sizeof(stat)/sizeof(*stat); pc++) {
Expand Down
62 changes: 61 additions & 1 deletion src/via.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ typedef struct {

static via_t via[2];

#define VIA2_SOCKET_ERROR 0xFF

#ifdef _WIN32
// Windows not yet supported
#else // UNIX / POSIX
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
static int via2_socket_fd = -1;
#endif

// only internal logic is handled here, see via1/2 calls for external
// operations specific to each unit

Expand Down Expand Up @@ -335,18 +346,56 @@ void
via2_init()
{
via_init(&via[1]);
if (via2_socket) {
#ifdef _WIN32
// Windows not yet supported
via2_socket = NULL;
activity_led = VIA2_SOCKET_ERROR;
#else // UNIX / POSIX
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, via2_socket, sizeof(addr.sun_path) - 1);
if ((via2_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 || connect(via2_socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
via2_shutdown();
// Clear to prevent socket I/O
via2_socket = NULL;
activity_led = VIA2_SOCKET_ERROR;
}
#endif
}
}

uint8_t
via2_read(uint8_t reg, bool debug)
{
return via_read(&via[1], reg, debug);
uint8_t byte = via_read(&via[1], reg, debug);
if (via2_socket && reg <= 1) {
#ifdef _WIN32
// Windows not yet supported
#else // UNIX / POSIX
if (recv(via2_socket_fd, &byte, 1, MSG_DONTWAIT) <= 0) {
// Use 0xFF for no data
byte = 0xFF;
}
#endif
}
return byte;
}

void
via2_write(uint8_t reg, uint8_t value)
{
via_write(&via[1], reg, value);
if (via2_socket && reg <= 1) {
#ifdef _WIN32
// Windows not yet supported
#else // UNIX / POSIX
if (send(via2_socket_fd, &value, 1, 0) <= 0) {
activity_led = VIA2_SOCKET_ERROR;
}
#endif
}
}

void
Expand All @@ -360,3 +409,14 @@ via2_irq()
{
return (via[1].registers[13] & via[1].registers[14]) != 0;
}

void
via2_shutdown()
{
#ifdef _WIN32
// Windows not yet supported
#else // UNIX / POSIX
close(via2_socket_fd);
via2_socket_fd = -1;
#endif
}
1 change: 1 addition & 0 deletions src/via.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ uint8_t via2_read(uint8_t reg, bool debug);
void via2_write(uint8_t reg, uint8_t value);
void via2_step(unsigned clocks);
bool via2_irq();
void via2_shutdown();

#endif