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)