Added VICE emulation patch

This commit is contained in:
Gregor Riepl 2022-08-22 19:10:11 +02:00
parent 9f2054453d
commit 42b1c29509
2 changed files with 343 additions and 1 deletions

View file

@ -36,7 +36,7 @@ The other pin mappings are as follows:
| Cassette Sense | RTS | ESP->PET | PIA1 PA4 | PIA1 PA5 | | Cassette Sense | RTS | ESP->PET | PIA1 PA4 | PIA1 PA5 |
A suitable serial signal (RS-232-like) must be generated/decoded in software. A suitable serial signal (RS-232-like) must be generated/decoded in software.
Make sure that you don't access the cassette port #1 while the adapter is Make sure that you don't access the cassette port 1 while the adapter is
connected. connected.
For programming info, refer to this excellent document: For programming info, refer to this excellent document:
@ -107,3 +107,15 @@ This program assumes that the driver is already loaded at start address $7000.
120 SYS (28675) 120 SYS (28675)
130 END 130 END
``` ```
## VICE Emulation
To help test the RS-232 driver, there is a patch for the Commodore computer
emulator VICE in the file vice-tape-rs232.patch.
It is intended for VICE 3.6.1 and prints out decoded RS-232 transmissions
from the driver to the console. Reading data into the emulator is not
supported yet.
To enable the emulated device, you need to open the "Tape port" settings
and select "RS-232 interface" for tape port 1.

330
vice-tape-rs232.patch Normal file
View file

@ -0,0 +1,330 @@
diff --git a/src/tapeport/Makefile.am b/src/tapeport/Makefile.am
index cd38b6d..cf10121 100644
--- a/src/tapeport/Makefile.am
+++ b/src/tapeport/Makefile.am
@@ -30,4 +30,6 @@ libtapeport_a_SOURCES = \
tapecart.h \
tapecart-loader.h \
tapeport.c \
- tapeport.h
+ tapeport.h \
+ tape-rs232.c \
+ tape-rs232.h
diff --git a/src/tapeport/tape-rs232.c b/src/tapeport/tape-rs232.c
new file mode 100644
index 0000000..2b66748
--- /dev/null
+++ b/src/tapeport/tape-rs232.c
@@ -0,0 +1,268 @@
+/*
+ * tape-rs232.h: RS-232 interface for the PET, connected to the tape port
+ *
+ * Written by
+ * Gregor Riepl <onitake@gmail.com>
+ *
+ * This file is part of VICE, the Versatile Commodore Emulator.
+ * See README for copyright notice.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *
+ */
+
+#include "vice.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cmdline.h"
+#include "machine.h"
+#include "resources.h"
+#include "snapshot.h"
+#include "tapeport.h"
+#include "log.h"
+#include "charset.h"
+#include "alarm.h"
+#include "maincpu.h"
+
+#include "tape-rs232.h"
+
+
+typedef struct {
+ /* true if the initialization routine has been called */
+ bool initialized;
+ /* the line transmission rate in bit/s (includes frame bits) */
+ unsigned int baudrate;
+ /* alarm callbacks */
+ alarm_t *write_alarm;
+ alarm_t *read_alarm;
+ /* current state of the inputs/outputs */
+ uint8_t write_level;
+ uint8_t read_level;
+ /* these are 10-bit shift registers (1 start bit + 8 data bits + 1 stop bit) */
+ uint16_t write_register;
+ uint16_t read_register;
+ uint8_t write_shift;
+ uint8_t read_shift;
+} tape_rs232_state_t;
+static tape_rs232_state_t tape_rs232_state[TAPEPORT_MAX_PORTS] = { { false, }, };
+
+/* ------------------------------------------------------------------------- */
+
+/* device interface prototypes */
+static void tape_rs232_powerup(int port);
+static int tape_rs232_enable(int port, int val);
+static void tape_rs232_motor(int port, int flag);
+static void tape_rs232_write(int port, int write_bit);
+static void tape_rs232_sense(int port, int sense);
+static void tape_rs232_read(int port, int val);
+/* callbacks */
+static void tape_rs232_write_sample(CLOCK offset, void *data);
+
+static tapeport_device_t tape_rs232_device = {
+ "RS-232 interface", /* device name */
+ TAPEPORT_DEVICE_TYPE_SERIAL, /* device is a 'serial port' type device */
+ VICE_MACHINE_PET, /* device works only on the PET */
+ TAPEPORT_PORT_1_MASK | TAPEPORT_PORT_2_MASK, /* device works on tape port 1 and 2 */
+ tape_rs232_enable, /* device enable function */
+ tape_rs232_powerup, /* device specific hard reset function */
+ NULL, /* NO device shutdown function */
+ tape_rs232_motor, /* device specific set motor line function (DCE CTS) */
+ tape_rs232_write, /* device specific set write line function (DCE RXD) */
+ tape_rs232_sense, /* device specific set sense line function (DCE RTS) */
+ tape_rs232_read, /* device specific set read line function (DCE TXD) */
+ NULL, /* NO device snapshot write function */
+ NULL /* NO device snapshot read function */
+};
+
+/* ------------------------------------------------------------------------- */
+
+static int tape_rs232_enable(int port, int value)
+{
+ bool enable = value != 0;
+
+ if (port >= TAPEPORT_MAX_PORTS || port < 0) {
+ /* port no. out of range */
+ return -1;
+ }
+ tape_rs232_state_t *dev = &tape_rs232_state[port];
+ if (dev->initialized == enable) {
+ /* already (de)initialized */
+ return 0;
+ }
+
+ if (enable) {
+ /* TODO initialize */
+ log_debug("tape_rs232: enable port %d", port);
+ dev->read_shift = 0;
+ dev->write_shift = 0;
+ /* TODO make the baud rate customizable */
+ dev->baudrate = 300;
+ if (dev->write_alarm != NULL) {
+ alarm_destroy(dev->write_alarm);
+ }
+ dev->write_alarm = alarm_new(maincpu_alarm_context, "tape_rs232_write_sample", tape_rs232_write_sample, dev);
+ /* TODO */
+ dev->read_alarm = NULL;
+ } else {
+ log_debug("tape_rs232: disable port %d", port);
+ /* ensure no alarms are pending, and clear them out */
+ if (dev->write_alarm != NULL) {
+ alarm_unset(dev->write_alarm);
+ dev->write_alarm = NULL;
+ }
+ if (dev->read_alarm != NULL) {
+ alarm_unset(dev->read_alarm);
+ dev->read_alarm = NULL;
+ }
+ }
+
+ tape_rs232_state[port].initialized = enable;
+ return 0;
+}
+
+int tape_rs232_init(int amount)
+{
+ log_debug("tape_rs232: initializing %d ports", amount);
+ /* clear all state data on startup */
+ memset(tape_rs232_state, 0, sizeof(tape_rs232_state));
+ return tapeport_device_register(TAPEPORT_DEVICE_RS232, &tape_rs232_device);
+}
+
+/* ---------------------------------------------------------------------*/
+
+static void tape_rs232_powerup(int port)
+{
+ if (port < 0 || port >= TAPEPORT_MAX_PORTS) {
+ log_error(LOG_ERR, "tape_rs232: invalid port %d", port);
+ return;
+ }
+ tape_rs232_state_t *dev = &tape_rs232_state[port];
+ if (!dev->initialized) {
+ log_error(LOG_ERR, "tape_rs232: port %d not initialized", port);
+ return;
+ }
+ log_debug("tape_rs232: powerup port %d", port);
+ dev->read_shift = 0;
+ dev->write_shift = 0;
+ /* ensure no alarms are pending */
+ if (dev->write_alarm != NULL) {
+ alarm_unset(dev->write_alarm);
+ }
+ if (dev->read_alarm != NULL) {
+ alarm_unset(dev->read_alarm);
+ }
+}
+
+static void tape_rs232_write(int port, int write_bit)
+{
+ if (port < 0 || port >= TAPEPORT_MAX_PORTS) {
+ log_error(LOG_ERR, "tape_rs232: invalid port %d", port);
+ return;
+ }
+ tape_rs232_state_t *dev = &tape_rs232_state[port];
+ if (!dev->initialized) {
+ log_error(LOG_ERR, "tape_rs232: port %d not initialized", port);
+ return;
+ }
+ /* FIXME RS-232 bit-clocking must be done on a time base, not on level change interrupts */
+ /* write_bit is actually the contents of VIA_PB, and the write bit is PB3, i.e. 0x08 */
+ dev->write_level = (write_bit & 0x08) != 0 ? 1 : 0;
+ log_debug("tape_rs232: write port %d level %d", port, dev->write_level);
+ /* write in progress? */
+ if (dev->write_shift == 0) {
+ /* no, start a new transmission - but only if this was a high->low transition (start bit) */
+ if (dev->write_level == 0) {
+ /* initialize the shift register */
+ dev->write_shift = 10;
+ dev->write_register = 0;
+ /* arm the first baud rate timer: sample in the middle of each bit,
+ * so the delay must be half a baud clock cycle */
+ clock_t delay = machine_get_cycles_per_second() / (dev->baudrate * 2);
+ log_debug("tape_rs232: schedule start bit alarm after %d clock cycles", delay);
+ alarm_set(dev->write_alarm, (CLOCK) (maincpu_clk + delay));
+ }
+ }
+}
+
+static void tape_rs232_write_sample(CLOCK offset, void *data)
+{
+ if (data == NULL) {
+ return;
+ }
+ tape_rs232_state_t *dev = (tape_rs232_state_t *) data;
+ /* is this the first bit of a transmission? */
+ if (dev->write_shift == 10) {
+ /* yes, reconfigure the timer to run at baudrate intervals */
+ alarm_unset(dev->write_alarm);
+ clock_t delay = machine_get_cycles_per_second() / dev->baudrate;
+ log_debug("tape_rs232: schedule regular bit alarm after %d clock cycles", delay);
+ alarm_set(dev->write_alarm, (CLOCK) (maincpu_clk + delay));
+ }
+ /* shift in the next bit */
+ dev->write_register = (dev->write_register << 1) | dev->write_level;
+ /* and decrement the counter */
+ --dev->write_shift;
+ /* transmission complete? */
+ if (dev->write_shift == 0) {
+ /* yes, unarm the timer */
+ alarm_unset(dev->write_alarm);
+ /* check if start and stop bits are valid */
+ if ((dev->write_register & 0x0200) == 0x0000 && (dev->write_register & 0x0001) == 0x0001) {
+ /* yes, output data byte */
+ uint8_t wbyte = (uint8_t) (dev->write_register >> 1);
+ /* utf-8 codes generated by the petcii conversion routine can be max. 4 bytes (plus terminator) */
+ char udata[5];
+ size_t udatalen = charset_ucs_to_utf8((uint8_t *) udata, wbyte, sizeof(udata));
+ if (udatalen <= 0) {
+ log_debug("tape_rs232: write hex 0x%02x", wbyte);
+ } else {
+ /* ensure there is a terminator */
+ udata[udatalen] = 0;
+ log_debug("tape_rs232: write hex 0x%02x char %s", wbyte, udata);
+ }
+ } else {
+ log_warning(LOG_ERR, "tape_rs232: invalid RS-232 transmission 0x%04x", dev->write_register);
+ }
+ }
+}
+
+static void tape_rs232_read(int port, int val)
+{
+ if (port < 0 || port >= TAPEPORT_MAX_PORTS) {
+ log_error(LOG_ERR, "tape_rs232: invalid port %d", port);
+ return;
+ }
+ tape_rs232_state_t *dev = &tape_rs232_state[port];
+ if (!dev->initialized) {
+ log_error(LOG_ERR, "tape_rs232: port %d not initialized", port);
+ return;
+ }
+ log_debug("tape_rs232: read port %d level %d", port, val);
+ /* TODO */
+}
+
+/* FIXME RTS/CTS not supported yet */
+static void tape_rs232_motor(int port, int flag)
+{
+ /*log_debug("tape_rs232: motor port %d level %d", port, flag);*/
+}
+static void tape_rs232_sense(int port, int sense)
+{
+ /*log_debug("tape_rs232: sense port %d level %d", port, sense);*/
+}
diff --git a/src/tapeport/tape-rs232.h b/src/tapeport/tape-rs232.h
new file mode 100644
index 0000000..eac1924
--- /dev/null
+++ b/src/tapeport/tape-rs232.h
@@ -0,0 +1,38 @@
+/*
+ * tape-rs232.h: RS-232 interface for the PET, connected to the tape port
+ *
+ * Written by
+ * Gregor Riepl <onitake@gmail.com>
+ *
+ * This file is part of VICE, the Versatile Commodore Emulator.
+ * See README for copyright notice.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *
+ */
+
+#ifndef VICE_TAPE_RS232_H
+#define VICE_TAPE_RS232_H
+
+#include "types.h"
+
+/* Initialize data structures for the emulated serial ports.
+ * amount=number of tape devices in the system
+ * (max=2, only two tape interfaces are available in the PET)
+ */
+extern int tape_rs232_init(int amount);
+
+#endif