List of Bugs

Discussions about testing new clib
Post Reply
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

List of Bugs

Post by alvin »

Bugfixes are applied as of the time of the posting and will be fixed in the next successful nightly build.


Bug #1 (fixed) sccz80

sccz80 places statically initialized variables in rodata section when they should be placed in data section.


Bug #2 (fixed) bss initialization

The crt saves the stack pointer into a bss variable before the bss section is initialized, meaning bss initialization wipes out the stored value.

A new section was introduced ("BSS_UNINITIALIZED") to allocate space for data that is never initialized.


Test program:

Code: Select all

// zcc +zx -vn -startup=1 -clib=new rand.c -o rand

#include <stdio.h>
#include <stdlib.h>
#include <arch/spectrum.h>
#include <sys/ioctl.h>

#pragma output CRT_INITIALIZE_BSS = 1

int distribution[256];
char *s, b;

int j, k;

unsigned long t;
unsigned long sum;
unsigned long n;

main()
{
   int i;
   
   zx_border(INK_WHITE);
   ioctl(1, IOCTL_OTERM_CLS);
   
   do
   {
      // scale RAND_MAX to [0,255]
      
      i = rand() >> 7;
      ++distribution[i];
      
      // plot(x,y)
      // no graphics lib available yet
      
      s = zx_pxy2saddr(i, 192 - distribution[i]);
      b = zx_px2bitmask(i);
      *s |= b;
   
   } while (distribution[i] < 192);
   
   // print distribution & accumulate statistics
   
   printf("\x16\x01\x17\n\nDistribution:\n\n");
   
   k = 127;
   for (i=0; i!=256; ++i)
   {
      // accumulate sum
      
      t = (j + 64) * (unsigned long)(distribution[i]);
      
      sum += t;
      n += distribution[i];
      
      // print frequency for 20% of intervals
      
      if ((i % 5) == 0)
         printf("[%5u,%5u] = %u\n", j, k, distribution[i]);
      
      // next interval boundary
      
      j += 128;
      k += 128;
   }
   
   // print statistics
   
   printf("\n%11s : %lu\n", "Sample Size", n);
   printf("%11s : %lu\n", "Sum", sum);
   printf("%11s : %lu\n", "Average", sum / n);

   printf("\n\n");
   return 0;   
}
This program tests the random number generator's distribution.

This is a RAM model program so no section initialization is normally performed, however the pragma forces the crt to do a bss initialization in the startup. This allows the program to be executed more than once as all data that needs to be zeroed is zeroed on each execution. The random number seed sits in a data section and its value is retained between runs, allowing a different sequence of random numbers to be generated on each run.

The bug caused the stored value of sp to be zeroed so that return after execution caused a crash. With the fix in place the program exits fine and can be re-run.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #3 (fixed) edit text pushed into input terminal must be echoed to output terminal when editing begins

Bug #4 (fixed) flushing input terminal should not reset key state in zx_01_inkey console

Resetting the key state means the key scanner goes back to the debounce state so that key presses are immediately accepted without delay. This has meant that two scanfs in a row, separated by an fflush, could see the key pressed at the end of the last scanf also immediately appear at the front of the second scanf. This was very frustrating for the user and problems occurred if that key was '\n'


Test Program:

Code: Select all

// zcc +zx -vn -clib=new -startup=1 ioctl_edit.c -o ioctl_edit

#include <stdio.h>
#include <arch/spectrum.h>
#include <sys/ioctl.h>
#include <string.h>
#include <adt.h>

b_array_t edit_buffer;

char username[21];
char password[20];

main()
{   
   zx_border(INK_WHITE);
   ioctl(1, IOCTL_OTERM_CLS);
   
   printf("USERNAME + PASSWORD QUERY\n");
   printf("username pushed into edit buffer\n");
   
   username[20] = 'X';   // test for buffer overrun
   
   // gather details about the input terminal's buffer
   
   ioctl(0, IOCTL_ITERM_GET_EDITBUF, & edit_buffer);
   
   while(1)
   {
      printf("\n=====\n\nUsername: ");

      // push last username into edit buffer

      fflush(stdin);
      
      edit_buffer.size = strlen(username);
      strcpy(edit_buffer.data, username);
      ioctl(0, IOCTL_ITERM_SET_EDITBUF, & edit_buffer);
      
      // read username
      
      scanf("%19[^\n]", username);
      
      // read password
      
      printf("Password: ");
      
      ioctl(0, IOCTL_ITERM_PASS, 1);
      
      fflush(stdin);
      scanf("%19[^\n]", password);
      
      ioctl(0, IOCTL_ITERM_PASS, 0);
      
      // echo
      
      printf("\n\"%s\"\n", username);
      printf("\"%s\"\n", password);
      
      if (username[20] != 'X')
         printf("\nBUFFER OVERRUN\n");
   }
}
This program pushes edit text into the input terminal before the username is read. The text pushed is the last username entered. It also tests password mode when entering the password.

NOTES:

Usually it's a better idea to getline() a line of text and then sscanf that as dealing with the stream directly can cause unexpected behaviours for the programmer. scanf does not remove text from the stream that is rejected by a scanf converter and this has consequences that most programmers find unexpected.

In the above listing, stdin is flushed prior to pushing the edit buffer into the input terminal. The reason is a character might be sitting in the ungetc slot of the stream and even though we control what characters go into the input buffer, the first character scanf will read in the ungetc char, not a buffer character, so the fflush is necessary to get rid of that.

One other thing that programmers may find unexpected is that when the input terminal is in line mode, it gathers an entire line of text into its own private buffer before passing results from that line one char at a time to scanf. scanf will only consume chars from that stored line as long as they are accepted by the converter. The stored line always terminates in '\n' and both scanfs above will leave that \n on the stream.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #5 (fixed) malloc heap improperly initialized

malloc's heap has a pointer "__malloc_heap" pointing to the malloc block labelled "__malloc_block".
Confusion in the labelling had the heap initialization code zeroing "__malloc_heap" so that it pointed into ROM rather than at the free block.
This caused erroneous memory allocations out of the rom area (and very likely crashes).


Test Program:

Code: Select all

// zcc +zx -vn -clib=new fememopen.c -o fmemopen

#include <stdio.h>
#include <string.h>

static char buffer[] = "foobar";

int main(void)
{
   int ch;
   FILE *stream;

   stream = fmemopen(buffer, strlen(buffer), "r");

   if (stream == NULL)
      perror("fmemopen");
   else
   {
      while ((ch = fgetc(stream)) != EOF)
         printf("Got %c\n", ch);
         
      fclose(stream);
   }

   return 0;
}
This program tests the C11 extension memory streams. A malloc occurs to allocate a memstream FILE* which led to a crash before the fix was applied.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #6 (fixed) printf() causing errno to be overwritten when there are no errors

printf() was generating an ENOTSUP error for terminals without tty emulation. This was because the intentionally uncaught OTERM_MSG_TTY message was causing the driver to indicate it did not understand the message. The fix has adds a simple message handler (jp error_zc) added in case the terminal does not implement tty emulation.


Test Program:

Code: Select all

// zcc +zx -vn -clib=new fememopen.c -o fmemopen

#include <stdio.h>
#include <string.h>

extern int errno;

static char buffer[] = "foobar";

int main(void)
{
   int ch;
   FILE *stream;

   stream = fmemopen(buffer, strlen(buffer), "r");
   
stream = 0;
printf("errno = %d\n", errno);

   if (stream == NULL)
      perror("fmemopen");
   else
   {
      while ((ch = fgetc(stream)) != EOF)
         printf("Got %c\n", ch);
         
      fclose(stream);
   }

   return 0;
}
This is a hack on the Bug5 program to see if perror() would work. Before the fix, the printf would print 0 for errno but perror would print ENOTSUP. The printf was changing errno but only on terminals without tty emulation (like startup=0 here, but if compiled with startup=-1 there is no error). After the fix the program reports EOK as expected.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #7 (fixed) ioctl IOCTL_OTERM_FCOLOR not changing foreground colour

There was a "ret z" in there instead of a "ret nz" when testing if the colour was < 255

Test Program:

Code: Select all

// zcc +zx -vn -clib=new beepfx.c -o beepfx

#include <stdio.h>
#include <sound.h>
#include <arch/spectrum.h>
#include <sys/ioctl.h>
#include <z80.h>

// list of all beepfx sound effects

typedef struct effects_s
{

   void *effect;
   char *name;
   
} effects_t;

effects_t beepfx[] = {

    {BEEPFX_SHOT_1,            "BEEPFX_SHOT_1"},
    {BEEPFX_SHOT_2,            "BEEPFX_SHOT_2"},
    {BEEPFX_JUMP_1,            "BEEPFX_JUMP_1"},
    {BEEPFX_JUMP_2,            "BEEPFX_JUMP_2"},
    {BEEPFX_PICK,              "BEEPFX_PICK"},
    {BEEPFX_DROP_1,            "BEEPFX_DROP_1"},
    {BEEPFX_DROP_2,            "BEEPFX_DROP_2"},
    {BEEPFX_GRAB_1,            "BEEPFX_GRAB_1"},
    {BEEPFX_GRAB_2,            "BEEPFX_GRAB_2"},
    {BEEPFX_FAT_BEEP_1,        "BEEPFX_FAT_BEEP_1"},
    {BEEPFX_FAT_BEEP_2,        "BEEPFX_FAT_BEEP_2"},
    {BEEPFX_FAT_BEEP_3,        "BEEPFX_FAT_BEEP_3"},
    {BEEPFX_HARSH_BEEP_1,      "BEEPFX_HARSH_BEEP_1"},
    {BEEPFX_HARSH_BEEP_2,      "BEEPFX_HARSH_BEEP_2"},
    {BEEPFX_HARSH_BEEP_3,      "BEEPFX_HARSH_BEEP_3"},
    {BEEPFX_HIT_1,             "BEEPFX_HIT_1"},
    {BEEPFX_HIT_2,             "BEEPFX_HIT_2"},
    {BEEPFX_HIT_3,             "BEEPFX_HIT_3"},
    {BEEPFX_HIT_4,             "BEEPFX_HIT_4"},
    {BEEPFX_JET_BURST,         "BEEPFX_JET_BURST"},
    {BEEPFX_BOOM_1,            "BEEPFX_BOOM_1"},
    {BEEPFX_BOOM_2,            "BEEPFX_BOOM_2"},
    {BEEPFX_BOOM_3,            "BEEPFX_BOOM_3"},
    {BEEPFX_BOOM_4,            "BEEPFX_BOOM_4"},
    {BEEPFX_BOOM_5,            "BEEPFX_BOOM_5"},
    {BEEPFX_BOOM_6,            "BEEPFX_BOOM_6"},
    {BEEPFX_BOOM_7,            "BEEPFX_BOOM_7"},
    {BEEPFX_BOOM_8,            "BEEPFX_BOOM_8"},
    {BEEPFX_ITEM_1,            "BEEPFX_ITEM_1"},
    {BEEPFX_ITEM_2,            "BEEPFX_ITEM_2"},
    {BEEPFX_ITEM_3,            "BEEPFX_ITEM_3"},
    {BEEPFX_ITEM_4,            "BEEPFX_ITEM_4"},
    {BEEPFX_ITEM_5,            "BEEPFX_ITEM_5"},
    {BEEPFX_ITEM_6,            "BEEPFX_ITEM_6"},
    {BEEPFX_SWITCH_1,          "BEEPFX_SWITCH_1"},
    {BEEPFX_SWITCH_2,          "BEEPFX_SWITCH_2"},
    {BEEPFX_POWER_OFF,         "BEEPFX_POWER_OFF"},
    {BEEPFX_SCORE,             "BEEPFX_SCORE"},
    {BEEPFX_CLANG,             "BEEPFX_CLANG"},
    {BEEPFX_WATER_TAP,         "BEEPFX_WATER_TAP"},
    {BEEPFX_SELECT_1,          "BEEPFX_SELECT_1"},
    {BEEPFX_SELECT_2,          "BEEPFX_SELECT_2"},
    {BEEPFX_SELECT_3,          "BEEPFX_SELECT_3"},
    {BEEPFX_SELECT_4,          "BEEPFX_SELECT_4"},
    {BEEPFX_SELECT_5,          "BEEPFX_SELECT_5"},
    {BEEPFX_SELECT_6,          "BEEPFX_SELECT_6"},
    {BEEPFX_SELECT_7,          "BEEPFX_SELECT_7"},
    {BEEPFX_ALARM_1,           "BEEPFX_ALARM_1"},
    {BEEPFX_ALARM_2,           "BEEPFX_ALARM_2"},
    {BEEPFX_ALARM_3,           "BEEPFX_ALARM_3"},
    {BEEPFX_EAT,               "BEEPFX_EAT"},
    {BEEPFX_GULP,              "BEEPFX_GULP"},
    {BEEPFX_ROBOBLIP,          "BEEPFX_ROBOBLIP"},
    {BEEPFX_NOPE,              "BEEPFX_NOPE"},
    {BEEPFX_UH_HUH,            "BEEPFX_UH_HUH"},
    {BEEPFX_OLD_COMPUTER,      "BEEPFX_OLD_COMPUTER"},
    {BEEPFX_YEAH,              "BEEPFX_YEAH"},
    {BEEPFX_AWW,               "BEEPFX_AWW"}

};

main()
{
   static unsigned int i;
   static unsigned int j;
   static unsigned int offset;
   
   static char *buffer;   // static vars are initialized to zero at least first run
   static int len;        // static vars are initialized to zero at least first run
   
   zx_border(INK_BLUE);
   zx_cls(INK_WHITE | PAPER_BLUE);
   
   ioctl(1, IOCTL_OTERM_FCOLOR, INK_WHITE | PAPER_BLUE);
   ioctl(1, IOCTL_OTERM_BCOLOR, INK_WHITE | PAPER_BLUE);
   ioctl(1, IOCTL_OTERM_PAUSE, 0);
   
   printf("LIST OF BEEPFX EFFECTS:\n\n");
   
   for (i = 0; i < sizeof(beepfx) / sizeof(effects_t); ++i)
   {
      printf("%2u: %s\n", i, beepfx[i].name);
      
      bit_beepfx_di(beepfx[i].effect);
      z80_delay_ms(100);
   }
   
   printf("\nENTER A SPACE SEPARATED LIST OF");
   printf("\nEFFECTS TO PLAY BY NUMBER");
   
   while(1)
   {
      printf("\n\nEffect: ");
      getline(&buffer, &len, stdin);
      
      for (offset = 0; sscanf(buffer + offset, " %u%n", &i, &j) == 1; offset += j)
      {
         if (i < sizeof(beepfx) / sizeof(effects_t))
         {
            printf("\n  %2u: %s", i, beepfx[i].name);
            bit_beepfx_di(beepfx[i].effect);
         }
         else
         {
            printf("\n  %2u: INVALID", i);
         }
      }
   }
}
The ioctls at the beginning of the program set foreground colour, background colour (scroll / cls colour) and turn off scroll pause. Setting foreground colour was not working before the fix.

Notes:

This is the first use of getline() which in turn makes use of realloc() to grow the line buffer. Worked first time :)
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #8 (fixed) qsort() crashes using default shellsort algorithm

shellsort was written using the C interface to call memswap and at some point the call to memswap was changed to the asm interface without changes to how parameters were delivered.


Test Program:

Code: Select all

// zcc +zx -vn -clib=new -startup=4 qsort_test.c -o qsort_test

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arch/spectrum.h>

#pragma output CRT_INITIALIZE_BSS = 1

unsigned char *strings[20];

int string_compare(unsigned char **a, unsigned char **b)
{
   return stricmp(*a, *b);
}

main()
{
   static unsigned int i;
   static unsigned int j;
   static unsigned int sz;
   
   zx_border(INK_WHITE);
   zx_cls(INK_BLACK | PAPER_WHITE);
   
   // read strings from stdin
   
   printf("ENTER UP TO %u LINES OF TEXT.\n", sizeof(strings) / sizeof(unsigned char *));
   printf("END EARLY WITH AN EMPTY LINE.\n\n");
   
   for (i = 0; i != sizeof(strings) / sizeof(unsigned char *); ++i)
   {
      printf("%2u: ", i+1);
      
      sz = 0;
      if ((getline(&strings[i], &sz, stdin) == -1) || (sz <= 2))
      {
         if (sz > 0) free(strings[i]);
         break;
      }
      
      strrstrip(strings[i]);
   }
   
   // show the strings
   
   printf("\n\nTHE STRINGS YOU ENTERED:\n\n");
   
   for (j = 0; j != i; ++j)
      printf("\"%.60s\"\n", strings[j]);
   
   // sort them
   
   printf("\nQSORT BEGINS\n\n");
   qsort(strings, i, sizeof(unsigned char *), string_compare);

   for (j = 0; j != i; ++j)
      printf("\"%.60s\"\n", strings[j]);

   printf("\n\n\n");
   return 0;
}
This is a straight test of qsort. The program exits to basic and the pragma ensures that a repeated run zeroes critical variables so that the program can run more than once.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #9 (fixed) read() on keyboard input must return at \n

Header files have been completed (first pass anyway) and with that the low level fd functions are available.

read() from the keyboard attached to stdin has a few subtelties. One is read() should return early if a \n is encountered on the stream. A method also has to be present to generate EOF at the keyboard; on unix-related machines this ascii code is CTRL-D.

Before the fix was applied, read() applied to key input did not stop early on \n and the keyboard driver had no means to interpret an EOF generated at the keyboard. Both issues are fixed, with the keyboard driver now remembering an EOF event.

Test Program:

Code: Select all

// zcc +zx -vn -clib=new fd_read.c -o fd_read
// copy /b fd_read_CODE.bin+fd_read_DATA.bin fd_read_image.bin

#include <unistd.h>
#include <stropts.h>
#include <string.h>

// The rom model will initialize the data & bss sections at startup.

// This is necessary to allow this program to be run more than once
// since without it, the EOF condition on stdin will persist across runs.

#pragma output CRT_MODEL    = 1        // uncompressed ROM model
#pragma output CRT_ORG_DATA = 40000    // address of data & bss sections

char *msg_0 = "READ() CONNECTED TO KBD\n"
              "reads 5-char chunks\n\n"
              "ctrl-d is EOF (c+s+d)\n\n\n";

char buffer[10];

main()
{
   int n;
   
   ioctl(1, IOCTL_OTERM_CLS);
   write(STDOUT_FILENO, msg_0, strlen(msg_0));
   
   while ((n = read(STDIN_FILENO, buffer, 5)) != -1)
   {
      write(STDOUT_FILENO, "Got: \"", 6);
      write(STDOUT_FILENO, buffer, n);
      write(STDOUT_FILENO, "\"\n", 2);
   }
}
As mentioned in the comments, the rom model is selected in pragmas so that the crt will initialize both the data and bss segments at startup. This allows the program to be run more than once after exit. Without the data segment initialization, the keyboard driver will retain the EOF status seen to end the loop and no key input would be possible on subsequent runs.

With the rom model selected, zcc will output three binaries, one each for the CODE, DATA and BSS sections. To form an executable image, the DATA section must be appended to the CODE section as is done by the windows shell 'copy' in the comments. It is this image that must be loaded into an emulator and run by executing "RAND USR 32768".
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #10 (fixed) fzx terminals in edit mode: x coordinate of last char in edit buffer was incorrectly calculated when backspacing

Before the fix was applied, backspacing caused garbled text on screen.

The test program is another repeat, ioctl_edit, which shows text input from a non-zero initial x coordinate in plain text mode and in password mode. This time the program is compiled with "-startup=8" on the compile line which attached an fzx terminal to stdout.

Code: Select all

// zcc +zx -vn -clib=new ioctl_edit.c -o ioctl_edit
// zcc +zx -vn -startup=8 -clib=new ioctl_edit.c -o ioctl_edit

#include <stdio.h>
#include <arch/spectrum.h>
#include <sys/ioctl.h>
#include <string.h>
#include <adt.h>

b_array_t edit_buffer;

char username[21];
char password[20];

main()
{   
   zx_border(INK_WHITE);
   ioctl(1, IOCTL_OTERM_CLS);
   
   printf("USERNAME + PASSWORD QUERY\n");
   printf("username pushed into edit buffer\n");
   
   username[20] = 'X';   // test for buffer overrun
   
   // gather details about the input terminal's buffer
   
   ioctl(0, IOCTL_ITERM_GET_EDITBUF, & edit_buffer);
   
   while(1)
   {
      printf("\n=====\n\nUsername: ");

      // push last username into edit buffer

      fflush(stdin);
      
      edit_buffer.size = strlen(username);
      strcpy(edit_buffer.data, username);
      ioctl(0, IOCTL_ITERM_SET_EDITBUF, & edit_buffer);
      
      // read username
      
      scanf("%19[^\n]", username);
      
      // read password
      
      printf("Password: ");
      
      ioctl(0, IOCTL_ITERM_PASS, 1);
      
      fflush(stdin);
      scanf("%19[^\n]", password);
      
      ioctl(0, IOCTL_ITERM_PASS, 0);
      
      // echo
      
      printf("\n\"%s\"\n", username);
      printf("\"%s\"\n", password);
      
      if (username[20] != 'X')
         printf("\nBUFFER OVERRUN\n");
   }
}
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Bug #11 (fixed) fzx character printing was colouring excessively large area around chars in the zx target

Before the fix was applied, colour applied to output fzx chars could be applied to several character squares below the character printed. This was especially evident with fzx output terminals which define a text window on screen; this bug caused colouring to occur outside the text window boundaries.

This test program uses a custom crt selected with "-startup=200" on the compile line. The custom crt defines two fzx terminal windows that are printed to by the test program. No more colour bleeding outside the text windows occurs while printing.

"fzx_term2.c"

Code: Select all

// sccz80
// zcc +zx -vn -startup=200 -clib=new fzx_term2.c -o fzx_term2
//
// sdcc
// zcc +zx -vn -startup=200 -clib=sdcc_ix --reserve-regs-iy --max-allocs-per-node200000 fzx_term2.c -o fzx_term2

#include <stdio.h>
#include <stropts.h>
#include <arch/spectrum.h>
#include <stdlib.h>
#include <string.h>

extern FILE *window_1;
extern FILE *window_2;

main()
{
   static int i;
   static char buffer[100];

   zx_border(INK_BLACK);
   zx_cls(INK_GREEN | PAPER_GREEN);

   ioctl(1, IOCTL_OTERM_CLS);
   ioctl(2, IOCTL_OTERM_CLS);

   while (1)
   {
      // fill buffer with random letters
      
      memset(buffer, 0, 100);
      
      for (i = rand() % 100; i >= 0; --i)
         buffer[i] = (char)((rand() % ('z' - ' ')) + ' ');
      
      // print to both windows
      
      fprintf(window_1, "%s\n", buffer);
      fprintf(window_2, "%s\n", buffer);
      
      // gather input
      
      if (rand() > 32200)
      {
         fprintf(window_1, "\nEnter some text: ");
         
         fflush(stdin);
         scanf("%99[^\n]", buffer);
         
         i = ioctl(2, IOCTL_OTERM_FCOLOR, INK_YELLOW | PAPER_BLUE);
         fprintf(window_2, "\n%s\n\n", buffer);
         ioctl(2, IOCTL_OTERM_FCOLOR, i);
      }
   }
}
Post Reply