CTRL-C processing

alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

CTRL-C processing

Post by alank2 »

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.
nuc1e0n
Member
Posts: 49
Joined: Wed Jun 10, 2020 12:34 am

Re: CTRL-C processing

Post by nuc1e0n »

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.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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!
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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?
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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:

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);
}
nuc1e0n
Member
Posts: 49
Joined: Wed Jun 10, 2020 12:34 am

Re: CTRL-C processing

Post by nuc1e0n »

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.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

I wonder if there is a bios issue going on with this.

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);
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!
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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!
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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.

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

//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

alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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.

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

alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

It is an issue with the cp/m bios. If I boot it under z-system, it works fine.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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?
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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:

Code: Select all

int fputc_cons(char c)
{
  return 0;
}
But printf still prints.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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:

Code: Select all

int myputc(int c) { }

-pragma-redirect:fputc_cons=myputc

alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

I think it is a CP/M 2.2 BDOS bug.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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

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;
}
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

It could also easily be modified to look exit(1) instead of an outer main program loop having to check for it.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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;
}
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

Also, can this option:

-pragma-redirect:fputc_cons=_putch

be put in code instead of on the command line?
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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:

Code: Select all

#pragma redirect fputc_cons=_putch
In code or zpragma.inc
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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!!!
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: CTRL-C processing

Post by dom »

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
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Re: CTRL-C processing

Post by alank2 »

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