From 3c9dac8a9c6c380aead837e07a7605bdb42c1ebd Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 10 Dec 2023 15:47:41 +0100 Subject: [PATCH 01/17] Consolidated commits from branch scriptconsole: Add command line options for console input and log files, and check error on initial chdir(). Refactoring: Route all console input via console.h. No more _getch*() from anywhere but console.h. Gate console_in and console_log behind SCRIPTCONSOLE feature flag Move definition of console_in and console_log from abstraction_posix.h to main.c, so that it is available in all platform builds. Reverse the order of _getcon* and _putcon* functions in console.h. This is in preparation to adding the script input and log output features. Add input from script input file and output to log file to console.h. Add corresponding init and close call to main.c --- RunCPM/abstraction_posix.h | 49 ++++++++++++++++++++++- RunCPM/console.h | 82 +++++++++++++++++++++++++++++++++----- RunCPM/cpm.h | 8 ++-- RunCPM/cpu.h | 4 +- RunCPM/disk.h | 2 +- RunCPM/globals.h | 3 ++ RunCPM/main.c | 12 ++++++ 7 files changed, 141 insertions(+), 19 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 69cb3ce..7b5334b 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -1,6 +1,9 @@ #ifndef ABSTRACT_H #define ABSTRACT_H +#include +#include +#include #include #include #include @@ -476,8 +479,52 @@ uint32 _HardwareIn(const uint32 Port) { /* Host initialization functions */ /*===============================================================================*/ +#ifdef SCRIPTCONSOLE +static struct argp_option options[] = { + {"input", 'i', "FILE", 0, "File to read console input from"}, + {"output", 'o', "FILE", 0, "File to log console output to"}, + {0} +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'i': + console_in = fopen(arg, "r"); + if (NULL == console_in) { + error(EXIT_FAILURE, errno, + "error opening console input file %s", arg); + } + break; + case 'o': + console_log = fopen(arg, "w"); + if (NULL == console_log) { + error(EXIT_FAILURE, errno, + "error opening console log output file %s", arg); + } + break; + case ARGP_KEY_ARG: + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = {options, parse_opt, NULL, + "RunCPM - an emulator to run CP/M programs on modern hosts"}; +#endif + void _host_init(int argc, char* argv[]) { - int x = chdir(dirname(argv[0])); +#ifdef SCRIPTCONSOLE + argp_parse(&argp, argc, argv, 0, NULL, NULL); +#endif + if (chdir(dirname(argv[0]))) { + error(EXIT_FAILURE, errno, "error performing chdir(%s)", + dirname(argv[0])); + } } /* Console abstraction functions */ diff --git a/RunCPM/console.h b/RunCPM/console.h index db524d5..43a00c6 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -10,19 +10,13 @@ uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM relat // required for XMODEM to work. // Use the CONSOLE7 and CONSOLE8 programs to change this on the fly. -uint8 _chready(void) // Checks if there's a character ready for input -{ - return(_kbhit() ? 0xff : 0x00); -} - -uint8 _getchNB(void) // Gets a character, non-blocking, no echo -{ - return(_kbhit() ? _getch() : 0x00); -} - void _putcon(uint8 ch) // Puts a character { _putch(ch & mask8bit); +#ifdef SCRIPTCONSOLE + if (console_log) fputc(ch & mask8bit, console_log); +#endif + } void _puts(const char* str) // Puts a \0 terminated string @@ -43,4 +37,70 @@ void _puthex16(uint16 w) // puts a HHHH hex string _puthex8(w & 0x00ff); } -#endif \ No newline at end of file +#ifdef SCRIPTCONSOLE +int nextConInChar; + +void _getNextConInChar(void) +{ + nextConInChar = console_in ? fgetc(console_in) : EOF; +} + +uint8 _conInCharReady(void) +{ + return EOF != nextConInChar; +} + +uint8 _getConInChar(void) +{ + uint8 result = nextConInChar; + _getNextConInChar(); + if (0x0a == result) result = 0x0d; + return result; +} + +uint8 _getConInCharEcho() +{ + uint8 result = _getConInChar(); + _putcon(result); + return result; +} + +void _scriptConsoleInit(void) +{ + _getNextConInChar(); +} +#endif + +uint8 _chready(void) // Checks if there's a character ready for input +{ +#ifdef SCRIPTCONSOLE + if (_conInCharReady()) return 0xff; +#endif + return(_kbhit() ? 0xff : 0x00); +} + +uint8 _getconNB(void) // Gets a character, non-blocking, no echo +{ +#ifdef SCRIPTCONSOLE + if (_conInCharReady()) return _getConInChar(); +#endif + return(_kbhit() ? _getch() : 0x00); +} + +uint8 _getcon(void) // Gets a character, blocking, no echo +{ +#ifdef SCRIPTCONSOLE + if (_conInCharReady()) return _getConInChar(); +#endif + return _getch(); +} + +uint8 _getconE(void) // Gets a character, blocking, with echo +{ +#ifdef SCRIPTCONSOLE + if (_conInCharReady()) return _getConInCharEcho(); +#endif + return _getche(); +} + +#endif diff --git a/RunCPM/cpm.h b/RunCPM/cpm.h index ab8fb69..e2511fb 100644 --- a/RunCPM/cpm.h +++ b/RunCPM/cpm.h @@ -536,7 +536,7 @@ void _Bios(void) { break; } case B_CONIN: { // 3 - Console input - SET_HIGH_REGISTER(AF, _getch()); + SET_HIGH_REGISTER(AF, _getcon()); #ifdef DEBUG if (HIGH_REGISTER(AF) == 4) Debug = 1; @@ -698,7 +698,7 @@ void _Bdos(void) { Returns: A=Char */ case C_READ: { - HL = _getche(); + HL = _getconE(); #ifdef DEBUG if (HL == 4) Debug = 1; @@ -766,7 +766,7 @@ void _Bdos(void) { */ case C_RAWIO: { if (LOW_REGISTER(DE) == 0xff) { - HL = _getchNB(); + HL = _getconNB(); #ifdef DEBUG if (HL == 4) Debug = 1; @@ -853,7 +853,7 @@ void _Bdos(void) { // pre-backspace, retype & post backspace counts uint8 preBS = 0, reType = 0, postBS = 0; - chr = _getch(); //input a character + chr = _getcon(); //input a character if (chr == 1) { // ^A - Move cursor one character to the left if (curCol > 0) { diff --git a/RunCPM/cpu.h b/RunCPM/cpu.h index 9b104ae..c0afc90 100644 --- a/RunCPM/cpu.h +++ b/RunCPM/cpu.h @@ -1343,7 +1343,7 @@ void Z80debug(void) { _puts("\r\n"); _puts("Command|? : "); - ch = _getch(); + ch = _getcon(); if (ch > 21 && ch < 127) _putch(ch); switch (ch) { @@ -2077,7 +2077,7 @@ static inline void Z80run(void) { #ifdef DEBUG _puts("\r\n::CPU HALTED::"); // A halt is a good indicator of broken code _puts("Press any key..."); - _getch(); + _getcon(); #endif --PC; goto end_decode; diff --git a/RunCPM/disk.h b/RunCPM/disk.h index a97a5d0..63b097f 100644 --- a/RunCPM/disk.h +++ b/RunCPM/disk.h @@ -35,7 +35,7 @@ void _error(uint8 error) { _puts("\r\nCP/M ERR"); break; } - Status = _getch(); + Status = _getcon(); _puts("\r\n"); cDrive = oDrive = _RamRead(DSKByte) & 0x0f; Status = 2; diff --git a/RunCPM/globals.h b/RunCPM/globals.h index 5716239..af5ccfe 100644 --- a/RunCPM/globals.h +++ b/RunCPM/globals.h @@ -92,6 +92,9 @@ //#define HASLUA // Will enable Lua scripting (BDOS call 254) // Should be passed externally per-platform with -DHASLUA +//#define SCRIPTCONSOLE // Will enable command line flags to read + // console input from file and to log console output to file + // Should be passed externally per-platform with -DSCRIPTCONSOLE //#define PROFILE // For measuring time taken to run a CP/M command // This should be enabled only for debugging purposes when trying to improve emulation speed diff --git a/RunCPM/main.c b/RunCPM/main.c index 46554a1..ccda6d9 100644 --- a/RunCPM/main.c +++ b/RunCPM/main.c @@ -26,6 +26,12 @@ This should be the only file modified for portability. Any other file should be kept the same. */ +#ifdef SCRIPTCONSOLE +#include +FILE *console_in = NULL; +FILE *console_log = NULL; +#endif + #ifdef _WIN32 #include "abstraction_vstudio.h" #else @@ -61,6 +67,9 @@ int main(int argc, char* argv[]) { #endif _host_init(argc, &argv[0]); +#ifdef SCRIPTCONSOLE + _scriptConsoleInit(); +#endif _console_init(); _clrscr(); _puts(" CP/M Emulator v" VERSION " by Marcelo Dantas\r\n"); @@ -115,6 +124,9 @@ int main(int argc, char* argv[]) { _puts("\r\n"); _console_reset(); +#ifdef SCRIPTCONSOLE + if (console_log) fclose(console_log); +#endif return(0); } From 3953c06f5cde239d664f645e0022917ec569c7ad Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Thu, 14 Dec 2023 22:30:38 +0100 Subject: [PATCH 02/17] Add -s option for connecting console in and out to stdin and stdout --- RunCPM/abstraction_posix.h | 5 +++++ RunCPM/console.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 7b5334b..f350950 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -483,6 +483,7 @@ uint32 _HardwareIn(const uint32 Port) { static struct argp_option options[] = { {"input", 'i', "FILE", 0, "File to read console input from"}, {"output", 'o', "FILE", 0, "File to log console output to"}, + {"stdio", 's', NULL, 0, "Route console in/out to stdin/out"}, {0} }; @@ -504,6 +505,10 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) "error opening console log output file %s", arg); } break; + case 's': + console_in = stdin; + console_log = stdout; + break; case ARGP_KEY_ARG: case ARGP_KEY_END: break; diff --git a/RunCPM/console.h b/RunCPM/console.h index 43a00c6..f55fecf 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -12,9 +12,11 @@ uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM relat void _putcon(uint8 ch) // Puts a character { - _putch(ch & mask8bit); #ifdef SCRIPTCONSOLE + if (console_log != stdout) _putch(ch & mask8bit); if (console_log) fputc(ch & mask8bit, console_log); +#else + _putch(ch & mask8bit); #endif } From 70dbd3c40e0bbed96bbd1d23c1eded7a86c28a8e Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sat, 16 Dec 2023 16:54:45 +0100 Subject: [PATCH 03/17] Rename SCRIPTCONSOLE compile flag to STREAMIO Change my indents from 2 spaces to 4-wide tabs --- RunCPM/abstraction_posix.h | 74 +++++++++++++++++++------------------- RunCPM/console.h | 46 ++++++++++++------------ RunCPM/globals.h | 4 +-- RunCPM/main.c | 10 +++--- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index f350950..f26f2e5 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -479,43 +479,43 @@ uint32 _HardwareIn(const uint32 Port) { /* Host initialization functions */ /*===============================================================================*/ -#ifdef SCRIPTCONSOLE +#ifdef STREAMIO static struct argp_option options[] = { - {"input", 'i', "FILE", 0, "File to read console input from"}, - {"output", 'o', "FILE", 0, "File to log console output to"}, - {"stdio", 's', NULL, 0, "Route console in/out to stdin/out"}, - {0} + {"input", 'i', "FILE", 0, "File to read console input from"}, + {"output", 'o', "FILE", 0, "File to log console output to"}, + {"stdio", 's', NULL, 0, "Route console in/out to stdin/out"}, + {0} }; static error_t parse_opt (int key, char *arg, struct argp_state *state) { - switch (key) - { - case 'i': - console_in = fopen(arg, "r"); - if (NULL == console_in) { - error(EXIT_FAILURE, errno, - "error opening console input file %s", arg); - } - break; - case 'o': - console_log = fopen(arg, "w"); - if (NULL == console_log) { - error(EXIT_FAILURE, errno, - "error opening console log output file %s", arg); - } - break; - case 's': - console_in = stdin; - console_log = stdout; - break; - case ARGP_KEY_ARG: - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + switch (key) + { + case 'i': + console_in = fopen(arg, "r"); + if (NULL == console_in) { + error(EXIT_FAILURE, errno, + "error opening console input file %s", arg); + } + break; + case 'o': + console_log = fopen(arg, "w"); + if (NULL == console_log) { + error(EXIT_FAILURE, errno, + "error opening console log output file %s", arg); + } + break; + case 's': + console_in = stdin; + console_log = stdout; + break; + case ARGP_KEY_ARG: + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } static struct argp argp = {options, parse_opt, NULL, @@ -523,13 +523,13 @@ static struct argp argp = {options, parse_opt, NULL, #endif void _host_init(int argc, char* argv[]) { -#ifdef SCRIPTCONSOLE - argp_parse(&argp, argc, argv, 0, NULL, NULL); +#ifdef STREAMIO + argp_parse(&argp, argc, argv, 0, NULL, NULL); #endif - if (chdir(dirname(argv[0]))) { - error(EXIT_FAILURE, errno, "error performing chdir(%s)", + if (chdir(dirname(argv[0]))) { + error(EXIT_FAILURE, errno, "error performing chdir(%s)", dirname(argv[0])); - } + } } /* Console abstraction functions */ diff --git a/RunCPM/console.h b/RunCPM/console.h index f55fecf..15bfb2d 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -12,9 +12,9 @@ uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM relat void _putcon(uint8 ch) // Puts a character { -#ifdef SCRIPTCONSOLE - if (console_log != stdout) _putch(ch & mask8bit); - if (console_log) fputc(ch & mask8bit, console_log); +#ifdef STREAMIO + if (console_log != stdout) _putch(ch & mask8bit); + if (console_log) fputc(ch & mask8bit, console_log); #else _putch(ch & mask8bit); #endif @@ -39,68 +39,68 @@ void _puthex16(uint16 w) // puts a HHHH hex string _puthex8(w & 0x00ff); } -#ifdef SCRIPTCONSOLE +#ifdef STREAMIO int nextConInChar; void _getNextConInChar(void) { - nextConInChar = console_in ? fgetc(console_in) : EOF; + nextConInChar = console_in ? fgetc(console_in) : EOF; } uint8 _conInCharReady(void) { - return EOF != nextConInChar; + return EOF != nextConInChar; } uint8 _getConInChar(void) { - uint8 result = nextConInChar; - _getNextConInChar(); - if (0x0a == result) result = 0x0d; - return result; + uint8 result = nextConInChar; + _getNextConInChar(); + if (0x0a == result) result = 0x0d; + return result; } uint8 _getConInCharEcho() { - uint8 result = _getConInChar(); - _putcon(result); - return result; + uint8 result = _getConInChar(); + _putcon(result); + return result; } -void _scriptConsoleInit(void) +void _streamioInit(void) { - _getNextConInChar(); + _getNextConInChar(); } #endif uint8 _chready(void) // Checks if there's a character ready for input { -#ifdef SCRIPTCONSOLE - if (_conInCharReady()) return 0xff; +#ifdef STREAMIO + if (_conInCharReady()) return 0xff; #endif return(_kbhit() ? 0xff : 0x00); } uint8 _getconNB(void) // Gets a character, non-blocking, no echo { -#ifdef SCRIPTCONSOLE - if (_conInCharReady()) return _getConInChar(); +#ifdef STREAMIO + if (_conInCharReady()) return _getConInChar(); #endif return(_kbhit() ? _getch() : 0x00); } uint8 _getcon(void) // Gets a character, blocking, no echo { -#ifdef SCRIPTCONSOLE - if (_conInCharReady()) return _getConInChar(); +#ifdef STREAMIO + if (_conInCharReady()) return _getConInChar(); #endif return _getch(); } uint8 _getconE(void) // Gets a character, blocking, with echo { -#ifdef SCRIPTCONSOLE - if (_conInCharReady()) return _getConInCharEcho(); +#ifdef STREAMIO + if (_conInCharReady()) return _getConInCharEcho(); #endif return _getche(); } diff --git a/RunCPM/globals.h b/RunCPM/globals.h index af5ccfe..d8fd4a3 100644 --- a/RunCPM/globals.h +++ b/RunCPM/globals.h @@ -92,9 +92,9 @@ //#define HASLUA // Will enable Lua scripting (BDOS call 254) // Should be passed externally per-platform with -DHASLUA -//#define SCRIPTCONSOLE // Will enable command line flags to read +//#define STREAMIO // Will enable command line flags to read // console input from file and to log console output to file - // Should be passed externally per-platform with -DSCRIPTCONSOLE + // Should be passed externally per-platform with -DSTREAMIO //#define PROFILE // For measuring time taken to run a CP/M command // This should be enabled only for debugging purposes when trying to improve emulation speed diff --git a/RunCPM/main.c b/RunCPM/main.c index ccda6d9..be9221f 100644 --- a/RunCPM/main.c +++ b/RunCPM/main.c @@ -26,7 +26,7 @@ This should be the only file modified for portability. Any other file should be kept the same. */ -#ifdef SCRIPTCONSOLE +#ifdef STREAMIO #include FILE *console_in = NULL; FILE *console_log = NULL; @@ -67,8 +67,8 @@ int main(int argc, char* argv[]) { #endif _host_init(argc, &argv[0]); -#ifdef SCRIPTCONSOLE - _scriptConsoleInit(); +#ifdef STREAMIO + _streamioInit(); #endif _console_init(); _clrscr(); @@ -124,8 +124,8 @@ int main(int argc, char* argv[]) { _puts("\r\n"); _console_reset(); -#ifdef SCRIPTCONSOLE - if (console_log) fclose(console_log); +#ifdef STREAMIO + if (console_log) fclose(console_log); #endif return(0); } From 012a3504d824fc52bda2a5aed707a1cdc0760716 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sat, 16 Dec 2023 17:11:59 +0100 Subject: [PATCH 04/17] Rename input and output file pointers, and add streamIn/Out and echo flags --- RunCPM/abstraction_posix.h | 12 ++++++------ RunCPM/console.h | 18 ++++++++++++------ RunCPM/globals.h | 7 +++++++ RunCPM/main.c | 8 +------- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index f26f2e5..f92c2e6 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -492,22 +492,22 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) switch (key) { case 'i': - console_in = fopen(arg, "r"); - if (NULL == console_in) { + streamInFile = fopen(arg, "r"); + if (NULL == streamInFile) { error(EXIT_FAILURE, errno, "error opening console input file %s", arg); } break; case 'o': - console_log = fopen(arg, "w"); - if (NULL == console_log) { + streamOutFile = fopen(arg, "w"); + if (NULL == streamOutFile) { error(EXIT_FAILURE, errno, "error opening console log output file %s", arg); } break; case 's': - console_in = stdin; - console_log = stdout; + streamInFile = stdin; + streamOutFile = stdout; break; case ARGP_KEY_ARG: case ARGP_KEY_END: diff --git a/RunCPM/console.h b/RunCPM/console.h index 15bfb2d..869907e 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -13,8 +13,8 @@ uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM relat void _putcon(uint8 ch) // Puts a character { #ifdef STREAMIO - if (console_log != stdout) _putch(ch & mask8bit); - if (console_log) fputc(ch & mask8bit, console_log); + if (streamOutFile != stdout) _putch(ch & mask8bit); + if (streamOutFile) fputc(ch & mask8bit, streamOutFile); #else _putch(ch & mask8bit); #endif @@ -40,21 +40,24 @@ void _puthex16(uint16 w) // puts a HHHH hex string } #ifdef STREAMIO -int nextConInChar; +int _nextStreamInChar; +bool streamInput; +bool streamOutput; +bool echoOutput; void _getNextConInChar(void) { - nextConInChar = console_in ? fgetc(console_in) : EOF; + _nextStreamInChar = streamInFile ? fgetc(streamInFile) : EOF; } uint8 _conInCharReady(void) { - return EOF != nextConInChar; + return EOF != _nextStreamInChar; } uint8 _getConInChar(void) { - uint8 result = nextConInChar; + uint8 result = _nextStreamInChar; _getNextConInChar(); if (0x0a == result) result = 0x0d; return result; @@ -69,6 +72,9 @@ uint8 _getConInCharEcho() void _streamioInit(void) { + streamInput = FALSE; + streamOutput = FALSE; + echoOutput = FALSE; _getNextConInChar(); } #endif diff --git a/RunCPM/globals.h b/RunCPM/globals.h index d8fd4a3..c006c5b 100644 --- a/RunCPM/globals.h +++ b/RunCPM/globals.h @@ -224,6 +224,13 @@ static uint16 physicalExtentBytes;// # bytes described by 1 directory entry static uint32 timer; +#ifdef STREAMIO +#include +FILE *streamInFile = NULL; +FILE *streamOutFile = NULL; +#endif + + /* Definition of externs to prevent precedence compilation errors */ #ifdef __cplusplus // If building on Arduino extern "C" diff --git a/RunCPM/main.c b/RunCPM/main.c index be9221f..c28e7eb 100644 --- a/RunCPM/main.c +++ b/RunCPM/main.c @@ -26,12 +26,6 @@ This should be the only file modified for portability. Any other file should be kept the same. */ -#ifdef STREAMIO -#include -FILE *console_in = NULL; -FILE *console_log = NULL; -#endif - #ifdef _WIN32 #include "abstraction_vstudio.h" #else @@ -125,7 +119,7 @@ int main(int argc, char* argv[]) { _puts("\r\n"); _console_reset(); #ifdef STREAMIO - if (console_log) fclose(console_log); + if (streamOutFile) fclose(streamOutFile); #endif return(0); } From ba87947acd75411766652210ed115203e3fc031a Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sat, 16 Dec 2023 17:33:50 +0100 Subject: [PATCH 05/17] Move streamio file and flag vars to globals, move log file closing to console.h --- RunCPM/console.h | 11 +++++------ RunCPM/globals.h | 7 +++++-- RunCPM/main.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/RunCPM/console.h b/RunCPM/console.h index 869907e..7f0641c 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -41,9 +41,6 @@ void _puthex16(uint16 w) // puts a HHHH hex string #ifdef STREAMIO int _nextStreamInChar; -bool streamInput; -bool streamOutput; -bool echoOutput; void _getNextConInChar(void) { @@ -72,11 +69,13 @@ uint8 _getConInCharEcho() void _streamioInit(void) { - streamInput = FALSE; - streamOutput = FALSE; - echoOutput = FALSE; _getNextConInChar(); } + +void _streamioReset(void) +{ + if (streamOutFile) fclose(streamOutFile); +} #endif uint8 _chready(void) // Checks if there's a character ready for input diff --git a/RunCPM/globals.h b/RunCPM/globals.h index c006c5b..1beeac4 100644 --- a/RunCPM/globals.h +++ b/RunCPM/globals.h @@ -226,8 +226,11 @@ static uint32 timer; #ifdef STREAMIO #include -FILE *streamInFile = NULL; -FILE *streamOutFile = NULL; +static FILE *streamInFile = NULL; +static FILE *streamOutFile = NULL; +static bool streamInput = FALSE; +static bool streamOutput = FALSE; +static bool echoOutput = FALSE; #endif diff --git a/RunCPM/main.c b/RunCPM/main.c index c28e7eb..9d462bd 100644 --- a/RunCPM/main.c +++ b/RunCPM/main.c @@ -119,7 +119,7 @@ int main(int argc, char* argv[]) { _puts("\r\n"); _console_reset(); #ifdef STREAMIO - if (streamOutFile) fclose(streamOutFile); + _streamioReset(); #endif return(0); } From a4e878e59b96f8b2f55d9c0a10a4f2c3f88810c6 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sat, 16 Dec 2023 18:29:40 +0100 Subject: [PATCH 06/17] streamline streamio in console.h --- RunCPM/abstraction_posix.h | 3 +++ RunCPM/console.h | 31 +++++++++++++++---------------- RunCPM/globals.h | 5 ++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index f92c2e6..487b116 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -497,6 +497,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) error(EXIT_FAILURE, errno, "error opening console input file %s", arg); } + streamInActive = TRUE; break; case 'o': streamOutFile = fopen(arg, "w"); @@ -508,6 +509,8 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) case 's': streamInFile = stdin; streamOutFile = stdout; + streamInActive = TRUE; + consoleOutActive = FALSE; break; case ARGP_KEY_ARG: case ARGP_KEY_END: diff --git a/RunCPM/console.h b/RunCPM/console.h index 7f0641c..9ccfd72 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -13,7 +13,7 @@ uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM relat void _putcon(uint8 ch) // Puts a character { #ifdef STREAMIO - if (streamOutFile != stdout) _putch(ch & mask8bit); + if (consoleOutActive) _putch(ch & mask8bit); if (streamOutFile) fputc(ch & mask8bit, streamOutFile); #else _putch(ch & mask8bit); @@ -42,34 +42,33 @@ void _puthex16(uint16 w) // puts a HHHH hex string #ifdef STREAMIO int _nextStreamInChar; -void _getNextConInChar(void) +void _getNextStreamInChar(void) { _nextStreamInChar = streamInFile ? fgetc(streamInFile) : EOF; + if (EOF == _nextStreamInChar) { + streamInActive = FALSE; + } } -uint8 _conInCharReady(void) -{ - return EOF != _nextStreamInChar; -} - -uint8 _getConInChar(void) +uint8 _getStreamInChar(void) { uint8 result = _nextStreamInChar; - _getNextConInChar(); + _getNextStreamInChar(); + // TODO: delegate to abstrction_posix.h if (0x0a == result) result = 0x0d; return result; } -uint8 _getConInCharEcho() +uint8 _getStreamInCharEcho() { - uint8 result = _getConInChar(); + uint8 result = _getStreamInChar(); _putcon(result); return result; } void _streamioInit(void) { - _getNextConInChar(); + _getNextStreamInChar(); } void _streamioReset(void) @@ -81,7 +80,7 @@ void _streamioReset(void) uint8 _chready(void) // Checks if there's a character ready for input { #ifdef STREAMIO - if (_conInCharReady()) return 0xff; + if (streamInActive) return 0xff; #endif return(_kbhit() ? 0xff : 0x00); } @@ -89,7 +88,7 @@ uint8 _chready(void) // Checks if there's a character ready for input uint8 _getconNB(void) // Gets a character, non-blocking, no echo { #ifdef STREAMIO - if (_conInCharReady()) return _getConInChar(); + if (streamInActive) return _getStreamInChar(); #endif return(_kbhit() ? _getch() : 0x00); } @@ -97,7 +96,7 @@ uint8 _getconNB(void) // Gets a character, non-blocking, no echo uint8 _getcon(void) // Gets a character, blocking, no echo { #ifdef STREAMIO - if (_conInCharReady()) return _getConInChar(); + if (streamInActive) return _getStreamInChar(); #endif return _getch(); } @@ -105,7 +104,7 @@ uint8 _getcon(void) // Gets a character, blocking, no echo uint8 _getconE(void) // Gets a character, blocking, with echo { #ifdef STREAMIO - if (_conInCharReady()) return _getConInCharEcho(); + if (streamInActive) return _getStreamInCharEcho(); #endif return _getche(); } diff --git a/RunCPM/globals.h b/RunCPM/globals.h index 1beeac4..9398709 100644 --- a/RunCPM/globals.h +++ b/RunCPM/globals.h @@ -228,9 +228,8 @@ static uint32 timer; #include static FILE *streamInFile = NULL; static FILE *streamOutFile = NULL; -static bool streamInput = FALSE; -static bool streamOutput = FALSE; -static bool echoOutput = FALSE; +static uint8 streamInActive = FALSE; +static uint8 consoleOutActive = TRUE; #endif From 1516431fd04bdf0f80485b8755e37562057c3946 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 00:16:52 +0100 Subject: [PATCH 07/17] Use Posix getopt() instead of argp_parse() --- RunCPM/abstraction_posix.h | 61 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 487b116..050c8e9 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -1,7 +1,9 @@ #ifndef ABSTRACT_H #define ABSTRACT_H +#ifdef STREAMIO2 #include +#endif #include #include #include @@ -479,7 +481,7 @@ uint32 _HardwareIn(const uint32 Port) { /* Host initialization functions */ /*===============================================================================*/ -#ifdef STREAMIO +#ifdef STREAMIO2 static struct argp_option options[] = { {"input", 'i', "FILE", 0, "File to read console input from"}, {"output", 'o', "FILE", 0, "File to log console output to"}, @@ -503,7 +505,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) streamOutFile = fopen(arg, "w"); if (NULL == streamOutFile) { error(EXIT_FAILURE, errno, - "error opening console log output file %s", arg); + "error opening console output file %s", arg); } break; case 's': @@ -525,9 +527,62 @@ static struct argp argp = {options, parse_opt, NULL, "RunCPM - an emulator to run CP/M programs on modern hosts"}; #endif -void _host_init(int argc, char* argv[]) { #ifdef STREAMIO +static void _usage(char *argv[]) { + fprintf(stderr, + "usage: %s [-i input_file] [-o output_file] [-s]\n", argv[0]); +} + +static void _parse_options(int argc, char *argv[]) { + int c; + int errflg = 0; + while ((c = getopt(argc, argv, ":i:o:s")) != -1) { + switch(c) { + case 'i': + streamInFile = fopen(optarg, "r"); + if (NULL == streamInFile) { + error(EXIT_FAILURE, errno, + "error opening console input file %s", optarg); + } + streamInActive = TRUE; + break; + case 'o': + streamOutFile = fopen(optarg, "w"); + if (NULL == streamOutFile) { + error(EXIT_FAILURE, errno, + "error opening console output file %s", optarg); + } + break; + case 's': + streamInFile = stdin; + streamOutFile = stdout; + streamInActive = TRUE; + consoleOutActive = FALSE; + break; + case ':': /* -f or -o without operand */ + fprintf(stderr, + "Option -%c requires an operand\n", optopt); + errflg++; + break; + case '?': + fprintf(stderr, + "Unrecognized option: '-%c'\n", optopt); + errflg++; + } + if (errflg || optind == argc) { + _usage(argv); + exit(2); + } + } +} +#endif + +void _host_init(int argc, char* argv[]) { +#ifdef STREAMIO2 argp_parse(&argp, argc, argv, 0, NULL, NULL); +#endif +#ifdef STREAMIO + _parse_options(argc, argv); #endif if (chdir(dirname(argv[0]))) { error(EXIT_FAILURE, errno, "error performing chdir(%s)", From 252d708bccc160759bd47a56212e0cf5b51e5335 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 11:58:06 +0100 Subject: [PATCH 08/17] Remove non-Posix error() function --- RunCPM/abstraction_posix.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 050c8e9..90310a7 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -3,9 +3,9 @@ #ifdef STREAMIO2 #include +#include #endif #include -#include #include #include #include @@ -527,6 +527,14 @@ static struct argp argp = {options, parse_opt, NULL, "RunCPM - an emulator to run CP/M programs on modern hosts"}; #endif +static void _file_failure_exit(char *argv[], char* fmt, char* filename) +{ + fprintf(stderr, "%s: ", argv[0]); + fprintf(stderr, fmt, filename); + fprintf(stderr, ": %s", strerror(errno)); + exit(EXIT_FAILURE); +} + #ifdef STREAMIO static void _usage(char *argv[]) { fprintf(stderr, @@ -541,16 +549,16 @@ static void _parse_options(int argc, char *argv[]) { case 'i': streamInFile = fopen(optarg, "r"); if (NULL == streamInFile) { - error(EXIT_FAILURE, errno, - "error opening console input file %s", optarg); + _file_failure_exit(argv, + "error opening console input file %s", optarg); } streamInActive = TRUE; break; case 'o': streamOutFile = fopen(optarg, "w"); if (NULL == streamOutFile) { - error(EXIT_FAILURE, errno, - "error opening console output file %s", optarg); + _file_failure_exit(argv, + "error opening console output file %s", optarg); } break; case 's': @@ -571,7 +579,7 @@ static void _parse_options(int argc, char *argv[]) { } if (errflg || optind == argc) { _usage(argv); - exit(2); + exit(EXIT_FAILURE); } } } @@ -585,8 +593,8 @@ void _host_init(int argc, char* argv[]) { _parse_options(argc, argv); #endif if (chdir(dirname(argv[0]))) { - error(EXIT_FAILURE, errno, "error performing chdir(%s)", - dirname(argv[0])); + _file_failure_exit(argv, "error performing chdir(%s)", + dirname(argv[0])); } } From b19cecdfb47388be3a0cbaf5374e4f2b9e9a17ab Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 12:29:37 +0100 Subject: [PATCH 09/17] Remove linux non-posix option parsing. Add more details to usage(). --- RunCPM/abstraction_posix.h | 68 +++++++++----------------------------- 1 file changed, 15 insertions(+), 53 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 90310a7..0c0fc85 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -1,10 +1,6 @@ #ifndef ABSTRACT_H #define ABSTRACT_H -#ifdef STREAMIO2 -#include -#include -#endif #include #include #include @@ -481,52 +477,6 @@ uint32 _HardwareIn(const uint32 Port) { /* Host initialization functions */ /*===============================================================================*/ -#ifdef STREAMIO2 -static struct argp_option options[] = { - {"input", 'i', "FILE", 0, "File to read console input from"}, - {"output", 'o', "FILE", 0, "File to log console output to"}, - {"stdio", 's', NULL, 0, "Route console in/out to stdin/out"}, - {0} -}; - -static error_t parse_opt (int key, char *arg, struct argp_state *state) -{ - switch (key) - { - case 'i': - streamInFile = fopen(arg, "r"); - if (NULL == streamInFile) { - error(EXIT_FAILURE, errno, - "error opening console input file %s", arg); - } - streamInActive = TRUE; - break; - case 'o': - streamOutFile = fopen(arg, "w"); - if (NULL == streamOutFile) { - error(EXIT_FAILURE, errno, - "error opening console output file %s", arg); - } - break; - case 's': - streamInFile = stdin; - streamOutFile = stdout; - streamInActive = TRUE; - consoleOutActive = FALSE; - break; - case ARGP_KEY_ARG: - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static struct argp argp = {options, parse_opt, NULL, - "RunCPM - an emulator to run CP/M programs on modern hosts"}; -#endif - static void _file_failure_exit(char *argv[], char* fmt, char* filename) { fprintf(stderr, "%s: ", argv[0]); @@ -538,7 +488,22 @@ static void _file_failure_exit(char *argv[], char* fmt, char* filename) #ifdef STREAMIO static void _usage(char *argv[]) { fprintf(stderr, + "RunCPM - an emulator to run CP/M programs on modern hosts\n" "usage: %s [-i input_file] [-o output_file] [-s]\n", argv[0]); + fprintf(stderr, + " -i input_file: console input will be read from the file " + "with the\ngiven name. " + "After input file's EOF, further console input\nwill be read " + "from the keyboard.\n"); + fprintf(stderr, + " -o output_file: console output will be written to the file " + "with the\ngiven name, in addition to the screen.\n"); + fprintf(stderr, + " -s: console input and output is connected directly to " + "stdin and stdout.\nSince on Posix keyboard input is read from " + "stdin, switching from\nstdin to keyboard on stdin EOF is a " + "no-op. Therefore stdin EOF is an\nerror condition on Posix in " + "this case.\n"); } static void _parse_options(int argc, char *argv[]) { @@ -586,9 +551,6 @@ static void _parse_options(int argc, char *argv[]) { #endif void _host_init(int argc, char* argv[]) { -#ifdef STREAMIO2 - argp_parse(&argp, argc, argv, 0, NULL, NULL); -#endif #ifdef STREAMIO _parse_options(argc, argv); #endif From 5b71a7c6622ab6fde24a23ce4f31d95dd5f7c1c6 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 12:40:50 +0100 Subject: [PATCH 10/17] Add error handling for EOF on stdin to console.h --- RunCPM/console.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RunCPM/console.h b/RunCPM/console.h index 9ccfd72..7b32d0a 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -81,6 +81,18 @@ uint8 _chready(void) // Checks if there's a character ready for input { #ifdef STREAMIO if (streamInActive) return 0xff; + // TODO: delegate to abstrction_posix.h function canReadFromKbd() or + // something similar. + // On Posix, if !streamInActive && streamInFile == stdin, this means + // EOF on stdin. Assuming that stdin is connected to a file or pipe, + // further reading from stdin won't read from the keyboard but just + // continue to yield EOF. + // On Windows, this problem doesn't exist because of the separete + // conio.h. + if (streamInFile == stdin) { + _puts("EOF on console input from stdin\n"); + exit(EXIT_FAILURE); + } #endif return(_kbhit() ? 0xff : 0x00); } @@ -89,6 +101,7 @@ uint8 _getconNB(void) // Gets a character, non-blocking, no echo { #ifdef STREAMIO if (streamInActive) return _getStreamInChar(); + // TODO: Consider adding canReadFromKbd() here, too. #endif return(_kbhit() ? _getch() : 0x00); } @@ -97,6 +110,7 @@ uint8 _getcon(void) // Gets a character, blocking, no echo { #ifdef STREAMIO if (streamInActive) return _getStreamInChar(); + // TODO: Consider adding canReadFromKbd() here, too. #endif return _getch(); } @@ -105,6 +119,7 @@ uint8 _getconE(void) // Gets a character, blocking, with echo { #ifdef STREAMIO if (streamInActive) return _getStreamInCharEcho(); + // TODO: Consider adding canReadFromKbd() here, too. #endif return _getche(); } From ac748838c62130171eb41e519aa301da8e6d6f94 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 14:48:27 +0100 Subject: [PATCH 11/17] Fix optind == argc bug and add abort on stdin EOF --- RunCPM/abstraction_posix.h | 21 ++++++++++++++++++++- RunCPM/abstraction_vstudio.h | 7 +++++++ RunCPM/console.h | 23 ++++++++--------------- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 0c0fc85..45d54bf 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -542,7 +542,7 @@ static void _parse_options(int argc, char *argv[]) { "Unrecognized option: '-%c'\n", optopt); errflg++; } - if (errflg || optind == argc) { + if (errflg || optind != argc) { _usage(argv); exit(EXIT_FAILURE); } @@ -584,6 +584,25 @@ void _console_reset(void) { tcsetattr(0, TCSANOW, &_old_term); } +#ifdef STREAMIO +extern void _streamioReset(void); + +static void _abort_if_kbd_eof() { + // On Posix, if !streamInActive && streamInFile == stdin, this means + // EOF on stdin. Assuming that stdin is connected to a file or pipe, + // further reading from stdin won't read from the keyboard but just + // continue to yield EOF. + // On Windows, this problem doesn't exist because of the separete + // conio.h. + if (streamInFile == stdin) { + _puts("\nEOF on console input from stdin\n"); + _console_reset(); + _streamioReset(); + exit(EXIT_FAILURE); + } +} +#endif + int _kbhit(void) { struct pollfd pfds[1]; diff --git a/RunCPM/abstraction_vstudio.h b/RunCPM/abstraction_vstudio.h index cd869d4..7f53eee 100644 --- a/RunCPM/abstraction_vstudio.h +++ b/RunCPM/abstraction_vstudio.h @@ -490,6 +490,7 @@ void _host_init(int argc, char* argv[]) { } + /* Console abstraction functions */ /*===============================================================================*/ DWORD cOutMode; // Stores initial console mode for the std output @@ -511,6 +512,12 @@ void _console_init(void) { void _console_reset(void) { } +#ifdef STREAMIO +static void _abort_if_kbd_eof() { + +} +#endif + /* Implemented by conio.h int _kbhit(void) { diff --git a/RunCPM/console.h b/RunCPM/console.h index 7b32d0a..be5bd6c 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -81,18 +81,8 @@ uint8 _chready(void) // Checks if there's a character ready for input { #ifdef STREAMIO if (streamInActive) return 0xff; - // TODO: delegate to abstrction_posix.h function canReadFromKbd() or - // something similar. - // On Posix, if !streamInActive && streamInFile == stdin, this means - // EOF on stdin. Assuming that stdin is connected to a file or pipe, - // further reading from stdin won't read from the keyboard but just - // continue to yield EOF. - // On Windows, this problem doesn't exist because of the separete - // conio.h. - if (streamInFile == stdin) { - _puts("EOF on console input from stdin\n"); - exit(EXIT_FAILURE); - } + // TODO: Consider adding/keeping _abort_if_kbd_eof() here. + _abort_if_kbd_eof(); #endif return(_kbhit() ? 0xff : 0x00); } @@ -101,7 +91,8 @@ uint8 _getconNB(void) // Gets a character, non-blocking, no echo { #ifdef STREAMIO if (streamInActive) return _getStreamInChar(); - // TODO: Consider adding canReadFromKbd() here, too. + // TODO: Consider adding/keeping _abort_if_kbd_eof() here. + _abort_if_kbd_eof(); #endif return(_kbhit() ? _getch() : 0x00); } @@ -110,7 +101,8 @@ uint8 _getcon(void) // Gets a character, blocking, no echo { #ifdef STREAMIO if (streamInActive) return _getStreamInChar(); - // TODO: Consider adding canReadFromKbd() here, too. + // TODO: Consider adding/keeping _abort_if_kbd_eof() here. + _abort_if_kbd_eof(); #endif return _getch(); } @@ -119,7 +111,8 @@ uint8 _getconE(void) // Gets a character, blocking, with echo { #ifdef STREAMIO if (streamInActive) return _getStreamInCharEcho(); - // TODO: Consider adding canReadFromKbd() here, too. + // TODO: Consider adding/keeping _abort_if_kbd_eof() here. + _abort_if_kbd_eof(); #endif return _getche(); } From 9ab6d5b4de2b967a259ded565e295362bd906c94 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 15:18:13 +0100 Subject: [PATCH 12/17] Guard against -s with stdin coming from tty --- RunCPM/abstraction_posix.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 45d54bf..df2f1e2 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -16,6 +16,10 @@ #include #define millis() clock()/1000 +#ifdef STREAMIO +#include +#endif + // Lua scripting support #ifdef HASLUA #include "lua/lua.h" @@ -481,7 +485,10 @@ static void _file_failure_exit(char *argv[], char* fmt, char* filename) { fprintf(stderr, "%s: ", argv[0]); fprintf(stderr, fmt, filename); - fprintf(stderr, ": %s", strerror(errno)); + if (errno) { + fprintf(stderr, ": %s", strerror(errno)); + } + fprintf(stderr, "\n"); exit(EXIT_FAILURE); } @@ -509,6 +516,7 @@ static void _usage(char *argv[]) { static void _parse_options(int argc, char *argv[]) { int c; int errflg = 0; + struct termios dummyTermios; while ((c = getopt(argc, argv, ":i:o:s")) != -1) { switch(c) { case 'i': @@ -527,6 +535,23 @@ static void _parse_options(int argc, char *argv[]) { } break; case 's': +// fprintf(stderr, "tcgetattr() = %d\n", +// tcgetattr(0, &dummyTermios)); +// fprintf(stderr, "\nerrno = %d\n", errno); +// fprintf(stderr, "ENOTTY = %d\n", ENOTTY); +// fprintf(stderr, "dummyTermios:\n" +// " c_iflag = %x\n c_oflag = %x\n" +// " c_cflag = %x\n c_lflag = %x\n", +// dummyTermios.c_iflag, dummyTermios.c_oflag, +// dummyTermios.c_cflag, dummyTermios.c_lflag); + if (0 == tcgetattr(0, &dummyTermios) || + errno != ENOTTY) { +// fprintf(stderr, "\nerrno = %d\n", errno); +// fprintf(stderr, "ENOTTY = %d\n", ENOTTY); + _file_failure_exit(argv, + "option -s is illegal when stdin comes from %s", + "tty"); + } streamInFile = stdin; streamOutFile = stdout; streamInActive = TRUE; From a94589035c561ed2befa261ce87be51ebf65d988 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 15:29:31 +0100 Subject: [PATCH 13/17] Rename *In/Out* global variables to *Input/Output* --- RunCPM/abstraction_posix.h | 28 ++++++++++++++-------------- RunCPM/console.h | 18 +++++++++--------- RunCPM/globals.h | 8 ++++---- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index df2f1e2..0134eeb 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -520,16 +520,16 @@ static void _parse_options(int argc, char *argv[]) { while ((c = getopt(argc, argv, ":i:o:s")) != -1) { switch(c) { case 'i': - streamInFile = fopen(optarg, "r"); - if (NULL == streamInFile) { + streamInputFile = fopen(optarg, "r"); + if (NULL == streamInputFile) { _file_failure_exit(argv, "error opening console input file %s", optarg); } - streamInActive = TRUE; + streamInputActive = TRUE; break; case 'o': - streamOutFile = fopen(optarg, "w"); - if (NULL == streamOutFile) { + streamOutputFile = fopen(optarg, "w"); + if (NULL == streamOutputFile) { _file_failure_exit(argv, "error opening console output file %s", optarg); } @@ -552,10 +552,10 @@ static void _parse_options(int argc, char *argv[]) { "option -s is illegal when stdin comes from %s", "tty"); } - streamInFile = stdin; - streamOutFile = stdout; - streamInActive = TRUE; - consoleOutActive = FALSE; + streamInputFile = stdin; + streamOutputFile = stdout; + streamInputActive = TRUE; + consoleOutputActive = FALSE; break; case ':': /* -f or -o without operand */ fprintf(stderr, @@ -613,13 +613,13 @@ void _console_reset(void) { extern void _streamioReset(void); static void _abort_if_kbd_eof() { - // On Posix, if !streamInActive && streamInFile == stdin, this means - // EOF on stdin. Assuming that stdin is connected to a file or pipe, - // further reading from stdin won't read from the keyboard but just - // continue to yield EOF. + // On Posix, if !streamInputActive && streamInputFile == stdin, + // this means EOF on stdin. Assuming that stdin is connected to a + // file or pipe, further reading from stdin won't read from the + // keyboard but just continue to yield EOF. // On Windows, this problem doesn't exist because of the separete // conio.h. - if (streamInFile == stdin) { + if (streamInputFile == stdin) { _puts("\nEOF on console input from stdin\n"); _console_reset(); _streamioReset(); diff --git a/RunCPM/console.h b/RunCPM/console.h index be5bd6c..c205552 100644 --- a/RunCPM/console.h +++ b/RunCPM/console.h @@ -13,8 +13,8 @@ uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM relat void _putcon(uint8 ch) // Puts a character { #ifdef STREAMIO - if (consoleOutActive) _putch(ch & mask8bit); - if (streamOutFile) fputc(ch & mask8bit, streamOutFile); + if (consoleOutputActive) _putch(ch & mask8bit); + if (streamOutputFile) fputc(ch & mask8bit, streamOutputFile); #else _putch(ch & mask8bit); #endif @@ -44,9 +44,9 @@ int _nextStreamInChar; void _getNextStreamInChar(void) { - _nextStreamInChar = streamInFile ? fgetc(streamInFile) : EOF; + _nextStreamInChar = streamInputFile ? fgetc(streamInputFile) : EOF; if (EOF == _nextStreamInChar) { - streamInActive = FALSE; + streamInputActive = FALSE; } } @@ -73,14 +73,14 @@ void _streamioInit(void) void _streamioReset(void) { - if (streamOutFile) fclose(streamOutFile); + if (streamOutputFile) fclose(streamOutputFile); } #endif uint8 _chready(void) // Checks if there's a character ready for input { #ifdef STREAMIO - if (streamInActive) return 0xff; + if (streamInputActive) return 0xff; // TODO: Consider adding/keeping _abort_if_kbd_eof() here. _abort_if_kbd_eof(); #endif @@ -90,7 +90,7 @@ uint8 _chready(void) // Checks if there's a character ready for input uint8 _getconNB(void) // Gets a character, non-blocking, no echo { #ifdef STREAMIO - if (streamInActive) return _getStreamInChar(); + if (streamInputActive) return _getStreamInChar(); // TODO: Consider adding/keeping _abort_if_kbd_eof() here. _abort_if_kbd_eof(); #endif @@ -100,7 +100,7 @@ uint8 _getconNB(void) // Gets a character, non-blocking, no echo uint8 _getcon(void) // Gets a character, blocking, no echo { #ifdef STREAMIO - if (streamInActive) return _getStreamInChar(); + if (streamInputActive) return _getStreamInChar(); // TODO: Consider adding/keeping _abort_if_kbd_eof() here. _abort_if_kbd_eof(); #endif @@ -110,7 +110,7 @@ uint8 _getcon(void) // Gets a character, blocking, no echo uint8 _getconE(void) // Gets a character, blocking, with echo { #ifdef STREAMIO - if (streamInActive) return _getStreamInCharEcho(); + if (streamInputActive) return _getStreamInCharEcho(); // TODO: Consider adding/keeping _abort_if_kbd_eof() here. _abort_if_kbd_eof(); #endif diff --git a/RunCPM/globals.h b/RunCPM/globals.h index 9398709..e85b120 100644 --- a/RunCPM/globals.h +++ b/RunCPM/globals.h @@ -226,10 +226,10 @@ static uint32 timer; #ifdef STREAMIO #include -static FILE *streamInFile = NULL; -static FILE *streamOutFile = NULL; -static uint8 streamInActive = FALSE; -static uint8 consoleOutActive = TRUE; +static FILE *streamInputFile = NULL; +static FILE *streamOutputFile = NULL; +static uint8 streamInputActive = FALSE; +static uint8 consoleOutputActive = TRUE; #endif From 0abca29b442a83f2d680cb1b191f3cfefe9c81ee Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 15:31:54 +0100 Subject: [PATCH 14/17] Put full failure condition into _abort_if_kbd_eof() --- RunCPM/abstraction_posix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index 0134eeb..c8ff456 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -619,7 +619,7 @@ static void _abort_if_kbd_eof() { // keyboard but just continue to yield EOF. // On Windows, this problem doesn't exist because of the separete // conio.h. - if (streamInputFile == stdin) { + if (!streamInputActive && streamInputFile == stdin) { _puts("\nEOF on console input from stdin\n"); _console_reset(); _streamioReset(); From b5ae6021c00f86151e516d19d22526f9a5507726 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 15:48:00 +0100 Subject: [PATCH 15/17] Move check whether all args were parsed out of getopt while loop --- RunCPM/abstraction_posix.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index c8ff456..a915eb7 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -567,10 +567,10 @@ static void _parse_options(int argc, char *argv[]) { "Unrecognized option: '-%c'\n", optopt); errflg++; } - if (errflg || optind != argc) { - _usage(argv); - exit(EXIT_FAILURE); - } + } + if (errflg || optind != argc) { + _usage(argv); + exit(EXIT_FAILURE); } } #endif From 092721bfbe862472bade5116da8fc879d261b275 Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sun, 17 Dec 2023 15:53:10 +0100 Subject: [PATCH 16/17] extract _fail_if_stdin_from_tty() from -s option parsing. Remove debug code --- RunCPM/abstraction_posix.h | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/RunCPM/abstraction_posix.h b/RunCPM/abstraction_posix.h index a915eb7..0bd412e 100644 --- a/RunCPM/abstraction_posix.h +++ b/RunCPM/abstraction_posix.h @@ -513,10 +513,19 @@ static void _usage(char *argv[]) { "this case.\n"); } +static void _fail_if_stdin_from_tty(char* argv[]) { + struct termios dummyTermios; + if (0 == tcgetattr(0, &dummyTermios) || + errno != ENOTTY) { + _file_failure_exit(argv, + "option -s is illegal when stdin comes from %s", + "tty"); + } +} + static void _parse_options(int argc, char *argv[]) { int c; int errflg = 0; - struct termios dummyTermios; while ((c = getopt(argc, argv, ":i:o:s")) != -1) { switch(c) { case 'i': @@ -535,29 +544,13 @@ static void _parse_options(int argc, char *argv[]) { } break; case 's': -// fprintf(stderr, "tcgetattr() = %d\n", -// tcgetattr(0, &dummyTermios)); -// fprintf(stderr, "\nerrno = %d\n", errno); -// fprintf(stderr, "ENOTTY = %d\n", ENOTTY); -// fprintf(stderr, "dummyTermios:\n" -// " c_iflag = %x\n c_oflag = %x\n" -// " c_cflag = %x\n c_lflag = %x\n", -// dummyTermios.c_iflag, dummyTermios.c_oflag, -// dummyTermios.c_cflag, dummyTermios.c_lflag); - if (0 == tcgetattr(0, &dummyTermios) || - errno != ENOTTY) { -// fprintf(stderr, "\nerrno = %d\n", errno); -// fprintf(stderr, "ENOTTY = %d\n", ENOTTY); - _file_failure_exit(argv, - "option -s is illegal when stdin comes from %s", - "tty"); - } + _fail_if_stdin_from_tty(argv); streamInputFile = stdin; streamOutputFile = stdout; streamInputActive = TRUE; consoleOutputActive = FALSE; break; - case ':': /* -f or -o without operand */ + case ':': /* -i or -o without operand */ fprintf(stderr, "Option -%c requires an operand\n", optopt); errflg++; From c2f4a3c02edaa7b38b3beeb00032ce3047817a4d Mon Sep 17 00:00:00 2001 From: Philip Zembrod Date: Sat, 13 Jan 2024 23:10:57 +0100 Subject: [PATCH 17/17] Add _parse_options() to abstraction_vstudio.h --- RunCPM/abstraction_vstudio.h | 89 +++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/RunCPM/abstraction_vstudio.h b/RunCPM/abstraction_vstudio.h index 7f53eee..fd55ad0 100644 --- a/RunCPM/abstraction_vstudio.h +++ b/RunCPM/abstraction_vstudio.h @@ -486,10 +486,92 @@ uint32 _HardwareIn(const uint32 Port) { /* Host initialization functions */ /*===============================================================================*/ -void _host_init(int argc, char* argv[]) { +#ifdef STREAMIO +static void _abort_if_kbd_eof() { +} +static void _file_failure_exit(char *argv[], char* fmt, char* filename) +{ + fprintf(stderr, "%s: ", argv[0]); + fprintf(stderr, fmt, filename); + if (errno) { + fprintf(stderr, ": %s", strerror(errno)); + } + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +static void _usage(char *argv[]) { + fprintf(stderr, + "RunCPM - an emulator to run CP/M programs on modern hosts\n" + "usage: %s [-i input_file] [-o output_file] [-s]\n", argv[0]); + fprintf(stderr, + " -i input_file: console input will be read from the file " + "with the\n given name. " + "After input file's EOF, further console input\n will be read " + "from the keyboard.\n"); + fprintf(stderr, + " -o output_file: console output will be written to the file " + "with the\n given name, in addition to the screen.\n"); + fprintf(stderr, + " -s: console input and output is connected directly to " + "stdin and stdout.\n"); +} + +#define SET_OPTARG ++i; if (i >= argc) {++errflg; break;} optarg = argv[i]; + +static void _parse_options(int argc, char *argv[]) { + int errflg = 0; + char *optarg; + for (int i = 1; i < argc && errflg == 0; ++i) { + if (strcmp("-i", argv[i]) == 0) { + /* ++i; + if (i >= argc) { + ++errflg; + break; + } + optarg = argv[i]; */ + SET_OPTARG + streamInputFile = fopen(optarg, "r"); + if (NULL == streamInputFile) { + _file_failure_exit(argv, + "error opening console input file %s", optarg); + } + streamInputActive = TRUE; + continue; + } + if (strcmp("-o", argv[i]) == 0) { + SET_OPTARG + streamOutputFile = fopen(optarg, "w"); + if (NULL == streamOutputFile) { + _file_failure_exit(argv, + "error opening console output file %s", optarg); + } + continue; + } + if (strcmp("-s", argv[i]) == 0) { + streamInputFile = stdin; + streamOutputFile = stdout; + streamInputActive = TRUE; + consoleOutputActive = FALSE; + continue; + } + fprintf(stderr, + "Unrecognized option: '%s'\n", argv[i]); + errflg++; + } + if (errflg) { + _usage(argv); + exit(EXIT_FAILURE); + } } +#endif +void _host_init(int argc, char* argv[]) { +#ifdef STREAMIO + _parse_options(argc, argv); +#endif +} /* Console abstraction functions */ /*===============================================================================*/ @@ -512,11 +594,6 @@ void _console_init(void) { void _console_reset(void) { } -#ifdef STREAMIO -static void _abort_if_kbd_eof() { - -} -#endif /* Implemented by conio.h int _kbhit(void)