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 +