[comp.os.minix] Summary: compiling with MSC. Also, Generic Disk Driver

ericr@ipmoea.UUCP (Eric Roskos) (07/26/87)

Recently I said that I'd post a list of the problems I found in getting
Minix to compile with Microsoft C.  Following is the list of the main
problems I found, along with some small pieces of source code required.

This is more a list of "things to watch out for"; a list of diffs would
be fairly large, though if there is enough interest I can try to get one
together.  [Of course, this message itself turned out fairly large... :-)]

Following this list is some information on my current Minix project, which
so far is working well, a generic fixed disk driver which one can use until
one has gotten a "real" driver working.  Anyway, here's the list.

IMPORTANT DISCLAIMER: All the following is from my own experience; it is
not "authoritative" in any sense, just my understanding based on what I had
to do to get Minix to compile.  I have no special information on MSC or
anything, so if you spot factual errors, please point them out...

1) C source files.  This was the good news; all the C source files compiled
with no trouble at all.  The problems were in the .ASM files.  The C sources
are really clean.

2) prologue.h.  This was the source of most of the subtle bugs, and took a
long time to get right.  Prologue.h contains a list of segments, and
a "group" directive.  The thing about this file is that there are a number
of data segments, which have names like @DATAV, which are grouped by a GROUP
directive into what becomes the data segment.

The ordering of these segments is extremely important.  Data are in these
different segments specifically so they will appear in a certain order in
the linked object module.

But what makes it especially hard is that MSC also creates its own segments,
of various classes, which also have to appear at certain places relative to
the above segments: some of these are of class 'DATA', some are of
class 'CONST', and some are of class 'BSS'.  The linker groups all the 'DATA' 
segments before the 'BSS' segments (with this configuration of segments, 
anyway), so if you use the segment classes that come with the original 
prologue.h, MSC's 'BSS' segments will come after what is supposed to be the 
last data segment.  You have to change some of the prologue.h segments to be
of class 'BSS', and one has to be of class 'CONST' (before I did this,
the constants were getting zeroed out when the BSS got initialized).

Following is the prologue.h file I am currently using.  Microsoft has
announced that a new C compiler, C 5.0, will be coming out shortly; I don't
know if this prologue.h file will work with that compiler or not (if not,
I'll post a new one if someone else doesn't do it first).  Here's the file:

--- start of prologue.h ---

;	prologue.h	
;	standard prologue for MSC assembly code
;	This file defines the correct ordering of segments

_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
_TEXT	ENDS
_DATAB	SEGMENT  PARA PUBLIC 'DATA'
_DATAB	ENDS
_DATA	SEGMENT  BYTE PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT  BYTE PUBLIC 'CONST'
CONST	ENDS
_DATAT	SEGMENT  BYTE PUBLIC 'CONST'
_DATAT	ENDS
_BSS	SEGMENT  BYTE PUBLIC 'BSS'
_BSS	ENDS
C_COMMON SEGMENT BYTE PUBLIC 'BSS'
C_COMMON ENDS
_DATAU	SEGMENT  BYTE PUBLIC 'BSS'
_DATAU	ENDS
_DATAV	SEGMENT  BYTE PUBLIC 'BSS'
_DATAV	ENDS

DGROUP	GROUP	_DATAB, _DATA, CONST, _DATAT, _BSS, C_COMMON, _DATAV

	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

---- end of prologue.h ----

There are a couple of things to notice in the above.  First, I changed
all the ears (@) to underscores to be compatible with MSC's segment naming
convention.

Also, importantly, note that MSC's main data segment is called _DATA, its
text segment is called _TEXT, and its BSS segment is called _BSS.  There
is also a segment called C_COMMON which seems to have BSS globals in it;
finding out about that was one of my last bugs, and was causing the stack
to be allocated in a bad place in the kernel for a long while.  The other
segments above do things like mark the boundary between data and BSS, mark
the end of all data, etc; and the first one contains data that have to be
at the front of the data segment (e.g., magic numbers, etc.).

If you accidentally link with MS-DOS libraries, you'll get all kinds of other
segments too; check the map from the linker after you link a module to be
sure you don't have any segments with names other than the above, particularly
segments with names beginning with X, which contain tables used by the DOS
application startup and termination routines.

You'll have to change all the segment names in the .ASM files to match the
above; but you don't have to duplicate anything other than the segment
name and the word SEGMENT, e.g.,

_DATAV	SEGMENT

since you've already declared all the other information in the prologue.h
file.  This makes it easier for you to change prologue.h if you need to,
without having to change all the SEGMENT directives in the source files to
match.

Some of the files also have a segment named @STACK in them; I left that
unchanged.  I don't think you actually need it; the linker produces an error
message if you omit it, but it is just a warning message.  Apparently @STACK
is there just to suppress the warning message.

3) External references.  If you successfully compile and link all the above,
along with the changes I've listed in the sections below,
you'll find that you have two types of external references unresolved.  The
first are files that are in the Minix library; it turns out you have to get
the library archive file and 'ar' command, compile 'ar', extract the library
routines, and compile them (making changes described elsewhere in this posting)
in order to link them with the OS modules.  The library routines contain a
good number of .ASM files which have to have changes made to them.  

Also, notice that the library sources contain both a head.asm, and a crtso.asm,
both containing the entry point declarations, start-off code, etc., for
a Minix object file.  head.asm is what you need for the OS modules; crtso.asm
is for executables that run under Minix.  In *both* of these, you need to
declare the symbol __acrtused, and make it public; I declared it as

	PUBLIC 		__acrtused 
	__acrtused	EQU	9876H

MSC seems to emit external references to this symbol in order to cause
a startup module to be included (I think)... anyway, in the DOS libraries
it's defined in the startup module, so I also defined it in Minix's startups,
in order to allow the reference to be resolved.

The other kind of unresolved external reference is to symbols whose names
start with "__a".  These turn out to be "helper" files from LIBH, containing
things like the 32-bit arithmetic routines.  So, you have to also add LIBH to
your list of libraries in the link command.  LIBH doesn't contain anything
other than these "helper" files (as far as I know), so it is OK just to
list LIBH; you don't need to extract the named files the way it says in the
textbook (I think that's why LIBH is separate, but I'm not sure).

You do need to specify the /NODEFAULTLIBRARYSEARCH option (/NOD for short)
to the link command, however, since the object files from MSC contain
records telling the linker to search some of the MS-DOS libraries.

4) Symbol names.  For some reason, apparently some limitation in C86, all
the symbols in the .ASM files longer than a certain length are truncated to
that maximum length.  You have to find these and fill in the proper, 
full-length, names, since MSC's references to them are not truncated that
way.  Fortunately, in most of the .ASM files, there's a comment next to
each truncated name telling what the name originally was (at least, for a
number of them).

5) build.c (actually, absread/abswrite).  As I posted earlier, the absread
and abswrite that come with Minix don't work with DOS 3.2.  Someone recently
posted a fixed version of these routines that is a good bit more general
than mine; here's the one I used:

---- start of absio routines ----

#ifdef C_DISKIO

int DMAoverrun(buff1)
char *buff1;
{
int i;

	i = (int)buff1;
	return(i > i + SECTOR_SIZE);
}

int
absio(fn, drive, blocknr, buff)
int fn;
int drive;
int blocknr;
char *buff;
{
union REGS iregs;
union REGS oregs;
int track;

	iregs.h.ah = fn;
	iregs.h.dl = drive;
	track = blocknr / 9;
	iregs.h.dh = track & 1;
	iregs.h.ch = track >> 1;
	iregs.h.cl = (blocknr % 9) + 1;
	iregs.h.al = 1;
	iregs.x.bx = (int)buff;

	int86(0x13, &iregs, &oregs);

	if (oregs.x.cflag)
	{
		fprintf(stderr, "absio: error %sing drv %c block %d address %x: bios code %x\n",
			fn==2? "read" : "writ", 'A'+drive, blocknr, buff, oregs.h.ah&0xff);
		return(oregs.x.ax);
	}
	else
	{
		return(0);
	}
}

int
absread(drive, blocknr, buff)
int drive;
int blocknr;
char *buff;
{
	return(absio(2, drive, blocknr, buff));
}

int
abswrite(drive, blocknr, buff)
int drive;
int blocknr;
char *buff;
{
	return(absio(3, drive, blocknr, buff));
}
#endif

---- end of absio routines ----

6) Missing routine.  The MPX88.ASM file was missing the routine wini_int;
I copied mine from the PCIX source directory's MPX88.ASM (and made the
appropriate changes to labels).

7) Errors in vid_copy.  The source for vid_copy in KLIB88.ASM had several
errors.  First, 4 lines under the label vid5, there is a

	mov	8[bp],0
	cmp	4[bp],0

these need to have WORD PTR added in front of the n[bp] operand to tell
the size of the thing being zeroed.

Second, the label vid6 was missing; I added mine just before the line

	pop	es	; restore registers

10 lines below the label vid5.  I think that's right; it seems to work OK.

8) Symbols referenced from C (or in C source files): MSC puts an underscore
in front of symbol names; so all the symbols which are referenced from C,
and symbols which are external references to symbols defined in C source files,
have to have underscores put in front of them.

And... as far as I can remember, that's all of it.  Most of the fun of
systems programming is trying to fix a large program that won't run when
you have no debugger or other tools to tell you why it won't work, so
the above can help you to have the Systems Programming Experience. :-)
Seriously, though, if there's enough interest, I can post diffs, though
it will take a good bit of work to do it, so I don't want to do it unless
there is sufficient interest.  It would be easier to post or archive the .ASM
files somewhere, because there are a lot of changes.

And now, for some other news...

This weekend I've been working on what all the above was leading up to,
namely, writing a "generic" winchester driver that one can use to get Minix
running until one has successfully adapted or written a device driver for
one's own particular brand of disk controller.  I currently have it working,
but only got it working a few hours ago, so it may be just that I haven't
found The Big Problem that keeps the whole thing from being possible at all.
You don't get the benefit of the 
	receive(HARDWARE, &w_mess);
call in w_transfer, but this is not as bad as, say, a floppy disk driver;
and, as John Gilmore pointed out about a month ago, this approach does let one
use Minix while one is developing the proper driver.  I don't know if there
is anything else that will go wrong or not.

If it works after a good bit of testing, I can post it.  It required adding
some assembly-language routines to klib88.asm (though they would probably
be better in a separate file), and changing the initialization in main.c
to not overwrite the BIOS's reserved vectors (40H-57H) and the disk BIOS
call vector (13H); obviously this is Not Recommended for Students, since
now you don't get an error message when you have int's with these addresses,
but it allows the disk to be accessed using the BIOS that comes with the
controller.  (With my machine*, at least, vector 13 (before DOS tampers
with it) points directly to the hard disk controller's BIOS entry point,
so this works very well).  I so far haven't figured out anything that should
go wrong with this approach; the hard disk BIOS changes the DS, but I
notice in save() that they save the DS (rather than assuming it stays
fixed), so I don't think that is a problem; and I think interrupts are still
on while the hard disk BIOS is working, so I don't think you lose any
data in there (otherwise terminal emulators, etc. wouldn't work with hard
disks under DOS, since they have to do interrupt driven serial I/O; I've
written several of those and had no problems).

*My machine has a DTK CPU board with an MCT Bios (by someone named E.S.O'Neal)
 in it... I replaced the BIOS it came with it because it was highly
 incompatible with IBM's BIOS, so I don't know if the original DTK BIOS
 will work OK with this or not... at first I hardcoded a simulated INT into
 the hard disk controller BIOS into my driver to avoid doing an INT 13, but
 that is not very generic, and also I think probably that the hard disk BIOS
 uses the reserved vectors anyway; I took out the overwriting of the BIOS
 reserved vectors from the start, since it's not good to mess with those.
 This is one of those clones you get from DoKay and put together yourself.

One thing I did notice, though... these winchester drivers that come with
Minix don't have a lot of internal consistency checks... for example, if
you have an errant program that overwrites part of the "wini" table,
and then you do a disk write, you can suddenly be writing into some other
disk partition.  I added some to my generic driver; if you're writing a
driver, it might be a good thing to do that, too.  When you're running
on a machine with no memory protection, you need to keep in mind what kinds
of things might go wrong if one of those programs out there writes over your
tables, particularly where you're writing to long-term storage...

Well, that's about it... sorry this is so long, I don't post much to the
Usenet any more, as you know, and must have built up a lot to say... :-)
This Minix is a lot of fun.

-- Eric Roskos
   uw-beaver!tikal!amc!ipmoea!ericr
   (formerly jer@peora)