Saturday, May 30, 2015

stdio functions via the CPUville monitor

This post covers replacing the standard crt0.rel file and making stdio work with the CPUville monitor.

To simplify compiling programs the first thing to do is replace the standard crt0.rel with the one described in the previous post.

This is as simple as backing up the standard file found in $SDCC_HOME/lib/z80 and replacing it with the crt0.rel assembled from the modified source.

Now the compiler does not need the --no-std-crt0 flag and single source file programs do not require a separate link step so crt0.rel can be placed first in the list of files to be linked.

A simple program can be compiled with a command such as "sdcc -mz80 --code-loc 0x0809 --data-loc 0 test.c". The --code-loc and --data-loc flags are still required so the linker adjusts the relocatable area correctly.

Adding CPUville appropriate getchar and putchar functions into the standard library is also pretty simple. The file to change is $SDCC_HOME/lib/src/z80/putchar.s. Replace all the code in that file with:


; CPUville ROM entry points.
write_char      =    0x0109
get_char        =    0x037c

    .area _CODE
_putchar::
    ld      hl, #2
    add     hl, sp
    ld      a, (hl)
    call    write_char
    ret
_getchar::
    call    get_char
    ld      l,a
    ret


This calls the appropriate CPUville monitor functions to write and read single characters via the serial port.

Compile this with "sdcc -mz80 -c putchar.c". The Z80 version of the standard library is found in $SDCC_HOME/lib/z80/z80.lib. Back up this file and use the sdar command to replace the version of putchar.rel in z80.lib with the newly compiled one: "sdar rv /d/sdcc/lib/z80/z80.lib putchar.rel".

At this point your code can use puts and gets from stdio.h. I'm still using the CPUville 2K RAM so I cannot call printf yet - the binary file was more than 3K! However SDCC ships with code for smaller/faster variations of printf and I with some editing of the source code I was able to get printf_small to compile. The modifications were mostly removing data storage keywords which the Z80 does not support. I also removed the conditional compilation code just to make things easier to see. Here is the modified code, in $SDCC_HOME/lib/src/printfl.c:


/*-----------------------------------------------------------------
    printfl.c - source file for reduced version of printf

   Copyright (C) 1999, Sandeep Dutta . sandeep.dutta@usa.net
   2001060401: Improved by was@icb.snz.chel.su

   This library is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License 
   along with this library; see the file COPYING. If not, write to the
   Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA.

   As a special exception, if you link this library with other files,
   some of which are compiled with SDCC, to produce an executable,
   this library does not by itself cause the resulting executable to
   be covered by the GNU General Public License. This exception does
   not however invalidate any other reasons why the executable file
   might be covered by the GNU General Public License.
-------------------------------------------------------------------------*/
/* following formats are supported :-
   format     output type       argument-type
     %d        decimal             int
     %ld       decimal             long
     %hd       decimal             char
     %x        hexadecimal         int
     %lx       hexadecimal         long
     %hx       hexadecimal         char
     %o        octal               int
     %lo       octal               long
     %ho       octal               char
     %c        character           char
     %s        character           generic pointer*/
#include <stdarg.h>#include <stdio.h>#include <stdlib.h>
static char radix ;static long_flag = 0;static string_flag =0;static char_flag = 0;static char * str ;static long val;
void printf_small (char * fmt, ... ){
    va_list ap ;

    va_start(ap,fmt);

    for (; *fmt ; fmt++ ) {
        if (*fmt == '%') {
            long_flag = string_flag = char_flag = 0;
            fmt++ ;
            switch (*fmt) {
            case 'l':
                long_flag = 1;
                fmt++;
                break;
            case 'h':
                char_flag = 1;
                fmt++;
            }

            switch (*fmt) {
            case 's':
                string_flag = 1;
                break;
            case 'd':
                radix = 10;
                break;
            case 'x':
                radix = 16;
                break;
            case 'c':
                radix = 0;
                break;
            case 'o':
                radix = 8;
                break;
            }

            if (string_flag) {
                str = va_arg(ap, char *);
                while (*str) putchar(*str++);
                continue ;
            }

            if (long_flag)
                val = va_arg(ap,long);
            else
                if (char_flag)
                    val = va_arg(ap,char);
                else
                    val = va_arg(ap,int);

            if (radix)
            {
              static char buffer[12]; /* 37777777777(oct) */
              char * stri;

              _ltoa (val, buffer, radix);
              stri = buffer;
              while (*stri)
                {
                  putchar (*stri);
                  stri++;
                }
            }
            else
              putchar((char)val);

        } else
            putchar(*fmt);
    }
}


Using the same commands as for putchar.c this code can be compiled and added into z80.lib: "sdcc -mz80 -c printfl.c", "sdar rv /d/sdcc/lib/z80/z80.lib printfl.rel".

printf_small is already declared in stdio.h, and is now ready to use:


#include <stdio.h>
static char clear_screen[] = { 0x1b, '[', '2', 'J', 0x00 };
static char cursor_home[] = { 0x1b, '[', 'H', 0x00 };
static char buffer[33];
void main(void) {
    printf_small("%s%s%s", clear_screen, cursor_home, "Hello world.\r\nEnter a string up to 32 characters long: ");
    gets(buffer);
    printf_small("\rYou entered: %s\r\n", buffer);
}


This code compiles to 1467 bytes, so it's still pushing the limits of my 2K system, but it does fit!



No comments: