diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c
index 78fae9b786aead0126cd564f199ee6a3f71f7292..d030306652a65016bbd7726a16ce31fa8927ea2e 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 c6ccd39a2d3b393645577cf9c368c4ab9205b399..b65a5a22dd82026709ae97d6f7247cfdd6c8de94 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 41a153b03bc1fc0ec762eaf3b2be12f3b3d8c95c..df4195147bb4bbb0bd8a273741e432c5425fa805 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 d3f1a0192e60eaaf832af3bbd1aa283d849b6685..cf3f32542a3f32cbfbc33f11f635f6329b458992 100644
--- a/minichlink/minichlink.c
+++ b/minichlink/minichlink.c
@@ -9,11 +9,14 @@
 #include <string.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include "terminalhelp.h"
 #include "minichlink.h"
 #include "../ch32v003fun/ch32v003fun.h"
 
 #if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
+#ifndef _SYNCHAPI_H_
 void Sleep(uint32_t dwMilliseconds);
+#endif
 #else
 #include <unistd.h>
 #endif
@@ -296,20 +299,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 +1669,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 +1692,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 +1705,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
 	{
diff --git a/minichlink/terminalhelp.h b/minichlink/terminalhelp.h
new file mode 100644
index 0000000000000000000000000000000000000000..38cbcf16a89821974d37b5530a476629936b0fcc
--- /dev/null
+++ b/minichlink/terminalhelp.h
@@ -0,0 +1,159 @@
+// terminalhelp from mini-rv32ima.
+#ifndef _TERMINALHELP_H
+#define _TERMINALHELP_H
+
+#include <stdint.h>
+
+// Provides the following:
+static void CaptureKeyboardInput()    __attribute__((used));
+static void ResetKeyboardInput()      __attribute__((used));
+static uint64_t GetTimeMicroseconds() __attribute__((used));
+static int ReadKBByte()               __attribute__((used));
+static int IsKBHit()                  __attribute__((used));
+
+#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
+
+#include <windows.h>
+#include <conio.h>
+
+#define strtoll _strtoi64
+
+static void CaptureKeyboardInput()
+{
+	system(""); // Poorly documented tick: Enable VT100 Windows mode.
+}
+
+static void ResetKeyboardInput()
+{
+}
+
+static uint64_t GetTimeMicroseconds()
+{
+	static LARGE_INTEGER lpf;
+	LARGE_INTEGER li;
+
+	if( !lpf.QuadPart )
+		QueryPerformanceFrequency( &lpf );
+
+	QueryPerformanceCounter( &li );
+	return ((uint64_t)li.QuadPart * 1000000LL) / (uint64_t)lpf.QuadPart;
+}
+
+
+static int IsKBHit()
+{
+	return _kbhit();
+}
+
+static int ReadKBByte()
+{
+	// This code is kind of tricky, but used to convert windows arrow keys
+	// to VT100 arrow keys.
+	static int is_escape_sequence = 0;
+	int r;
+	if( is_escape_sequence == 1 )
+	{
+		is_escape_sequence++;
+		return '[';
+	}
+
+	r = _getch();
+
+	if( is_escape_sequence )
+	{
+		is_escape_sequence = 0;
+		switch( r )
+		{
+			case 'H': return 'A'; // Up
+			case 'P': return 'B'; // Down
+			case 'K': return 'D'; // Left
+			case 'M': return 'C'; // Right
+			case 'G': return 'H'; // Home
+			case 'O': return 'F'; // End
+			default: return r; // Unknown code.
+		}
+	}
+	else
+	{
+		switch( r )
+		{
+			case 13: return 10; //cr->lf
+			case 224: is_escape_sequence = 1; return 27; // Escape arrow keys
+			default: return r;
+		}
+	}
+}
+
+#else
+
+#include <sys/ioctl.h>
+#include <termios.h>
+#undef BS0
+#undef BS1
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+
+static void CtrlC()
+{
+	exit( 0 );
+}
+
+// Override keyboard, so we can capture all keyboard input for the VM.
+static void CaptureKeyboardInput()
+{
+	// Hook exit, because we want to re-enable keyboard.
+	atexit(ResetKeyboardInput);
+	signal(SIGINT, CtrlC);
+
+	struct termios term;
+	tcgetattr(0, &term);
+	term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well
+	tcsetattr(0, TCSANOW, &term);
+}
+
+static void ResetKeyboardInput()
+{
+	// Re-enable echo, etc. on keyboard.
+	struct termios term;
+	tcgetattr(0, &term);
+	term.c_lflag |= ICANON | ECHO;
+	tcsetattr(0, TCSANOW, &term);
+}
+
+static uint64_t GetTimeMicroseconds()
+{
+	struct timeval tv;
+	gettimeofday( &tv, 0 );
+	return tv.tv_usec + ((uint64_t)(tv.tv_sec)) * 1000000LL;
+}
+
+static int is_eofd;
+
+static int ReadKBByte()
+{
+	if( is_eofd ) return 0xffffffff;
+	char rxchar = 0;
+	int rread = read(fileno(stdin), (char*)&rxchar, 1);
+
+	if( rread > 0 ) // Tricky: getchar can't be used with arrow keys.
+		return rxchar;
+	else
+		return -1;
+}
+
+static int IsKBHit()
+{
+	if( is_eofd ) return -1;
+	int byteswaiting;
+	ioctl(0, FIONREAD, &byteswaiting);
+	if( !byteswaiting && write( fileno(stdin), 0, 0 ) != 0 ) { is_eofd = 1; return -1; } // Is end-of-file for 
+	return !!byteswaiting;
+}
+
+
+#endif
+
+
+#endif
+