2022-08-22 19:10:11 +02:00
|
|
|
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
|
2022-08-27 15:54:06 +02:00
|
|
|
index 0000000..680a1c4
|
2022-08-22 19:10:11 +02:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/src/tapeport/tape-rs232.c
|
2022-08-27 15:54:06 +02:00
|
|
|
@@ -0,0 +1,270 @@
|
2022-08-22 19:10:11 +02:00
|
|
|
+/*
|
|
|
|
+ * 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 */
|
2022-08-27 15:54:06 +02:00
|
|
|
+ dev->baudrate = 600;
|
2022-08-22 19:10:11 +02:00
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+}
|
2022-08-27 15:54:06 +02:00
|
|
|
+CLOCK last_maincpu_clk = 0;
|
2022-08-22 19:10:11 +02:00
|
|
|
+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;
|
2022-08-27 15:54:06 +02:00
|
|
|
+ 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);
|
2022-08-22 19:10:11 +02:00
|
|
|
+ /* 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);
|
2022-08-27 15:54:06 +02:00
|
|
|
+ //log_debug("tape_rs232: schedule start bit alarm after %ld clock cycles", delay);
|
2022-08-22 19:10:11 +02:00
|
|
|
+ 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;
|
2022-08-27 15:54:06 +02:00
|
|
|
+ /* 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);
|
2022-08-22 19:10:11 +02:00
|
|
|
+ /* and decrement the counter */
|
|
|
|
+ --dev->write_shift;
|
2022-08-27 15:54:06 +02:00
|
|
|
+ long delta = (long) maincpu_clk - last_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);
|
2022-08-22 19:10:11 +02:00
|
|
|
+ /* transmission complete? */
|
|
|
|
+ if (dev->write_shift == 0) {
|
|
|
|
+ /* yes, unarm the timer */
|
|
|
|
+ alarm_unset(dev->write_alarm);
|
|
|
|
+ /* check if start and stop bits are valid */
|
2022-08-27 15:54:06 +02:00
|
|
|
+ if ((dev->write_register & 0x0200) == 0x0200 && (dev->write_register & 0x0001) == 0x0000) {
|
2022-08-22 19:10:11 +02:00
|
|
|
+ /* 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
|