• Please review our updated Terms and Rules here

MT86: a real-mode multitasking OS

Mike Chambers

Veteran Member
Joined
Sep 2, 2006
Messages
2,621
some might remember that OS i was working on late last year. i had gotten caught up on the FAT driver, and put the project aside. i decided to give it another go from scratch again about a week ago, and this time things are working out. the code is much more structed and well organized now that i have a better idea what i'm doing. i've got a working FAT12/16 driver that can load a rudimentary shell executable from disk, and any other program i want to make for it.

here's a quick overview of the kernel itself:
-true pre-emptive multitasking, process forking, parent processes can be made to pause and not eat CPU until the child returns, etc
-support for multiple virtual and pseudo terminals (like *NIX -- ctrl-alt-F# keys switch between terminals)
-interprocess signalling and communication
-FAT file system support (no write support yet, though... soon enough)

i plan to integrate the TCP/IP stack i wrote as part of the kernel so that it's accessible to user programs via the kernel API, and i'll be making it use normal DOS packet drivers so just about any network card will do the job. i'm going to make a telnet daemon for it too that attaches to pttys, so it can be operated remotely.

the shell only supports "exec" to load a binary and fork a new process, and "ps" to list active processes so far but i am trying to work it into something better.

obligatory screenshots ;)

booted into the shell on vtty #1, and listing running processes:

mt86-shell.png




and, you see plasma.bin up there running on vtty #4, which looks like this (but of course animated) when you switch to that terminal:

mt86-plasma.png




i've tried it several real machines too, from 8088 to 486. no stability problems so far. if anybody wants to get involved in the project let me know, i really think this could turn into something cool for our vintage systems... anybody who wants to write programs for it once the API expands a bit is welcome to, i'll provide all the info needed. i'm open to anybody who wants to get involved with the kernel itself too.

i'm aiming to make it somewhat like MINIX 2.x, but not POSIX and much lighter. it of course it isn't going to have all the features MINIX offered, but that's kind of a good thing. ever used MINIX 2 on an 8088? slow!
 
Last edited:
thanks pearce_jj. i'm hoping more people think so, too. i'd love to see this grow with support from other vintage nuts. :p

if you've got qemu or fake86, you can run it and see it in action so far. i put together this rar of the disk images like 12 hours ago though, and i didnt have exec working in the shell yet but "ps" works and you can still switch terminals and see the plasma demo running on vtty 4.

http://rubbermallet.org/mt86-0.11.9.7-demo.rar

it's a floppy and a hard disk image, they need to be run together. my bootloader doesn't do FAT yet (i wrote it about a week ago), so it reads the kernel from raw sectors on the floppy and runs shell.bin and plasma.bin from the root of the first hard drive partition.

qemu command line: qemu.exe -fda mt86.144 -hda mt86.raw -boot a
or fake86: fake86.exe -fd0 mt86.144 -hd0 mt86.raw -boot 0

it demonstrates the multitasking ability pretty well.
 
Haha, I was wondering if this was still in development. I'll give it a try as soon as it boots from floppy alone...

So, is 12KB the application heap for the shell instances, or the whole thing? Seems if you restricted your applications to not using self-modifying code (admittedly, impossible to enforce on 8086,) you could make better use of 640KB memory by sharing code segments across instances...

Also, any plans to support 286 protected mode?
 
Haha, I was wondering if this was still in development. I'll give it a try as soon as it boots from floppy alone...

So, is 12KB the application heap for the shell instances, or the whole thing? Seems if you restricted your applications to not using self-modifying code (admittedly, impossible to enforce on 8086,) you could make better use of 640KB memory by sharing code segments across instances...

Also, any plans to support 286 protected mode?

the old one i was working on turned into an ugly mess, but yeah i definitely still plan to keep working on it now.the RAM usage shows in the process list is the amount of RAM each separate one has to itself. although, i gave the shell instances an extremely generous 4 KB of stack space when i could probably get away with a fraction of that. i might look into the code sharing thing later, but for now i am trying to keep it as simple as i can.

i should have a self-booting floppy image within a day or so btw.
 
Mike, does your system have a guaranteed maximum response time to an event (e.g. keypress, etc.)? In other words, can it be classified as "time-critical"?
 
Haha, I was wondering if this was still in development. I'll give it a try as soon as it boots from floppy alone...

So, is 12KB the application heap for the shell instances, or the whole thing? Seems if you restricted your applications to not using self-modifying code (admittedly, impossible to enforce on 8086,) you could make better use of 640KB memory by sharing code segments across instances...

Also, any plans to support 286 protected mode?

Self modifying code? Oh, the horror!

Shared libraries are a good thing. But even on something primitive like an 8088 there is no need for self modifying code. The compiler (toolchain) should generate an executable with relocation information and external references that need to be resolved. At program load time the OS relocates the code to the right area of memory and tries to resolve the unresolved references. That means loading the shared libraries that contain the targets, or throwing an error and cleaning up if the unresolved references can't be found.

So yes, things get modified - the loader does the fixup based on the unresolved references identified in the executable. But no self modifying code ...

On a small machine I'd just make the following assumption/assertion: the libraries that you are going to use need to be loaded at OS load time, or shortly after. You really can't be loading shared libs after applications are up because of memory fragmentation.


Mike - what are you using for an application compiler, and do you have any sort of DOS compatibility? (How are you going to get apps running on it?)



Mike
 
well chuck, the task-switcher is set by default to run 100 times per second via the i8254, but the problem with real-mode CPUs with no protection at all is you can't really guarantee much of anything. any naughty process could run a CLI and never give another process any time ever. so my kernel is just best-effort. as along as all your programs are well-behaved the response time is quite good.

i am definitely going to add the ability to assign priority levels to processes to help with stuff where timing is important.
 
Self modifying code? Oh, the horror!

Shared libraries are a good thing. But even on something primitive like an 8088 there is no need for self modifying code. The compiler (toolchain) should generate an executable with relocation information and external references that need to be resolved. At program load time the OS relocates the code to the right area of memory and tries to resolve the unresolved references. That means loading the shared libraries that contain the targets, or throwing an error and cleaning up if the unresolved references can't be found.

So yes, things get modified - the loader does the fixup based on the unresolved references identified in the executable. But no self modifying code ...

On a small machine I'd just make the following assumption/assertion: the libraries that you are going to use need to be loaded at OS load time, or shortly after. You really can't be loading shared libs after applications are up because of memory fragmentation.


Mike - what are you using for an application compiler, and do you have any sort of DOS compatibility? (How are you going to get apps running on it?)



Mike

currently, the way i'm handling user apps interfacing with the kernel is through interrupt calls. i haven't decided exactly what i'm going to use for a compiler that can run inside MT86, but i'm sure there is something out there. i suppose if nothing else, Turbo C++ 1.01's compiler could be used. they've public domain'd it, no?

that's not an ideal solution though. i'd like to find a decent 16-bit compiler that's open source and can be ported to a native MT86 app. i do have a DOS compatibility layer though, but it's not very robust right now. i plan to support at least most of the major DOS 2.x calls, but ideally i'd rather not try to use many DOS apps in it. some can behave very, very badly and wreck everything since they have no idea they're in a multitasking environment.
 
I was going to ask you how you multitask the video display, particularly when different programs use different video modes and mappings.

the applications need to do all their console I/O through system calls the kernel provides, and each terminal has it's own screen buffer memory allocated. if a terminal being updated is the currently visible one, the buffer gets rep movsw'd directly to B800:0000 (or B000:0000 if an MDA is used)

yeah, it only supports 80x25 text mode for all terminals right now, but i'm thinking about making it able to keep track of which mode each is in and switching back as terminals are switched between. i dont even plan to bother with graphics modes, that would just take too much RAM and be too slow on my target PCs.

each tty has a struct:

Code:
struct structtty {
    uint8 buf[4000];
    uint16 cx; //current cursor X pos
    uint16 cy; //current cursor Y pos
    uint16 mx; //total screen columns
    uint16 my; //total screen rows
    uint8 curattr; //current text attribute byte
    uint8 typetty; //is this a local console buffer or a pseudo-tty (for telnet server)
    uint8 keybuffpos; //how many keystrokes are waiting in our input buffer
    uint8 keybuffer[64];
} tty[MAX_TTY];

there are also a couple system calls i came up with for high-performance video access, an application can make a call to get the pointer to it's terminal buffer so it can directly update instead of making tons of locate/print calls. there's another call that tells the kernel to immediately copy the terminal buffer to video memory if it's the active one.

that's what i use in the plasma demo for really fast animation... plus the fact that i have to make another call to actually put the buffer into video RAM when doing direct manipulation ensures smooth animation with no "tearing" artifacts. :)
 
Last edited:
Interesting. I'm trying to recall if Tandy 1000/PCjr computers' relocatable video-memory window can be changed with a granularity fine enough to simply switch directly between terminals' buffers rather than performing a copy...instant switching'd be nice, though Tandy-specific stuff is admittedly fairly limited-application.

(Alternatively, I know of at least two Tandy 1000 models that can have 768KB RAM, the extra 128KB of which is nominally video memory, but it would be nifty to allow its use as main system memory, as I believe it's mapped into the main address space.)
 
Interesting. I'm trying to recall if Tandy 1000/PCjr computers' relocatable video-memory window can be changed with a granularity fine enough to simply switch directly between terminals' buffers rather than performing a copy...instant switching'd be nice, though Tandy-specific stuff is admittedly fairly limited-application.

(Alternatively, I know of at least two Tandy 1000 models that can have 768KB RAM, the extra 128KB of which is nominally video memory, but it would be nifty to allow its use as main system memory, as I believe it's mapped into the main address space.)

that's a really neat idea actually. but yeah it's of limited use, and since i'm just doing a 2000-count rep movsw, even at 4.77 MHz it's literally instantaneous. speed isn't an issue at all.

you gave me an idea though, in addition to one of those tandys with 768 KB of RAM, i could also use the RAM on machines with VGA cards right up to 0xB7FFF -- which would make 736 KB available to the kernel and all applications. :twisted:

since i will never be using any graphics modes, that wouldn't be a problem. i could do a quick test when the kernel first loads to find all writable memory from the end of the kernel at 0x20000 through 0xB7FFF.
 
Back
Top