From 2c9675b8f03c257c47c207c4325d993768f82cd8 Mon Sep 17 00:00:00 2001 From: "Daniele Verducci (ZenPenguin)" Date: Wed, 16 Dec 2020 08:49:28 +0100 Subject: [PATCH] WIP reimplementing serial terminal with DATA_AVAILABLE register --- arduino/arduino_terminal/arduino_terminal.ino | 29 ++++++++++++++---- assembly/bios/drivers/arduino_terminal.asm | 17 +++++++++-- assembly/bios/monitor.asm | 30 ++++++++++--------- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/arduino/arduino_terminal/arduino_terminal.ino b/arduino/arduino_terminal/arduino_terminal.ino index 647e3f1..b093462 100644 --- a/arduino/arduino_terminal/arduino_terminal.ino +++ b/arduino/arduino_terminal/arduino_terminal.ino @@ -2,19 +2,28 @@ * Terminal interface. * This sketch allow an Arduino to be used as a terminal to log into Pat80. * The Arduino is connected to the Pat80 I/O bus and to the terminal computer via USB. - * The Arduino IDE serial monitor is used to send and receive commands to the Z80. + * The Python Terminal Monitor or the Arduino IDE serial monitor is used to send + * and receive commands to/from the Z80. + * + * Seen from the Pat80, the terminal interface has two registers: + * DATA Register at addr 0x00 (\RS) contains the last received byte from the pc + * DATA_AVAILABLE Register at addr 0x01 (RS) contains the number of bytes in the buffer, + * waiting to be read from the Pat80. A READ operation on DATA register removes the + * byte from the buffer and decrements DATA_AVAILABLE. */ // EN 2 // Input, Active low // WR 11 // Input, Active low +// RS 12 // Input, low = DATA register, high = DATA_AVAILABLE register // DATA BUS (Input/Output, active high): 3, 4, 5, 6, 7, 8, 9, 10; byte incomingBuffer = 0; // Incoming from computer, to the Pat80 byte outgoingBuffer = 0; // Outgoing to computer, from the Pat80 +byte availableBytes = 0; // Available bytes in the incoming buffer (for the DATA_AVAILABLE register) void setup() { DDRD = B00000010; // Port D (used arduino pins 2 (EN) and 3 to 7 (DATA)) is input. Avoid changing serial pins. - DDRB = B00000000; // Port B (used arduino pins 8 to 10 (DATA) and 11 (WR)) is input + DDRB = B00000000; // Port B (used arduino pins 8 to 10 (DATA), 11 (WR) and 12 (RS) is input Serial.begin(2000000); Serial.println("Pat80 terminal"); @@ -25,6 +34,7 @@ void setup() { void loop() { if (Serial.available() > 0) { incomingBuffer = Serial.read(); + availableBytes = 1; // TODO: Implement a 256 byte buffer and store the avail bytes number in this var } if (outgoingBuffer != 0) { if ((outgoingBuffer >= 8 && outgoingBuffer <= 13) || (outgoingBuffer >= 32 && outgoingBuffer <= 127)) { @@ -49,10 +59,17 @@ void onClk() { if ((PINB & B00001000) == B00001000) { // WR is HIGH (Pat80 wants to Read (we send data)) DDRD = DDRD | B11111000; // Port D (arduino pins 3 to 7) is output. In or to preserve serial pins and interrupt pin DDRB = B00000111; // Port B (0,1,2) = pins 8,9,10 output - // Split byte to two parts - PORTD = incomingBuffer << 3; - PORTB = incomingBuffer >> 5; - incomingBuffer = 0; + if ((PINB & B00010000) == B00010000) { // RS is HIGH: we send number of bytes available in buffer + // Split byte to two parts + PORTD = availableBytes << 3; + PORTB = availableBytes >> 5; + } else { + // Split byte to two parts + PORTD = incomingBuffer << 3; + PORTB = incomingBuffer >> 5; + incomingBuffer = 0; + availableBytes = 0; + } } else { // Pat80 wants to Write (we receive data) outgoingBuffer = (PIND >> 3) | (PINB << 5); // Compose the final byte from the two parts diff --git a/assembly/bios/drivers/arduino_terminal.asm b/assembly/bios/drivers/arduino_terminal.asm index 11cbf37..12de40f 100644 --- a/assembly/bios/drivers/arduino_terminal.asm +++ b/assembly/bios/drivers/arduino_terminal.asm @@ -3,6 +3,7 @@ ; config (IO port 0) TERM_DATA_REG: EQU IO_0 +TERM_DATA_AVAIL_REG: EQU IO_0 + 1 ; variables TERM_VAR_SPACE: EQU DRV_VAR_SPACE + 128 @@ -26,7 +27,9 @@ Term_printc: out (TERM_DATA_REG),a ret -; Reads a single character. 0s are ignored (can be used with keyboard) +; Reads a single character. 0s are ignored (can be used with keyboard). +; Doesn't check DATA_AVAILABLE register of parallel port, because a 0 byte +; is ignored anyway (it represents the ASCII NUL control char). ; @return A The read character Term_readc: in a, (TERM_DATA_REG) ; reads a character @@ -35,6 +38,8 @@ Term_readc: ret ; if not NULL, returns it in the a register ; Reads a line. 0s are ignored (can be used with keyboard) +; Doesn't check DATA_AVAILABLE register of parallel port, because a 0 byte +; is ignored anyway (it represents the ASCII NUL control char). ; @return BC The pointer to a null-terminated read string Term_readline: ld bc, incoming_string ; this array will contain read string @@ -56,9 +61,17 @@ Term_readline: ld bc, incoming_string ; Returns read string pointer ret -; Reads the byte currently on the I/O bus at the provided address. +; Returns the number of bytes available on the parallel port using the +; DATA_AVAILABLE register. +; @return a the number of available bytes +Term_availb: + in a, (TERM_DATA_AVAIL_REG) + ret + +; Reads the first available byte on the serial port using the DATA register. ; 0s are not ignored (cannot be used with keyboard) ; Affects NO condition bits! +; @return the available byte, even if 0 Term_readb: in a, (TERM_DATA_REG) ; reads a byte ret \ No newline at end of file diff --git a/assembly/bios/monitor.asm b/assembly/bios/monitor.asm index 75be49f..61e5c4f 100644 --- a/assembly/bios/monitor.asm +++ b/assembly/bios/monitor.asm @@ -25,6 +25,7 @@ MON_ARG_HEX: DB " 0x",0 MON_HELP: DB 10,"Available commands:\nHELP prints this message\nDUMP shows memory content\nSET sets memory content LOAD\nRUN executes code\nADB starts Assembly Deploy Bridge",0 MON_MSG_ADB: DB 10,"Waiting for data.",0 MON_ERR_SYNTAX: DB " Syntax error",0 +;MON_ADB_TIMEOUT: EQU 0xFF // Number of cycles after an ADB binary transfer is considered completed Monitor_main: ; Print welcome string @@ -101,8 +102,10 @@ monitor_adb: call Print ; start copying incoming data to application space call monitor_copyTermToAppMem - jp APP_SPACE ; Start executing code - ;jp monitor_main_loop + ;jp APP_SPACE ; Start executing code + ld bc, APP_SPACE + call Print + jp monitor_main_loop ; Read 1 hex byte (e.g. 0x8C) monitor_arg_byte: @@ -160,22 +163,21 @@ monitor_readHexDigit: ret ; Copy data from STDIN to application memory. This is tought to be used with parallel terminal, not keyboard: -; 0s are not ignored and the sequence is complete when found 8 zeros. +; 0s are not ignored and the sequence is complete when no data is available for 8 cpu cycles. monitor_copyTermToAppMem: - call Term_readb - cp 0 - jp z, monitor_copyTermToAppMem ; wait for data stream to begin: ignore leading zeros ld hl, APP_SPACE ; we will write in APP_SPACE - ld b, 8 ; the number of zeros that represent the end of stream + ld b, 255; MON_ADB_TIMEOUT ; the timeout counter (number cycles without available data that represent the end of stream) monitor_copyTermToAppMem_loop: + dec b ; decrement the timeout counter + ret 0 ; if counter is 0, timeout reached: return + ; check if bytes are available + call Term_availb + cp 0 + jp z, monitor_copyTermToAppMem ; no bytes available, next loop + ; bytes are available + ld b, 255 ;MON_ADB_TIMEOUT; reset the counter ld (hl), a ; copy byte to memory inc hl ; move to next memory position - cp 0 ; compare A to 0 - ; load next byte to A (this doesn't affect condition bits, so flag Z from previous cp is still valid) - call Term_readb - jp nz, monitor_copyTermToAppMem_loop ; if during previous cp A was not 0, execute next cycle - dec b ; if A is 0, decrement "end of stream" counter - ret z ; if B is 0, we found 8 zeros, so the stream is finished: return. - jp monitor_copyTermToAppMem_loop ; otherwise, continue loop + jp monitor_copyTermToAppMem_loop ; continue loop