Showing posts with label gcc. Show all posts
Showing posts with label gcc. Show all posts

Thursday, June 8, 2017

Building a 68000 Single Board Computer - The Newlib C Library



To protect it, I made a Lexan cover for the TS2 board, similar to what I did for the prototype. I mounted it using nylon standoffs and mounted the switches on the cover. I don't yet have the correct toggle switches, so the units here are temporary.




In order to run C programs of any complexity, you need a C run-time library to provide functions for things like input/output (e.g. printf) and string manipulation (e.g. strcmp). Since the TS2 is not running an operating system like Linux, this is not available.

A full C library is too large to run on a board like this with only 32K of RAM. Fortunately, there are some smaller alternative C run-time libraries available, mostly designed for embedded systems.

I spent some time looking at one of the popular ones, newlib. Designed for embedded systems, it supports the Motorola 68000 processor (usually referred to as m68k).

It is quite complex and documentation is sparse. I found and read a number of references. I found the easiest way to port it to the TS2 board was to start with the newlib code for another 68000-based single-board computer, copy it, and adapt it (As someone once said: "adopt, adapt, and improve").

Porting newlib involves creating at least some minimal functions for low-level input output and returning from the main() functions. These are in a part of newlib known as libgloss.

I won't cover all the details, but these were the files I needed to modify or create, all located in the libgloss/m68k/ direcytory of newlib:

Makefile.in - This is the make file which needed to be modified to add the new TS2 target platform.

crt0.S - This is the C run-time startup file. This only needed one minor change to work around a problem I encountered with atexit handling.

tutor.S - This file implements basic routines for character and string input and output and returning after main(). They are implemented in assembler and call the TUTOR ROMs routines via trap 14 with the exception of one routine which returns if a character is ready for input. It needed to talk directly to the UART hardware as TUTOR did not implement such a function. After executing main, controls returns to the TUTOR monitor.

tutor.h - This file defines some constants use by tutor.S, specifically the names of the TUTOR trap 14 functions.

ts2.ld - This is a linker script which defines the memory map to use when building for the TS2 board. You refer to this when linking your program with newlib.

The relevant files can all be found here.

The newlib directory contains a script which downloads the gcc and newlib source code, and then configures and builds gcc and newlib. It patches newlib with the files needed to support the TS2.

Also included is a sample program and make file which builds using the cross-compiler and newlib and runs on the TS2. It is a small C program I wrote some years ago to solve the "n queens" chess problem. It is very CPU and stack intensive program that can take a long time to run depending on the speed of the system and the size of the chessboard for the problem.

I ran it for a 5x5 board. The output is below:

TUTOR  1.3 > GO 804
PHYSICAL ADDRESS=00000804
Solving n queens problem for n = 5
+---------------+
| Q  .  .  .  . |
| .  .  Q  .  . |
| .  .  .  .  Q |
| .  Q  .  .  . |
| .  .  .  Q  . |
+---------------+
+---------------+
| Q  .  .  .  . |
| .  .  .  Q  . |
| .  Q  .  .  . |
| .  .  .  .  Q |
| .  .  Q  .  . |
+---------------+
+---------------+
| .  Q  .  .  . |
| .  .  .  Q  . |
| Q  .  .  .  . |
| .  .  Q  .  . |
| .  .  .  .  Q |
+---------------+
+---------------+
| .  Q  .  .  . |
| .  .  .  .  Q |
| .  .  Q  .  . |
| Q  .  .  .  . |
| .  .  .  Q  . |
+---------------+
+---------------+
| .  .  Q  .  . |
| Q  .  .  .  . |
| .  .  .  Q  . |
| .  Q  .  .  . |
| .  .  .  .  Q |
+---------------+
+---------------+
| .  .  Q  .  . |
| .  .  .  .  Q |
| .  Q  .  .  . |
| .  .  .  Q  . |
| Q  .  .  .  . |
+---------------+
+---------------+
| .  .  .  Q  . |
| Q  .  .  .  . |
| .  .  Q  .  . |
| .  .  .  .  Q |
| .  Q  .  .  . |
+---------------+
+---------------+
| .  .  .  Q  . |
| .  Q  .  .  . |
| .  .  .  .  Q |
| .  .  Q  .  . |
| Q  .  .  .  . |
+---------------+
+---------------+
| .  .  .  .  Q |
| .  Q  .  .  . |
| .  .  .  Q  . |
| Q  .  .  .  . |
| .  .  Q  .  . |
+---------------+
+---------------+
| .  .  .  .  Q |
| .  .  Q  .  . |
| Q  .  .  .  . |
| .  .  .  Q  . |
| .  Q  .  .  . |
+---------------+
Found 10 solutions after 53130 tries.
TUTOR  1.3 > 

This took about 2.5 minutes to find all the solutions. On a modern 64-bit computer running at a 2 GHz or so clock speed, the same program runs in negligeable time.

With newlib, I can now write C programs that use most of the common ANSI C library functions other than file i/o, threads, or other things lacking without a "real" operating system. The program also has to fit within the 32K RAM limit.

Thursday, May 25, 2017

Building a 68000 Single Board Computer - C Compiling Example

I earlier did some experimenting with using gcc to cross-compile C code for the embedded TS2 board. I recently spent a few hours making a longer example, and hooking it up to some of the TUTOR ROM routines.

I earlier built the GNU assembler, and while I was at it I also built the gcc compiler.

In this "bare metal" setup, there is no C run-time library so you can't call routines like printf() to do output. You can't even write code that might require external routines to do things that the 68000 can't do directly, like multiplying 32-bit integers.

My example code can be found here and includes a make file.

The basic steps are to first compile for the 68000 and no standard library. I used this command line:

m68k-elf-gcc -Wall -nostdlib -nodefaultlibs -m68000 -c demo.c

This produces an object file, demo.o. Next, you can link it. I used linker options to specify the addresses for the different sections that would work on my TS2 board. I also found I had to define the symbol _start to point to main to avoid a warning. This was the command line I used:

m68k-elf-ld --defsym=_start=main -Ttext=0x2000 -Tdata=0x3000 -Tbss=0x4000 --section-start=.rodata=0x5000 demo.o

This produces an ELF format excecutable called a.out. You can then generate an S record or Motorola hex file for downloading using a command like this:

m68k-elf-objcopy -I coff-m68k -O srec a.out demo.run

If you wanted to see the assembler output of the C compiler, we could have compiled with the -S option, as below, to produce a file demo.s:

m68k-elf-gcc -Wall -nostdlib -nodefaultlibs -m68000 -S demo.c

Given the S record file, we can directly download it to the TS2 using the TUTOR monitor's LO command, and then execute it.

To be able to produce output, I wrote some simple code to call TUTOR's firmware routines, which are accessed by a TRAP #14 instruction.

For example, to pass control to the TUTOR monitor you call function 228, by passing this value in register D7 and calling TRAP #14. Here is the code to do it in C using some in-line assembler code:

// Go to the TUTOR monitor using trap 14 function. Does not return.
void tutor() {
    asm("move.b #228,%d7\n\t"
        "trap #14");
}

To do output to the console I wrote a short routine that takes a character and calls TUTOR's OUTCH call which writes a character to the console. The code to do it is as follows:

// Print a character using the TUTOR monitor trap function.
void outch(char c) {
    asm("movem.l %d0/%d1/%a0,-(%sp)\n\t"  // Save modified registers
        "move.b #248,%d7\n\t"             // OUTCH trap function code
        "trap #14\n\t"                    // Call TUTOR function
        "movem.l (%sp)+,%d0/%d1/%a0");    // Restore registers
}

The code could be improved and made more robust, for example not
relying on the fact that gcc puts the passed parameter in register D0. But this worked well for a quick demo. I then wrote a routine in C to print a string by calling outch() for each character in the string:

// Print a string.
void printString(const char *s) {
    while (*s != 0) {
        outch(*s);
        s++;
    }
}

I wrote another routine in C to print a number in decimal. Both this and the previous routine could be implemented more efficiently by calling routines in TUTOR that can already do this.

The example program prints numbers from 1 to 7 along with their squares, value to the fourth power, and factorial. I stopped at 7 because I needed to use 16-bit short integers to avoid the need for run-time math routines and 7 factorial was the largest value that would fit in 16-bits.

Here is the output when run from the monitor:

TUTOR  1.3 > GO 2000
PHYSICAL ADDRESS=00002000
Start
n  n^2  n^4  n!
1 1 1 1
2 4 8 2
3 9 27 6
4 16 64 24
5 25 125 120
6 36 216 720
7 49 343 5040
Done

At some point in the future I might implement more routines, and then try building some larger applications like an interactive text adventure game that I wrote some time ago.

An update: By linking to libgcc, the compiled code can do 32-bit integer math and support the C "int" type. It can even do floating point math using the soft float support (although that make the code quickly become quite large). I've updated the code and make file accordingly. There is still no C run-time library for routines like printf(), but I am looking at some options for this that I will describe in a future blog post.

Friday, November 25, 2016

Building a 68000 Single Board Computer - Software

While waiting for the remaining parts, I spent some time getting the software for the board ready.

The Teeside TS2 used a monitor program which is listed in source code form in the book and on the included CD-ROM. The CD-ROM also includes a number of tools including a 68000 cross-assembler and simulator.

The programs are from the late 1980s and were written for MS-DOS (predating Windows). I was able to run the cross-assembler and the simulator under Windows 10 (it needs to be run as Administrator). I was able to run the small version of the monitor called minimon under the simulator. I was also able to run the programs under the Linux program dosbox (an MS-DOS emulator).


I normally use Linux and wanted a native cross-compiler for the 68000 that I could run there. The GNU assembler supports the Motorola 68K family. It is quite easy to build a 68000 cross-assembler under Linux. I followed some instructions I found here. These were the basic steps I followed:

  1. Get GNU binutils, which includes the assembler, by downloading it from http://ftp.gnu.org/gnu/binutils/
  2. Untar the source archive somewhere.
  3. Configure it using ./configure --target=m68k-elf
  4. Build it using make -j4
  5. Install it using sudo make install

The assembler binary is called m68-elf-as.

You can also build the GNU C compiler if you want to do high-level language programming. Quite a bit of code should fit in the RAM and ROM available. The basic steps I followed to do this are (based on http://daveho.github.io/2012/10/26/m68k-elf-cross-compiler.html):

  1. Get the code ftp://ftp.gnu.org/pub/gnu/gcc/gcc-5.4.0/gcc-5.4.0.tar.gz
  2. Untar it.
  3. You will need some development tool packages which vary depending on your Linux distribution.
  4. Configure, build, and install it using:

mkdir objdir
cd objdir
../gcc-5.4.0/configure --target=m68k-elf --enable-languages=c --disable-libssp
make -j4
sudo make install

The C compiler binary is m68k-elf-gcc. Many other tools also get built.

I was able to port the TS2 monitor to assemble under the GNU assembler and was able to generate binaries that match the original from the Teesside assembler. The port can be found here on github.

I also wrote some simple code that plan to use when I start to bring up the board - a simple ROM that will do only a little more than freerun mode, like a simple loop or writing to the serial port. This can be found here, but is still a work in progress and untested.

Future projects, if and when the board is up and running, could include trying to get the TUTOR software from Motorola's ECB board running (the TS2 should be compatible with it). It is a little more advanced than the TS2 monitor and includes an assembler than runs on the board, for example.

There are some free implementations of BASIC for the 68000 that could possibly be ported including http://members.shaw.ca/gbrandly/68ktinyb.html and http://www.easy68k.com/applications.htm