Skip to content

Commit

Permalink
Merge pull request MockbaTheBorg#193 from pzembrod/streamio (args / c…
Browse files Browse the repository at this point in the history
…onsole)

Adds command line arguments and console (stdin/stdout) support to RunCPM.
  • Loading branch information
MockbaTheBorg authored May 7, 2024
2 parents 3771101 + 801715b commit f98ee01
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 20 deletions.
119 changes: 118 additions & 1 deletion RunCPM/abstraction_posix.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef ABSTRACT_H
#define ABSTRACT_H

#include <errno.h>
#include <glob.h>
#include <ctype.h>
#include <stdio.h>
Expand All @@ -15,6 +16,10 @@
#include <time.h>
#define millis() clock()/1000

#ifdef STREAMIO
#include <termios.h>
#endif

// Lua scripting support
#ifdef HASLUA
#include "lua/lua.h"
Expand Down Expand Up @@ -476,8 +481,101 @@ uint32 _HardwareIn(const uint32 Port) {
/* Host initialization functions */
/*===============================================================================*/

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);
}

#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 _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;
while ((c = getopt(argc, argv, ":i:o:s")) != -1) {
switch(c) {
case 'i':
streamInputFile = fopen(optarg, "r");
if (NULL == streamInputFile) {
_file_failure_exit(argv,
"error opening console input file %s", optarg);
}
streamInputActive = TRUE;
break;
case 'o':
streamOutputFile = fopen(optarg, "w");
if (NULL == streamOutputFile) {
_file_failure_exit(argv,
"error opening console output file %s", optarg);
}
break;
case 's':
_fail_if_stdin_from_tty(argv);
streamInputFile = stdin;
streamOutputFile = stdout;
streamInputActive = TRUE;
consoleOutputActive = FALSE;
break;
case ':': /* -i 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(EXIT_FAILURE);
}
}
#endif

void _host_init(int argc, char* argv[]) {
int x = chdir(dirname(argv[0]));
#ifdef STREAMIO
_parse_options(argc, argv);
#endif
if (chdir(dirname(argv[0]))) {
_file_failure_exit(argv, "error performing chdir(%s)",
dirname(argv[0]));
}
}

/* Console abstraction functions */
Expand All @@ -504,6 +602,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 !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 (!streamInputActive && streamInputFile == stdin) {
_puts("\nEOF on console input from stdin\n");
_console_reset();
_streamioReset();
exit(EXIT_FAILURE);
}
}
#endif

int _kbhit(void) {
struct pollfd pfds[1];

Expand Down
86 changes: 85 additions & 1 deletion RunCPM/abstraction_vstudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,91 @@ 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 */
Expand All @@ -511,6 +594,7 @@ void _console_init(void) {
void _console_reset(void) {
}


/* Implemented by conio.h
int _kbhit(void)
{
Expand Down
96 changes: 85 additions & 11 deletions RunCPM/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,15 @@ 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
{
#ifdef STREAMIO
if (consoleOutputActive) _putch(ch & mask8bit);
if (streamOutputFile) fputc(ch & mask8bit, streamOutputFile);
#else
_putch(ch & mask8bit);
#endif

}

void _puts(const char* str) // Puts a \0 terminated string
Expand All @@ -43,4 +39,82 @@ void _puthex16(uint16 w) // puts a HHHH hex string
_puthex8(w & 0x00ff);
}

#endif
#ifdef STREAMIO
int _nextStreamInChar;

void _getNextStreamInChar(void)
{
_nextStreamInChar = streamInputFile ? fgetc(streamInputFile) : EOF;
if (EOF == _nextStreamInChar) {
streamInputActive = FALSE;
}
}

uint8 _getStreamInChar(void)
{
uint8 result = _nextStreamInChar;
_getNextStreamInChar();
// TODO: delegate to abstrction_posix.h
if (0x0a == result) result = 0x0d;
return result;
}

uint8 _getStreamInCharEcho()
{
uint8 result = _getStreamInChar();
_putcon(result);
return result;
}

void _streamioInit(void)
{
_getNextStreamInChar();
}

void _streamioReset(void)
{
if (streamOutputFile) fclose(streamOutputFile);
}
#endif

uint8 _chready(void) // Checks if there's a character ready for input
{
#ifdef STREAMIO
if (streamInputActive) return 0xff;
// TODO: Consider adding/keeping _abort_if_kbd_eof() here.
_abort_if_kbd_eof();
#endif
return(_kbhit() ? 0xff : 0x00);
}

uint8 _getconNB(void) // Gets a character, non-blocking, no echo
{
#ifdef STREAMIO
if (streamInputActive) return _getStreamInChar();
// TODO: Consider adding/keeping _abort_if_kbd_eof() here.
_abort_if_kbd_eof();
#endif
return(_kbhit() ? _getch() : 0x00);
}

uint8 _getcon(void) // Gets a character, blocking, no echo
{
#ifdef STREAMIO
if (streamInputActive) return _getStreamInChar();
// TODO: Consider adding/keeping _abort_if_kbd_eof() here.
_abort_if_kbd_eof();
#endif
return _getch();
}

uint8 _getconE(void) // Gets a character, blocking, with echo
{
#ifdef STREAMIO
if (streamInputActive) return _getStreamInCharEcho();
// TODO: Consider adding/keeping _abort_if_kbd_eof() here.
_abort_if_kbd_eof();
#endif
return _getche();
}

#endif
8 changes: 4 additions & 4 deletions RunCPM/cpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -698,7 +698,7 @@ void _Bdos(void) {
Returns: A=Char
*/
case C_READ: {
HL = _getche();
HL = _getconE();
#ifdef DEBUG
if (HL == 4)
Debug = 1;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit f98ee01

Please sign in to comment.