CTRL-C processing
CTRL-C processing
I made a quick program that just dumps a file with hex and ASCII. All it does it use printf to output. When testing ti under windows using a cpm player, it seems to support CTRL-C for program termination, but that could be something in the cpm player/emulator. When I try it on an actual CP/M system:
https://www.tindie.com/products/tindies ... puter-kit/
It will at first respond to CTRL-S to pause the screen and any key to unpause, but when I hit CTRL-C, it does not terminate. Also once I press CTRL-C once, it will no longer response to CTRL-S any longer.
https://www.tindie.com/products/tindies ... puter-kit/
It will at first respond to CTRL-S to pause the screen and any key to unpause, but when I hit CTRL-C, it does not terminate. Also once I press CTRL-C once, it will no longer response to CTRL-S any longer.
Re: CTRL-C processing
I've just checked the source code for the low level cp/m console input and output routines in z88dk. Please don't take this as any definitive answer to your problem but these routines seem to just call the computer's native bios functionality.
To me that implies the behaviour you describe might be a problem with the computer's bios implementation.
Also, could you post your source code? That might help folks figure things out better.
To me that implies the behaviour you describe might be a problem with the computer's bios implementation.
Also, could you post your source code? That might help folks figure things out better.
Re: CTRL-C processing
There's no special handling for ^C handling - I suspect the same as you that the aborting on the emulator is due to the emulator catching the signal.
I can't remember if you can building the libraries yourself, but I think we can catch it by editing libsrc/target/cpm/stdio/fgetc_cons.asm and jumping to exit if the returned value from the BDOS call is 3.
If that works, it probably ought to be dressed up into a runtime/compile time option - I suspect the default behaviour of catching ^C might be a bit annoying!
I can't remember if you can building the libraries yourself, but I think we can catch it by editing libsrc/target/cpm/stdio/fgetc_cons.asm and jumping to exit if the returned value from the BDOS call is 3.
If that works, it probably ought to be dressed up into a runtime/compile time option - I suspect the default behaviour of catching ^C might be a bit annoying!
Re: CTRL-C processing
Oh, but you're not reading the keyboard in anyway! That's an interesting one - maybe we need to do the same for getk() and you use call getk() for every loop?
EDIT: Actually you don't even need to do anything to the libraries, just have "if ( getk() == 3 ) exit(1)" every loop?
EDIT: Actually you don't even need to do anything to the libraries, just have "if ( getk() == 3 ) exit(1)" every loop?
Re: CTRL-C processing
The thing is, I'm not sure I would always want CTRL+C processing all the time. It is one of those things that is nice to be on by default, but a simple function call at the beginning of a program would disable it.
nuc1e0n - I wonder if I just call getk if that alone would check for ctrl-c. I'll test this later tonight.
I've been playing around with a cp/m compiler as well (hitech c) and z88dk produces an 8080 executable that is 20% smaller than hitech's z80 executable.
In this program I can easily put in dom's getk/exit test.
The code is pretty simple:
nuc1e0n - I wonder if I just call getk if that alone would check for ctrl-c. I'll test this later tonight.
I've been playing around with a cp/m compiler as well (hitech c) and z88dk produces an 8080 executable that is 20% smaller than hitech's z80 executable.
In this program I can easily put in dom's getk/exit test.
The code is pretty simple:
Code: Select all
#include <stdio.h>
void main(int argc, char** argv)
{
FILE *infile;
unsigned int i1, i2;
unsigned long pos;
unsigned char c, buf[128];
/*options*/
if (argc!=2)
{
printf("Usage: NDUMP filename\n");
return;
}
/*open file*/
infile=fopen(argv[1], "rb");
if (infile==NULL)
{
printf("Unable to open file\n");
return;
}
pos=0;
while (!feof(infile))
{
if (fread(buf, 128, 1, infile)!=1)
continue;
for (i1=0; i1<8; i1++)
{
printf("%06lX: ", pos+i1*16);
for (i2=0; i2<16; i2++)
printf("%02X ", buf[i1*16+i2]);
printf(" ");
for (i2=0; i2<16; i2++)
{
c=buf[i1*16+i2];
if (c>=32 && c<=128)
printf("%c", c);
else printf(".");
}
printf("\n");
}
pos+=128;
}
fclose(infile);
}
Re: CTRL-C processing
Yeah I agree calling getk in a loop would probably be a good way to check for ctrl-c being pressed and exit if it has been.
Dom would definitely know better than me of course as I'm only a user of z88dk myself, although I have been using it for a while now.
Dom would definitely know better than me of course as I'm only a user of z88dk myself, although I have been using it for a while now.
Re: CTRL-C processing
I wonder if there is a bios issue going on with this.
I added this into the loop:
When I start it on the SC131, the first character I type never shows. When I type the second character, it shows. Then if I type CTRL+C, it breaks and the first character appears at the cp/m prompt!
I added this into the loop:
Code: Select all
c=getk();
if (c)
printf("\n\n<<%d>>\n\n", c);
if ( c == 3 ) exit(1);
Re: CTRL-C processing
The getk() code interacts with the bios rather than bdos because it needs to check the console status. I do wonder if calling bdos 6, e=0xff would be a more portable solution.
I'm wondering if a buffer somewhere in BDOS/BIOS is getting out of sync because of this.
The good news though is that the principle works and you can break out!
I'm wondering if a buffer somewhere in BDOS/BIOS is getting out of sync because of this.
The good news though is that the principle works and you can break out!
Re: CTRL-C processing
I wonder this too. I developed some code in the past and when I test it using this, it works as expected in both places (cpm player and on the sc131 under cpm). This function waits for a key, but it can be modifed to pull the cp/jp so that it returns a 0 instead.
I also have a kbhit/getch that I've used before, but I haven't tested it on these two (cpm player/sc131) yet. It counts on getk()
Code: Select all
unsigned char g() __smallc __naked
{
__asm
loop1:
ld c,6 //bdos function 6
ld e,0xff //bdos parameter return character without echoing
call 5 //call bdos
cp a,0 //if character is zero (no character), wait until a key is pressed
jp z,loop1
ld l,a //copy result a into l for return
ret
__endasm;
}
Code: Select all
//cp/m specific
#if defined(__SCCZ80) || defined(__SDCC)
unsigned char keyread;
unsigned char kbhit()
{
unsigned char c1;
//if we've previously read a key then return 1
if (keyread)
return 1;
//let's get a key
c1=getk();
//if it is zero then return 0, no key is ready
if (c1==0)
return 0;
//if not we do have a key, cache it in keyread and return 1
keyread=c1;
return 1;
}
unsigned char getch()
{
unsigned char c1;
//if we have a cached key, return it
if (keyread)
{
c1=keyread;
keyread=0;
return c1;
}
//we must wait on a key and return it
do
{
c1=getk();
} while (c1==0);
return c1;
}
#endif
Re: CTRL-C processing
The issue is related to the SC131 bios somehow.
When the simple loop printing a . is running, it works correctly.
When the area is enabled that dumps in hex/text, it does not work correctly on the SC131, but still works fine in the cpm player emulator.
I've tried z80 and 8080 compiles, -O0 and -O3 and the behavior is the same, so not an issue with these parameters.
When the simple loop printing a . is running, it works correctly.
When the area is enabled that dumps in hex/text, it does not work correctly on the SC131, but still works fine in the cpm player emulator.
I've tried z80 and 8080 compiles, -O0 and -O3 and the behavior is the same, so not an issue with these parameters.
Code: Select all
#include <stdio.h>
unsigned char g() __smallc __naked
{
__asm
loop1:
ld c,6 //bdos function 6
ld e,0xff //bdos parameter return character without echoing
call 5 //call bdos
//cp a,0 //if character is zero (no character), wait until a key is pressed
//jp z,loop1
ld l,a //copy result a into l for return
ret
__endasm;
}
//cp/m specific
#if defined(__SCCZ80) || defined(__SDCC)
unsigned char keyread;
unsigned char kbhit()
{
unsigned char c1;
//if we've previously read a key then return 1
if (keyread)
return 1;
//let's get a key
c1=g();
//if it is zero then return 0, no key is ready
if (c1==0)
return 0;
//if not we do have a key, cache it in keyread and return 1
keyread=c1;
return 1;
}
unsigned char getch()
{
unsigned char c1;
//if we have a cached key, return it
if (keyread)
{
c1=keyread;
keyread=0;
return c1;
}
//we must wait on a key and return it
do
{
c1=g();
} while (c1==0);
return c1;
}
#endif
void main(int argc, char** argv)
{
FILE *infile;
unsigned int i1, i2;
unsigned long pos;
unsigned char c, buf[128];
/*options*/
if (argc!=2)
{
printf("Usage: NDUMP filename\n");
return;
}
/*open file*/
infile=fopen(argv[1], "rb");
if (infile==NULL)
{
printf("Unable to open file\n");
return;
}
pos=0;
while (!feof(infile))
{
if (kbhit())
{
c=getch();
if (c)
printf("\n\n<<%d>>\n\n", c);
if (c==3)
exit(1);
}
if (fread(buf, 128, 1, infile)!=1)
continue;
//this works fine
printf(".");
for (i1=0; i1<10000; i1++)
;
//when enabled, this does not work fine
/*for (i1=0; i1<8; i1++)
{
printf("%06lX: ", pos+i1*16);
for (i2=0; i2<16; i2++)
printf("%02X ", buf[i1*16+i2]);
printf(" ");
for (i2=0; i2<16; i2++)
{
c=buf[i1*16+i2];
if (c>=32 && c<=128)
printf("%c", c);
else printf(".");
}
printf("\n");
}*/
pos+=128;
}
fclose(infile);
}
Re: CTRL-C processing
It is an issue with the cp/m bios. If I boot it under z-system, it works fine.
Re: CTRL-C processing
I think the sc131 console is a serial connection?
I think your simple test might be showing that you can't read from the console at the same time as writing to it?
I think your simple test might be showing that you can't read from the console at the same time as writing to it?
Re: CTRL-C processing
There is something there; the maintainer of the bios is going to look at it. RX and TX are dedicated (full duplex), but I suspect there is something going on in the bios.
Re: CTRL-C processing
There is a warning on this site about not mixing the console access methods -
https://www.seasip.info/Cpm/bdos.html
BDOS function 6 (C_RAWIO) - Direct console I/O
Supported by: CP/M 1.4 and later, with variations
Entered with C=6, E=code. Returned values (in A) vary.
E=0FFh
Return a character without echoing if one is waiting; zero if none is available. In MP/M 1, this works like E=0FDh below and waits for a character.
E=0FEh
[CP/M3, NovaDOS, Z80DOS, DOS+] Return console input status. Zero if no character is waiting, nonzero otherwise.
E=0FDh
[CP/M3, DOS+] Wait until a character is ready, return it without echoing.
E=0FCh
[DOS+] One-character lookahead - return the next character waiting but leave it in the buffer.
Values of E not supported on a particular system will output the character. Under CP/M 2 and lower, direct console functions may interact undesirably with non-direct ones, since certain buffers may be bypassed. Do not mix them.
How is the putc() implemented? Does it use BDOS function 2 (C_WRITE)? Or does it use function 6 with E<0xFC?
How can I override this function? I tried:
But printf still prints.
https://www.seasip.info/Cpm/bdos.html
BDOS function 6 (C_RAWIO) - Direct console I/O
Supported by: CP/M 1.4 and later, with variations
Entered with C=6, E=code. Returned values (in A) vary.
E=0FFh
Return a character without echoing if one is waiting; zero if none is available. In MP/M 1, this works like E=0FDh below and waits for a character.
E=0FEh
[CP/M3, NovaDOS, Z80DOS, DOS+] Return console input status. Zero if no character is waiting, nonzero otherwise.
E=0FDh
[CP/M3, DOS+] Wait until a character is ready, return it without echoing.
E=0FCh
[DOS+] One-character lookahead - return the next character waiting but leave it in the buffer.
Values of E not supported on a particular system will output the character. Under CP/M 2 and lower, direct console functions may interact undesirably with non-direct ones, since certain buffers may be bypassed. Do not mix them.
How is the putc() implemented? Does it use BDOS function 2 (C_WRITE)? Or does it use function 6 with E<0xFC?
How can I override this function? I tried:
Code: Select all
int fputc_cons(char c)
{
return 0;
}
Re: CTRL-C processing
I'm surprised that input and output are clashing - I read that caveat as you might have problems if you mix calls on the input, but only on certain flavours of CP/M.
Overloading fputc_cons is done in a slightly different way:
Overloading fputc_cons is done in a slightly different way:
Code: Select all
int myputc(int c) { }
-pragma-redirect:fputc_cons=myputc
Re: CTRL-C processing
I think it is a CP/M 2.2 BDOS bug.
Re: CTRL-C processing
Thanks dom - here is what I worked out that switches to direct i/o for the console i/o and works even with the bdos bug:
I have to add this to the zcc command line options:
-pragma-redirect:fputc_cons=_putch
I have to add this to the zcc command line options:
-pragma-redirect:fputc_cons=_putch
Code: Select all
//console direct i/o
//cp/m specific
//#if defined(__SCCZ80) || defined(__SDCC)
//#endif
unsigned char keyread;
unsigned char c_rawio_get() __smallc __naked
{
__asm
ld c,6 //bdos function 6
ld e,0xff //bdos parameter return character without echoing
call 5 //call bdos
ld l,a //copy result a into l for return
ret
__endasm;
}
void c_rawio_put(char b) __naked __z88dk_fastcall
{
__asm
ld c,6 //bdos function 6
ld e,l //bdos output from l
call 5 //call bdos
ret
__endasm;
}
unsigned char kbhit()
{
unsigned char c1;
//if we've previously read a key then return 1
if (keyread)
return 1;
//let's get a key
c1=c_rawio_get();
//if it is zero then return 0, no key is ready
if (c1==0)
return 0;
//if not we do have a key, cache it in keyread and return 1
keyread=c1;
return 1;
}
unsigned char getch()
{
unsigned char c1;
//if we have a cached key, return it
if (keyread)
{
c1=keyread;
keyread=0;
return c1;
}
//we must wait on a key and return it
do
{
c1=c_rawio_get();
} while (c1==0);
return c1;
}
int putch(int c)
{
if (c<0xfc)
{
//pause for ctrl-s
kbhit();
if (keyread==19)
{
keyread=0;
if (getch()==3)
keyread=3;
}
if (c=='\n')
c_rawio_put('\r');
c_rawio_put(c);
return c;
}
else return EOF;
}
Re: CTRL-C processing
It could also easily be modified to look exit(1) instead of an outer main program loop having to check for it.
Re: CTRL-C processing
Modified to support CTRL-C exiting. Added a variable that can be changed externally to enable to disable CTRL-C exiting.
Code: Select all
//console direct i/o
//cp/m specific
//#if defined(__SCCZ80) || defined(__SDCC)
//#endif
unsigned char keyread, enablectrlcexit=1;
unsigned char c_rawio_get() __smallc __naked
{
__asm
ld c,6 //bdos function 6
ld e,0xff //bdos parameter return character without echoing
call 5 //call bdos
ld l,a //copy result a into l for return
ret
__endasm;
}
void c_rawio_put(char b) __naked __z88dk_fastcall
{
__asm
ld c,6 //bdos function 6
ld e,l //bdos output from l
call 5 //call bdos
ret
__endasm;
}
unsigned char kbhit()
{
unsigned char c1;
//if we've previously read a key then return 1
if (keyread)
return 1;
//let's get a key
c1=c_rawio_get();
//if it is zero then return 0, no key is ready
if (c1==0)
return 0;
//if not we do have a key, cache it in keyread and return 1
keyread=c1;
return 1;
}
unsigned char getch()
{
unsigned char c1;
//if we have a cached key, return it
if (keyread)
{
c1=keyread;
keyread=0;
return c1;
}
//we must wait on a key and return it
do
{
c1=c_rawio_get();
} while (c1==0);
return c1;
}
int putch(int c)
{
if (c<0xfc)
{
//pause for ctrl-s
kbhit();
if (keyread==19)
{
//clear the ctrl-s
keyread=0;
//pause and wait for a key, if it is ctrl-c leave it in the buffer
if (getch()==3)
goto quit;
}
else
if (keyread==3)
{
quit:
if (enablectrlcexit)
{
c_rawio_put('^');
c_rawio_put('C');
exit(1);
}
}
if (c=='\n')
c_rawio_put('\r');
c_rawio_put(c);
return c;
}
else return EOF;
}
Re: CTRL-C processing
Also, can this option:
-pragma-redirect:fputc_cons=_putch
be put in code instead of on the command line?
-pragma-redirect:fputc_cons=_putch
be put in code instead of on the command line?
Re: CTRL-C processing
Interesting, so is the solution checking for the keypress before printing or is it using the direct io print character call?
You should be able to:
In code or zpragma.inc
You should be able to:
Code: Select all
#pragma redirect fputc_cons=_putch
Re: CTRL-C processing
Hi dom,
The solution is to not mix the API access methods. In other words, don't use the non-RAW function for printing, but the RAW function for character input. The problem is that the non-RAW function for getting a key always prints it to the console and that may not be a good thing for some applications. So the only way to have a true getch() that doesn't echo to the screen is to use the RAW function. Once doing that, one should also use the RAW function for output as well. Doing that means that you are leaving the way CP/M itself handles XON/XOFF and also how it must convert \n to \r\n as well. Essentially what I did was switch to using the RAW character output so that I could properly use the RAW character input as well. I then added support for xon/xoff, cr translation, and optional program exit.
Ideally I would think if you could do the same with the library that would be cool, but I wonder about an incompatibility of programs using the raw output vs. the one you are using now. The raw output can't output characters >= 0xFC as well which is strange. Ultimately this is a bizarre cp/m API issue.
I'll try the in code pragma!!!
The solution is to not mix the API access methods. In other words, don't use the non-RAW function for printing, but the RAW function for character input. The problem is that the non-RAW function for getting a key always prints it to the console and that may not be a good thing for some applications. So the only way to have a true getch() that doesn't echo to the screen is to use the RAW function. Once doing that, one should also use the RAW function for output as well. Doing that means that you are leaving the way CP/M itself handles XON/XOFF and also how it must convert \n to \r\n as well. Essentially what I did was switch to using the RAW character output so that I could properly use the RAW character input as well. I then added support for xon/xoff, cr translation, and optional program exit.
Ideally I would think if you could do the same with the library that would be cool, but I wonder about an incompatibility of programs using the raw output vs. the one you are using now. The raw output can't output characters >= 0xFC as well which is strange. Ultimately this is a bizarre cp/m API issue.
I'll try the in code pragma!!!
Re: CTRL-C processing
Using:
#pragma-redirect:fputc_cons=_putch
in the .c code instead of putting -pragma-redirect:fputc_cons=_putch in the zcc line does not work.
#pragma-redirect:fputc_cons=_putch
in the .c code instead of putting -pragma-redirect:fputc_cons=_putch in the zcc line does not work.
Re: CTRL-C processing
I suspect you're using a makefile? Or at the least compiling each .c file to .o and then linking as a separate stage?
If so you'll need to put the pragmas into a separate file, there's an explanation here: https://github.com/z88dk/z88dk/wiki/Classic--Pragmas
If so you'll need to put the pragmas into a separate file, there's an explanation here: https://github.com/z88dk/z88dk/wiki/Classic--Pragmas
Re: CTRL-C processing
I'm not using a make file, just a single zcc command:
Code: Select all
@echo off
if not "%Z80_OZFILES%"=="" goto skip
SET Z80_OZFILES=C:\z88dk\Lib\
SET ZCCCFG=C:\z88dk\Lib\Config\
SET PATH=%PATH%;C:\z88dk\Bin
:skip
if exist ndump.com del ndump.com
rem z88dk options
rem zcc +cpm -clib=8080 main command line for 8080
rem -O0 sometimes needed to fix optimization bugs
rem -lndos removes file functions to reduce code size
rem --opt-code-speed=inlineints a little more speed but less compact
rem -DAMALLOC enables use of malloc/free
rem --list <file.c> outputs a list file
rem Classic with sccz80 target 8080 processor
rem -clib=8080 -O3
rem zcc +cpm --opt-code-speed=inlineints -pragma-redirect:fputc_cons=_putch ndump.c -ondump1.com
zcc +cpm --opt-code-speed=inlineints ndump.c -ondump2.com
if exist ndump.com dir ndump.com
Code: Select all
#pragma-redirect:fputc_cons=_putch
#include <stdio.h>
#include <stdlib.h>
//console direct i/o
//cp/m specific
//#if defined(__SCCZ80) || defined(__SDCC)
//#endif
unsigned char keyread, enablectrlcexit=1;
unsigned char c_rawio_get() __smallc __naked
{
__asm
ld c,6 //bdos function 6
ld e,0xff //bdos parameter return character without echoing
call 5 //call bdos
ld l,a //copy result a into l for return
ret
__endasm;
}
void c_rawio_put(char b) __naked __z88dk_fastcall
{
__asm
ld c,6 //bdos function 6
ld e,l //bdos output from l
call 5 //call bdos
ret
__endasm;
}
unsigned char kbhit()
{
unsigned char c1;
//if we've previously read a key then return 1
if (keyread)
return 1;
//let's get a key
c1=c_rawio_get();
//if it is zero then return 0, no key is ready
if (c1==0)
return 0;
//if not we do have a key, cache it in keyread and return 1
keyread=c1;
return 1;
}
unsigned char getch()
{
unsigned char c1;
//if we have a cached key, return it
if (keyread)
{
c1=keyread;
keyread=0;
return c1;
}
//we must wait on a key and return it
do
{
c1=c_rawio_get();
} while (c1==0);
return c1;
}
int putch(int c)
{
if (c<0xfc)
{
//pause for ctrl-s
kbhit();
if (keyread==19)
{
//clear the ctrl-s
keyread=0;
//pause and wait for a key, if it is ctrl-c leave it in the buffer
if (getch()==3)
goto quit;
}
else
if (keyread==3)
{
quit:
if (enablectrlcexit)
{
c_rawio_put('^');
c_rawio_put('C');
exit(1);
}
}
if (c=='\n')
c_rawio_put('\r');
c_rawio_put(c);
return c;
}
else return EOF;
}
void main(int argc, char** argv)
{
FILE *infile;
unsigned int i1, i2;
unsigned long pos;
unsigned char c, buf[128];
/*options*/
if (argc!=2)
{
printf("Usage: NDUMP filename\n");
return;
}
/*open file*/
infile=fopen(argv[1], "rb");
if (infile==NULL)
{
printf("Unable to open file\n");
return;
}
pos=0;
while (!feof(infile))
{
if (kbhit())
{
c=getch();
if (c)
{
printf("\n\n<<%d>>\n\n", c);
}
}
if (fread(buf, 128, 1, infile)!=1)
continue;
//this works fine
/*printf(".");
for (i1=0; i1<10000; i1++)
;*/
//when enabled, this does not work fine
for (i1=0; i1<8; i1++)
{
printf("%06lX: ", pos+i1*16);
for (i2=0; i2<16; i2++)
{
printf("%02X ", buf[i1*16+i2]);
}
printf(" ");
for (i2=0; i2<16; i2++)
{
c=buf[i1*16+i2];
if (c>=32 && c<=128)
{
printf("%c", c);
}
else
{
printf(".");
}
}
printf("\n");
}
pos+=128;
}
fclose(infile);
}