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.

Monday, May 29, 2017

Building a 68000 Single Board Computer - PCBs Arrived!


After almost three months of waiting, my printed circuit boards arrived! I had just about given up, and was in the process of filing a PayPal dispute when they finally showed up.



These were manufactured by EasyEDA.com. They were very low-cost and the boards are high quality double-sided, plated through, solder masked and silk screened. I found it very easy to upload the design files from kicad to EasyEDA.com.

I had to order a minimum of five (I actually received six) but the cost was lower than most other vendors for one board.


This was the first PCB I have ever layed out. I expected a few errors, or possibly something that would make the boards unusable. It turned out I did have a few issues. Somehow a few power connections were omitted in the layout. I'm not yet sure if this was my fault or a bug in Kicad or the router software. I did make one glaring error that I had overlooked on the schematics - the UDS* and LDS* signals to the 68000 were reversed. When I wirewrapped my prototype I had connected them properly.

As compared to the wirewrap version that took the better part of a week (part time) to wire up, assembling the PCB took only a couple of hours, and much of that was making some sockets out of smaller sockets and carefully checking that the right ICs got installed.


I had to add a few "bodge" wires to correct the missing and incorrect wiring. After that, the board is working 100%. It looks very nice. I layed it out so that the switches and LEDs could be mounted externally from the board in a case. I may install it in a case once I find a suitable enclosure Or I may again just mount a piece of plexiglass over the top.


At the moment I have no plans to make more boards, and I only have enough parts for one (I had to cannibalize the parts from the wirewrap prototype).

I will shortly update the design to address the layout issues and make a set of files for a rev 2.1.1.


All in all I am quite happy with my first PCB layout. When I started with kicad I didn't think I would bother beyond wirewrapping a board. It is very gratifying to see a professionally looking board that has my name on it.

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.

Saturday, May 20, 2017

Building a 68000 Single Board Computer - Programming Examples


I earlier mentioned the book 68000 Assembly Language Programming, Second Edition, by Lance A. Leventhal, Doug Hawkins, Gerry Kane, and William D. Cramer. The book has many complete programming examples listed in it that help explain 68000 programming.

The book recommends entering and running the programs ona 68000-based system. I've been doing that, and it makes the code much clearer than simply reading the text. With the 68000 TUTOR software it is very easy to disassemble code in memory, display amd enter memory values, and run the program examples. I typically step through the code an instruction at a time using the trace function, looking at the values of the registers and selected memory locations.

Here is a typical disassembly of some example code:

TUTOR  1.3 > MD 4000 24 ;DI
004000    307C6001             MOVE.W  #24577,A0 
004004    7003                 MOVEQ.L #3,D0 
004006    4281                 CLR.L   D1 
004008    4282                 CLR.L   D2 
00400A    6008                 BRA.S   $004014 
00400C    D241                 ADD.W   D1,D1 
00400E    3601                 MOVE.W  D1,D3 
004010    E54B                 LSL.W   #2,D3 
004012    D243                 ADD.W   D3,D1 
004014    1418                 MOVE.B  (A0)+,D2 
004016    D242                 ADD.W   D2,D1 
004018    51C8FFF2             DBF.L   D0,$00400C 
00401C    33C100006004         MOVE.W  D1,$00006004 
004022    4E75                 RTS

While I could enter the programs as hex data from the text, or use TUTOR's built-in assembler, I have been entering the source code on a Linux computer and cross-assembling it using the VASM assembler. Then I can load the Motorola hex (run) file generated by the assembler into the TS2 computer over the serial port.

Here is the source code corresponding to the disassembly above:

DATA     EQU     $6000
PROGRAM  EQU     $4000
STRING   EQU     $6001           ADDRESS OF FOUR DIGIT BCD STRING
RESULT   EQU     $6004           ADDRESS OF RESULT
         ORG     PROGRAM
PGM_7_4A MOVEA.W #STRING,A0      POINTER TO FIRST BCD DIGIT
         MOVEQ   #4-1,D0         NUMBER OF DIGITS(-1) TO PROCESS
         CLR.L   D1              CLEAR FINAL RESULT - D1
         CLR.L   D2              CLEAR DIGIT REGISTER
         BRA.S   NOMULT          SKIP MULTIPLY FIRST TIME
LOOP     ADD.W   D1,D1           2X
         MOVE.W  D1,D3
         LSL.W   #2,D3           8X = 2X * 4
         ADD.W   D3,D1           10X = 8X + 2X
NOMULT   MOVE.B  (A0)+,D2        NEXT BCD DIGIT,(D2[15-8] UNCHANGED)
         ADD.W   D2,D1           ADD NEXT DIGIT
         DBRA    D0,LOOP         CONTINUE PROCESSING IF STILL DIGITS
         MOVE.W  D1,RESULT       STORE RESULT
         RTS
         END     PGM_7_4A

The VASM assembler is almost entirely compatible with the Motorola assembler and I have had to make only a very few changes to the code listed in the book. I did find a couple of errors, too.

So far I have entered almost four chapters worth of examples, just over thirty programs. I have placed the code on my github account. I'll continue doing so until I either get bored or finish the examples.

Monday, May 15, 2017

Building a 68000 Single Board Computer - Dr Dobb's Demos


I expanded a couple of the programs that I entered from Dr. Dobb's Toolbook of 68000 Programming to run on my TS2 computer under the TUTOR monitor. I added a main program that uses the output routines provided by TUTOR through its trap 14 interface to display output of the routine.

The first is the random number generator routine. The demo lists a series of 32-bit random numbers in decimal. Here is some of the initial output:

TUTOR  1.3 > GO 1058
PHYSICAL ADDRESS=00001058
16807
282475249
1622650073
984943658
1144108930
470211272
101027544
1457850878
1458777923
2007237709
823564440
1115438165
1784484492
74243042
114807987
1137522503
1441282327
16531729
823378840
143542612

The second demo is for the square root routine. It lists the integer square roots of the numbers from 0 to 100 000, displaying the number and its corresponding square root. Here is some of the initial output:

TUTOR  1.3 > GO 10B0
PHYSICAL ADDRESS=000010B0
0 0
1 1
2 1
3 1
4 2
5 2
6 2
7 2
8 2
9 3
10 3
11 3
12 3
13 3
14 3
15 3
16 4
17 4
18 4
19 4
20 4

And here is the final output as it reached 100 000:

99980 316
99981 316
99982 316
99983 316
99984 316
99985 316
99986 316
99987 316
99988 316
99989 316
99990 316
99991 316
99992 316
99993 316
99994 316
99995 316
99996 316
99997 316
99998 316
99999 316
100000 316

Thursday, May 11, 2017

Building a 68000 Single Board Computer - Dr. Dobb's Toolbook of 68000 Programming



As part of my 68000 retrocomputing work, I've been collecting some old books on 68000 programming. One book that I have been searching for some time is Dr. Dobb's Toolbook of 68000 Programming. I was recently able to acquire a copy.



It is a collection of articles on the 68000 that were originally published in Dr. Dobb's Journal. Most of them were added or updated for publication in the book. It is an interesting collection of articles, ranging from an introduction to the 68000 family to some Forth implementations and 68000 assemblers, and some small programs.

Gordon Brandly's Tiny Basic, which I have earlier run on the TS2 board, is included.

Some of the articles are quite relevant to my recent work on the TS2 board. There is one on the TUTOR monitor program and another and on bringing up a new 68K board by getting it to freerun.

Overall, it makes for an interesting read. I even know one of the authors -- someone who did some consulting work (not 68000 related) where I was once employed.

This particular copy came from the University of Hertfordshire in England and contains the original library markings and card.



While some of the articles have very large software listings (like a 68000 assembler written in Modula-2), four of the articles were relatively small self-contained programs: I entered the source code for them and put the code up on my github account.

I successfully assembled them with the VASM assembler and ran them on my TS2 board under the TUTOR monitor. They all seemed to work flawlessly (with the exception of one that is not a complete self-contained program).

It was quite straightforward to run them under TUTOR, I added a small main routine with a JSR followed by a TRAP #14 to return to TUTOR after execution. From TUTOR I could set register values to fill in the parameters and the look at the values returned.

Now I'm debating whether to type in 88 pages of Modula-2 source code for an assembler - I don't think I can find a suitable compiler.

Wednesday, May 10, 2017

Programming the MeinEnigma - Alarm Clock Example



In this installment we'll tie together much of what we looked at in this blog series with a larger application.

I decided to implement a simple digital clock program with alarm. These were the basic requirements and features:

  1. Time display, 12 hour mode: 100 through 1159 with rightmost decimal point indicating pm. 24 hour mode: 0000 through 2359. The second decimal point will toggle at a one second on/one second off rate whenever the time is displayed. The time is also displayed in binary using the discrete LEDs. The hours are shown on the LEDs in the first row, minutes on the second row, and seconds on the third row.
  2. Time set function: pressing four keys below display will perform as follows (left to right): go back one hour, advance one hour, go back one minute, advance one minute.
  3. Chime: When enabled, will beep on the hour using the buzzer.
  4. Alarm: When enabled, buzzer will sound at one second on/one second off rate until any key is pressed. Leftmost decimal point indicates alarm on.

Keyboard keys:

D - Display date briefly, e.g "JA 1" "2017" or "DE24" "2018". Shows month and day, then year, then goes back to time mode.

C - Toggle chime. Briefly Display "CH Y" or "CH N", then goes back to time mode.

M - Toggle between 12 and 24 hour mode. Briefly display "12HR" or "24HR", then go back to time mode.

A - Toggle alarm on/off. Briefly display "AL N" or AL Y", then go back to time mode. Leftmost decimal point goes on when alarm is enabled.

S - Set alarm. Pressing keys under display will set alarm time, similar to time set. Pressing S again will exit alarm set mode. Leave alarm set mode if no key pressed for more than 5 seconds.

T - Set date. Pressing keys under display will set month and day, similar to time set. Pressing T again will exit date set mode. Leave date mode if no key pressed for more than 5 seconds.

I won't go over the source code line by line. The software can be found here.

It consists of one main loop which performs various functions:
  1. Get the current time from the RTC.
  2. Display current time, alarm time, or date depending on mode in effect.
  3. Display time in binary on the discrete LEDs.
  4. Check if it is time to play the chime (hour rolled over).
  5. Check if it is time to play the alarm. Toggle alarm beep if it is active.
  6. If no keys pressed for 5 seconds, exit alarm or date set modes and revert to time mode.
  7. Check if key pressed.
  8. If alarm is active, pressing any key will turn it off.
  9. Update how long since a key was pressed.
  10. Handle time/alarm/date set keys 1-4.
  11. Handle date key.
  12. Handle toggle chime key.
  13. Handle 12/24 hour mode key.
  14. Toggle alarm on/off key.
  15. Handle alarm mode key.
  16. Handle set date keys.
  17. Delay 1 second, unless a key was pressed.
  18. Toggle seconds decimal point.
The program was written for clarity and not intended as a polished application. Several things could could be made more efficient and there are some obvious enhancements, some of which are listed under "to do" at the top of the file.

It doesn't use the the sound module or the rotors. These could obviously used for sounds and for setting the time or date.

Incidentally, there are Arduino-based clocks on the market. The "new" Heathkit, for example, offers one as a kit which looks like a traditional digital clock and is Arduino based.