From 773381b08b6788fa1ea567e240a51482c63c3a00 Mon Sep 17 00:00:00 2001
From: cnlohr <lohr85@gmail.com>
Date: Wed, 21 Jun 2023 06:38:06 -0400
Subject: [PATCH] Add standard input through the debug printf module.  Closes
 #168

---
 ch32v003fun/ch32v003fun.c                  | 45 ++++++++++++++++++++--
 ch32v003fun/ch32v003fun.h                  |  6 +++
 examples/debugprintfdemo/debugprintfdemo.c | 15 +++++++-
 minichlink/minichlink.c                    | 42 +++++++++++++++-----
 4 files changed, 94 insertions(+), 14 deletions(-)

diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c
index 78fae9b..d030306 100644
--- a/ch32v003fun/ch32v003fun.c
+++ b/ch32v003fun/ch32v003fun.c
@@ -917,24 +917,61 @@ int putchar(int c)
 }
 #else
 
+
+void handle_debug_input( int numbytes, uint8_t * data ) __attribute__((weak));
+void handle_debug_input( int numbytes, uint8_t * data ) { }
+
+static void internal_handle_input( uint32_t * dmdata0 )
+{
+	uint32_t dmd0 = *dmdata0;
+	int bytes = (dmd0 & 0x3f) - 4;
+	if( bytes > 0 )
+	{
+		handle_debug_input( bytes, ((uint8_t*)dmdata0) + 1 );
+	}
+}
+
+
+void poll_input()
+{
+	uint32_t lastdmd = (*DMDATA0);
+ 	if( !(lastdmd & 0x80) )
+	{
+		internal_handle_input( (uint32_t*)DMDATA0 );
+		*DMDATA0 = 0x84; // Negative
+	}
+}
+
+
 //           MSB .... LSB
 // DMDATA0: char3 char2 char1 [status word]
 // where [status word] is:
 //   b7 = is a "printf" waiting?
-//   b0..b3 = # of bytes in printf (+4).  (4 or higher indicates a print of some kind)
+//   b0..b3 = # of bytes in printf (+4).  (5 or higher indicates a print of some kind)
+//     note: if b7 is 0 in reply, but b0..b3 have >=4 then we received data from host.
 
 int _write(int fd, const char *buf, int size)
 {
 	char buffer[4] = { 0 };
 	int place = 0;
+	uint32_t lastdmd;
 	uint32_t timeout = 160000; // Give up after ~40ms
+	if( size == 0 )
+	{
+		// Simply seeking input.
+		lastdmd = (*DMDATA0);
+		if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 );
+	}
 	while( place < size )
 	{
 		int tosend = size - place;
 		if( tosend > 7 ) tosend = 7;
 
-		while( ((*DMDATA0) & 0x80) )
+		while( ( lastdmd = (*DMDATA0) ) & 0x80 )
 			if( timeout-- == 0 ) return place;
+
+		if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 );
+
 		timeout = 160000;
 
 		int t = 3;
@@ -963,7 +1000,9 @@ int _write(int fd, const char *buf, int size)
 int putchar(int c)
 {
 	int timeout = 16000;
-	while( ((*DMDATA0) & 0x80) ) if( timeout-- == 0 ) return 0;
+	uint32_t lastdmd = 0;
+	while( (lastdmd = (*DMDATA0)) & 0x80 ) if( timeout-- == 0 ) return 0;
+	if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 );
 	*DMDATA0 = 0x85 | ((const char)c<<8);
 	return 1;
 }
diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index c6ccd39..b65a5a2 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -5080,6 +5080,12 @@ void WaitForDebuggerToAttach();
 // Just a definition to the internal _write function.
 int _write(int fd, const char *buf, int size);
 
+// Call this to busy-wait the polling of input.
+void poll_input();
+
+// Receiving bytes from host.  Override if you wish.
+void handle_debug_input( int numbytes, uint8_t * data );
+
 #endif
 
 // xw_ext.inc, thanks to @macyler, @jnk0le, @duk for this reverse engineering.
diff --git a/examples/debugprintfdemo/debugprintfdemo.c b/examples/debugprintfdemo/debugprintfdemo.c
index 41a153b..df41951 100644
--- a/examples/debugprintfdemo/debugprintfdemo.c
+++ b/examples/debugprintfdemo/debugprintfdemo.c
@@ -1,11 +1,19 @@
 /* Small example showing how to use the SWIO programming pin to 
    do printf through the debug interface */
 
+#define SYSTEM_CORE_CLOCK 48000000
 #include "ch32v003fun.h"
 #include <stdio.h>
 
 uint32_t count;
 
+int last = 0;
+void handle_debug_input( int numbytes, uint8_t * data )
+{
+	last = data[0];
+	count += numbytes;
+}
+
 int main()
 {
 	SystemInit48HSI();
@@ -31,9 +39,14 @@ int main()
 		GPIOD->BSHR = 1 | (1<<4);	 // Turn on GPIOs
 		GPIOC->BSHR = 1;
 		printf( "+%lu\n", count++ );
+		Delay_Ms(100);
+		int i;
+		for( i = 0; i < 10000; i++ )
+			poll_input();
 		GPIOD->BSHR = (1<<16) | (1<<(16+4)); // Turn off GPIODs
 		GPIOC->BSHR = (1<<16);
-		printf( "-%lu\n", count++ );
+		printf( "-%lu[%c]\n", count++, last );
+		Delay_Ms(100);
 	}
 }
 
diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c
index d3f1a01..7b756d2 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -9,6 +9,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include "terminalhelp.h"
 #include "minichlink.h"
 #include "../ch32v003fun/ch32v003fun.h"
 
@@ -296,20 +297,42 @@ keep_going:
 					MCF.HaltMode( dev, 2 );
 				}
 
+				CaptureKeyboardInput();
+
+				uint32_t appendword = 0;
 				do
 				{
 					uint8_t buffer[256];
 					if( !IsGDBServerInShadowHaltState( dev ) )
 					{
-						int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), 0, 0 );
-						if( r < 0 )
+						// Handle keyboard input.
+						if( appendword == 0 )
+						{
+							int i;
+							for( i = 0; i < 3; i++ )
+							{
+								if( !IsKBHit() ) break;
+								appendword |= ReadKBByte() << (i*8+8);
+							}
+							appendword |= i+4; // Will go into DATA0.
+						}
+						int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), appendword, 0 );
+						if( r == -1 )
+						{
+							// Other end ack'd without printf.
+							appendword = 0;
+						}
+						else if( r < 0 )
 						{
 							fprintf( stderr, "Terminal dead.  code %d\n", r );
 							return -32;
 						}
-						if( r > 0 )
+						else if( r > 0 )
 						{
-							fwrite( buffer, r, 1, stdout ); 
+							fwrite( buffer, r, 1, stdout );
+							fflush( stdout );
+							// Otherwise it's basically just an ack for appendword.
+							appendword = 0;
 						}
 					}
 
@@ -1644,14 +1667,14 @@ static int DefaultHaltMode( void * dev, int mode )
 	return 0;
 }
 
-// Returns positive if received text.
+// Returns positive if received text, or request for input.
+// Returns -1 if nothing was printed but received data.
 // Returns negative if error.
 // Returns 0 if no text waiting.
 // maxlen MUST be at least 8 characters.  We null terminate.
 int DefaultPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leaveflagA, int leaveflagB )
 {
 	struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
-
 	int r;
 	uint32_t rr;
 	if( iss->statetag != STTAG( "TERM" ) )
@@ -1667,9 +1690,7 @@ int DefaultPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leav
 	//  bit  7 = host-acknowledge.
 	if( rr & 0x80 )
 	{
-		int ret = 0;
 		int num_printf_chars = (rr & 0xf)-4;
-
 		if( num_printf_chars > 0 && num_printf_chars <= 7)
 		{
 			if( num_printf_chars > 3 )
@@ -1682,11 +1703,12 @@ int DefaultPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leav
 			if( firstrem > 3 ) firstrem = 3;
 			memcpy( buffer, ((uint8_t*)&rr)+1, firstrem );
 			buffer[num_printf_chars] = 0;
-			ret = num_printf_chars;
 		}
 		if( leaveflagA ) MCF.WriteReg32( dev, DMDATA1, leaveflagB );
 		MCF.WriteReg32( dev, DMDATA0, leaveflagA ); // Write that we acknowledge the data.
-		return ret;
+		if( num_printf_chars == 0 ) return -1;      // was acked?
+		if( num_printf_chars < 0 ) num_printf_chars = 0;
+		return num_printf_chars;
 	}
 	else
 	{
-- 
GitLab