petwifi/vice-tape-rs232.patch
2022-09-04 10:24:01 +02:00

336 lines
12 KiB
Diff

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..416657b
--- /dev/null
+++ b/src/tapeport/tape-rs232.c
@@ -0,0 +1,274 @@
+/*
+ * 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, }, };
+
+CLOCK last_maincpu_clk = 0;
+CLOCK last_sample_maincpu_clk = 0;
+
+/* ------------------------------------------------------------------------- */
+
+/* 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 = 600;
+ 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;
+ long delta = (long) maincpu_clk - last_maincpu_clk;
+ last_maincpu_clk = maincpu_clk;
+ log_debug("tape_rs232: write port %d level %d clock %lu delta %ld", port, dev->write_level, maincpu_clk, delta);
+ /* 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 %ld 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;
+ /* prepare the next alarm, at baudrate interval */
+ alarm_unset(dev->write_alarm);
+ clock_t delay = machine_get_cycles_per_second() / dev->baudrate;
+ //log_debug("tape_rs232: schedule regular bit alarm after %ld clock cycles", delay);
+ alarm_set(dev->write_alarm, (CLOCK) (maincpu_clk + delay));
+ /* shift in the next bit, from the head */
+ dev->write_register = (dev->write_register >> 1) | (dev->write_level ? 0x200 : 0x000);
+ /* and decrement the counter */
+ --dev->write_shift;
+ long delta = (long) maincpu_clk - last_sample_maincpu_clk;
+ last_sample_maincpu_clk = maincpu_clk;
+ last_maincpu_clk = maincpu_clk;
+ log_debug("tape_rs232: sample %d reg 0x%04x offset %lu clock %lu delta %ld shift %u", dev->write_level, dev->write_register, offset, maincpu_clk, delta, 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) == 0x0200 && (dev->write_register & 0x0001) == 0x0000) {
+ /* 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