[comp.os.minix] MINIX and the 680X0

eesrajm@cc.brunel.ac.uk (Andrew J Michael) (01/13/91)

This article has been written in response to the continued questions about
the use of MINIX on 680X0 processors where X > 0.  I finally got my act
together and put something in writing.

I have ported MINIX to three different 68020 boxes over the last few
years, using MINIX-ST as a starting point.  I hope that the knowledge
gained in the process will be useful to others.  Not all of the material is
new, but several undocumented features of MINIX-ST are discussed.  The article
is aimed at those porting MINIX to 680X0 machines which have not previously
run MINIX, but also contains the information needed for converting from a
68000 to a 680X0.  There are probably some errors or omissions, so if anyone
spots any, please let me know.  Note that the distinction between general 
principles and 680X0 specifics is somewhat blurred.  If in doubt, read both
sections.

You may use this article for any purpose that you think fit, but I'd be 
grateful if you acknowledged its authorship.


GENERAL PRINCIPLES

This article is NOT intended as a general purpose recipe for porting MINIX, but 
some idea of the issues involved may be relevant.  

When the author carried out his first 68020 port, an Atari ST running a
very early version of MINIX-ST was used as the host system.  Such an approach
has several advantages - the host already has a known working MINIX C compiler,
and is directly capable of producing boot disks for the target system.  Now that
MINIX is available for the Amiga, it would make an equally suitable platform.
I don't consider the Macintosh suitable due to its use of non-standard floppy
disks, although since most later Macs can read and write MSDOS floppies this
notion may be incorrect.  There is no absolute reason why the target machine 
should use MINIX floppy disks, but all the ports undertaken so far have booted 
from a traditional MINIX boot floppy.  Since I consider it highly desirable to 
allow the 680X0 to read and write MINIX-ST floppies once the port is complete, 
a 720k 3.5" floppy has always been made part of the target hardware and used to
boot the system.

The first thing required is a bootstrap to load the MINIX image from disk into 
memory. Examination of the code in tools/boot.s will show that the MINIX-ST 
bootstrap loads the first sector of the boot floppy into memory and looks at
the location nsect to determine the total number of sectors required to load.
The boot floppy is then read sequentially into a buffer located at some
suitable address in memory.  Once the required number of sectors have been
loaded, the image is copied from the buffer into memory starting at address 0.
The code then jumps to the restart vector at address 0.  On the ST it is 
important not to overwrite some TOS system variables in the area 0x0400 to 
0x0600, so the bootstrap skips over this region.  Although this might be 
unnecessary for other hardware, leaving this feature in saves possible
confusion later.  As a general rule, the less you change, the less there is to
go wrong !  In particular, if you remove the skip, remember to alter the code
in kernel/mpx.s accordingly. The "reserved" area left by the skip may also be
useful for pseudo DMA, as explained later.  

You should also note that the code of both tools/boot.s and tools/build.c
changed at MINIX-1.5, and the bss segment is now handled differently to
earlier versions of MINIX-ST.  To save space, instead of loading a pre-cleared
bss from the boot disk, the bss is cleared by the bootstrap.  Make sure that
your build and bootstrap are compatible, or some very strange things can
happen.

The bootstrap code needs to be written for the target machine before anything 
else can proceed, unless the transfer of code to the target will be by a serial
link or similar.  Two of the authors ports were to bare hardware, so the 
bootstrap was written in assembler (under SK*DOS/68k) and blown into ROM.  On 
the third, which originally ran OS9/68k, a small C OS9 program was used to load 
MINIX initially.  The bootstrap can be tested by attempting to load a disk 
containing a "Hello World" program or similar.

Once the bootstrap is in place, development proper can proceed.  So far it has
been assumed that the host system runs MINIX/68k and its own C compiler will 
be used for development.  There is of course no reason why MINIX cannot be
cross-compiled on a host using gcc or some other C compiler producing 68k
output.  In this case make sure that your cross-compiler produces known
working MINIX code; you don't want to be actually debugging the C library when 
you think you are debugging device drivers !   Also beware endian problems
when using a host machine which does not itself use a 680X0 processor.

If you want to use gcc, excellent ports of gcc up to version 1.36 were provided
by Jhawar Bammi and are still available from various archive sites.  These
provide both cross and native gcc compilers, and I have used the cross-
compiler running on a Sun-3 to compile many utility programs for MINIX-ST.  You
should be aware that the libraries provided are pre MINIX-1.5, and this may
cause some problems.  I have produced a version of gcc-1.37.1 which
uses MINIX-1.5 libraries.  By default, all these versions of gcc will produce
code using 32-bit ints in order to solve difficulties with the large amount
of public domain code which assumes (sizeof (int) == sizeof (char *)).  The
MINIX-ST kernel needs to be compiled with 16-bit ints, and gcc supports this
through the -mshort compile-time flag.  All the author's 680X0 ports run 
versions of MINIX-1.5 with gcc compiled kernels.  A gcc compiled dhrystone on
a gcc compiled kernel is nearly twice the speed of the ack compiled originals.
Throughout this article, "gcc" refers to Bammi's port, or derivatives thereof.

Because of the twin 16 and 32-bit nature of gcc, some snags can arise when
compiling the MINIX source.  In general, be very careful of sizeof's, which
need casting to int.  A more annoying feature is that gcc uses the gnu 
assembler gas, which only understands MIT 68k assembler syntax.  This means that
the kernel assembler files need to be converted from Motorola syntax to MIT
syntax before using gcc.  Unless you are very sure (or have no choice), it may
be better to use the ack C compiler to get your port running and convert to gcc
later.

I will assume that you have gathered enough information about the target
machine to be able to write device drivers for it.  You may be able to borrow
some code from existing versions of MINIX.  One important point to realise is
that you do not need to have DMA hardware to run MINIX, although all the
official versions use it.  On two of the author's 68020 ports, the floppy
accesses are carried out by "pseudo DMA".  The floppy controller is a WD1772,
set up to produce a level 7 interrupt from its DRQ pin and a level 2 interrupt
from the IRQ pin.  The code in kernel/floppy.c is very close to that of the
MINIX-ST original except that the commands are issued directly to the 1772
command register.  A dummy DMA device is also programmed with the direction
of the transfer, the address of the data and the number of bytes required.  The 
registers of the dummy DMA device are actually memory locations in the 
"reserved area" mentioned above.  Code in kernel/mpx.s handles the DRQ 
interrupts generated as data is presented, using the dummy DMA registers to 
determine the address of the data and the direction in which to copy it.  These 
level 7 interrupts are completely hidden from MINIX and simply dealt with by a 
few lines of assembler.  On completion of a transfer, the 1772 produces an IRQ 
signal, which then calls the MINIX fdcint() routine.

It is perhaps worth commenting on the way in which MINIX-ST determines the 
amount of ram present in the machine.  On the Atari ST, there is a TOS variable
at 0x0436 which contains the highest ram memory address.  (This is one of the 
variables in the "reserved area" which the bootstrap doesn't overwrite).  This 
variable is read by MINIX-ST during initialisation.  To avoid changing any more 
MINIX source than necessary, I have a line of code in kernel/mpx.s which puts 
the appropriate address in 0x0436.  If the amount of memory on your machine is 
liable to change, then a memory test in mpx.s (or preferably the bootstrap) 
might be a good idea.

You should also note that MINIX-ST blocks interrupts below level 2 with code
in kernel/main.c and kernel/mpx.s.  These will need modifying if you want to
use all the interrupt levels on your hardware.

Once you have a hopefully complete system compiled, you can try to boot it.
The TRACE routines in the kernel drivers are a great help here if things go
wrong.  The most important thing is to get console output working.  Debugging
is a lot easier once the patient can tell the doctor what is wrong !  If you
can't even get that far to start with, try some simple assembler routines in
mpx.s to output progress numbers to the screen or something similar.  At one
stage I had a port which was falling over in kernel/main.c, so I used some
code in mpx.s to beep at various points in the initialisation.


680X0 SPECIFICS

The official code for MINIX-ST and AmigaMINIX (I havn't seen MacMINIX) makes
several assumptions about the use of a 68000 processor.  Luckily these are
almost all contained in the file kernel/mpx.s, written in assembler.

The first thing to note is that mpx.s uses a 68000 dependant trick in its
exception code handling.  Most exceptions are dealt with by the common
handlers trp, int and non.  Since these handlers can be called by a wide
number of exceptions, MINIX needs to know which exception actually called
the handler.  

On a 68000, only the lower 24 bits of the address bus are present in hardware, 
so the contents of the upper byte are irrelevant.  On other words, the address 
appearing on the address bus will be the same regardless of the contents of the 
upper byte of the program counter.  MINIX-ST uses this to its advantage by 
placing the exception number in the high byte of each exception vector.  The
processor will jump to the (in)correct address, and the stacked program counter
can be examined to determine the exception responsible.

Unfortunately of course, this will have grave consequences on a 68020 where the
upper address lines are present and decoded.  The processor will jump to some
strange location and (hopefully) cause a bus error.  However, this will in 
turn call the bus error exception, and eventually the processor will halt with
a double bus error.  The author was originally blissfully unaware of this
problem because quite by accident my first 68020 did not use the upper 8 bits 
of the address bus.  If your hardware doesn't either, then you need not worry.
If it does, then replace the upper byte of each exception vector with zero.  My
preferred method is to write the vectors as longs, one per line, with a comment.
This takes up more space in the source code, but is much easier to read than a
pile of meaningless data statements.  Removing the exception number does of
course upset MINIX's exception identification, but the kernel will run in this
state although the vector numbers reported will be meaningless.  We will deal
with this later.

The next area of difficulty is that of stack frames.  The restart routine in
mpx.s works by setting up a dummy stack frame and then executing an rte.  This
is again processor dependant and will only work reliably on a 68000.  The
680X0 machines have a format word in the stack, but the 68000 doesn't.  If the
existing code is run on a 68020, the processor will examine the format word of
the dummy stack frame when executing the rte.  This has to be zero for the code
to work correctly, otherwise the processor will take the format exception trap.
The presence of the format word on a 680X0 also upsets the mpx.s save routine.

Thus in its simplest form, to get the stack frame right for a 680X0, change the
save and restart routines in mpx.s so that they read:

     move.l d0-d7/a0-a5,(a6)             
     move.l (sp)+,savea6(a6)         ! a6
>    lea    12(sp),a1                ! 12 because of format word
     btst   #5,4(sp)                 ! test old S-bit

     ..................

  L6:move.l a0,sp
> L7:move.w #0,-(sp)                 ! format word
     move.l savpc(a6),-(sp)          ! pc
     move.w savsr(a6),-(sp)          ! sr

The two lines marked with ">" are the minimum that need to be changed to get 
MINIX running on a 68020 (and presumably 68010).  I have recently had a report 
that this modification will allow AmigaMINIX to run on a machine with a 68020 
processor card.

Although this modification will work successfully, it is rather a hack than a
permanent solution, because the 680X0 can produce differing stack frames
depending on the cause of the exception.  We can tie the solution of this 
difficulty together with the problem encountered earlier of determining the 
cause of the exception when the upper byte of the exception vector is zero.  
Here are some example code fragments, based on original code by Inigo Cyliax.  
They are shown here in gcc (gas) MIT syntax.


	.text
|
| Trap vectors
|
.long	0		| Reset stack
.long	start		| Reset PC
.long   err		| Bus error
.long	err		| Address error
.long	trp		| Illegal instruction
.long	trp		| Zero divide
.long	trp		| CHK, CHK2
.long	trp		| cpTRAPcc, TRAPcc, TRAPV
.long	trp		| Privilege violation
.long	trc		| Trace
.long	trp		| Line 1010
.long	trp		| Line 1111
.long	trp		| Unassigned
.long	trp		| Coprocessor protocol violation
.long	trp		| Format error
.long	trp		| Uninitialised interrupt

   ...................................

|
| Perform task switch by save and restart
|
save:
	movw	#0x2700,sr		| ?
	movl	a6,sp@-
	movl	_proc_ptr,a6
	moveml	d0-d7/a0-a5,a6@
	movl	sp@+,a6@(sava6)		| a6
	lea	sp@(10),a1		| Point to format word
	btst	#5,sp@(4)		| test old S-bit
	beq	L45			| jump if usp
	movw	a1@+, d2		| Get format word in d2
	andw	#0xf000, d2		| Mask to give format type
	cmpw	#0x0000, d2		| Format type 0 ?
	beq	L50			| Yes
	cmpw	#0x1000, d2		| Format type 1 ?
	beq	L50			| Yes
	cmpw	#0x2000, d2		| Format type 2 ?
	bne	L41			| Jump if not
	addl	#8, a1			| Else add two words
	bra	L50

L41:	cmpw	#0x9000, d2		| Format type 9 ?
	bne	L42			| No
	addl	#12, a1			| Yes - add six words
	bra	L50			

L42:	cmpw	#0xa000, d2		| Format type a ?
	bne	L43			| No
	addl	#24, a1			| Yes - add twelve words
	bra 	L50

L43:	cmpw	#0xb000, d2		| Format type b ?
	bne	L50			| No
	addl	#84, a1			| Yes - add forty-two words
	bra	L50
	
L45:	movl	usp, a1			| Load a1 with old usp
L50:	movl	a1, a6@(savsp)		| old sp: usp or ksp
	movb	sp@, a6@(savtt)		| trap type
	clrb	sp@			| cleanup
	movl	sp@+, a1		| return address
	movw	sp@+, a6@(savsr)	| sr
	movl	sp@+, a6@(savpc)	| pc
	movw	sp@+, d2		| Get format word
	lsrw	#2, d2			| Convert it to vector number
	movb	d2, a6@(savtt)		| Save it as trap type
	addb	#1, _k_reenter		| from -1 if not reentering
	jmp	a1@


Note that I have made no attempt to determine the processor type on a run-time
basis.  Since my ports have always been to specific hardware, there hasn't been
any advantage in doing so.  For general purpose versions such as AmigaMINIX
there is an obvious case for detecting the processor type when booting and
acting accordingly.  Hopefully future official versions of MINIX will implement 
this.


OTHER ISSUES

Astute readers will have noticed that even when running on a 68020 I have
retained the existing 16-bit int and 32-bit pointer nature of the original
MINIX-68k kernel.  Although there are obvious advantages in using 32-bit ints
on a 68020, I decided that compatibility with MINIX-ST was more important.  To
use 32-bit ints would require changes to the C library as well as the kernel,
and I prefer to use a common C library for both 68000 and 680X0.  This is of
particular importance to me since I use a 68020 to develop software for the
ST, and I want them to run common binaries.  You may have other ideas; if 
anyone does produce a 32-bit kernel I'd be interested to know.

For the same reasons I havn't used any 68020 specific or 68881 code, even 
though my current 68020 machine has a 68881 on board.  Bammi's port of gcc was 
intended for the Atari ST, so the 68881 and 68851 generating code was removed
from gas.  When I ported gas-1.37 I put this code back, but havn't yet tested 
it.  Also remember that if you use the 68881 then alterations will have to be 
made to the kernel to handle the FPU registers on a context switch.

I also havn't bothered to flush the cache on a context switch, since this 
doesn't seem to cause me any problems.  It probably ought to be done for the 
68020, and will almost certainly need to be done for the 68030.  I have had a 
report that AmigaMINIX with the above mods to mpx.s won't run on a 68030, so 
this may well be an area to investigate.   I don't have a 68030 to experiment 
with, so I'd appreciate any feedback in this area.

Several people have told me that mm does some strange things with the upper 
byte of memory pointers.  I havn't investigated, but if this is true then it is
another area that may cause trouble if your hardware decodes the upper byte
of the address bus, and has ram at these addresses.

Best of luck, and happy porting !

Andy Michael
13 January 1991.


-- 
Andy Michael (eesrajm@cc.brunel.ac.uk)      "You might think that.  I
85 Hawthorne Crescent                        couldn't possibly comment."
West Drayton
Middlesex                                  
UB7 9PA