Good morning,
I have been still working on my no-OS loader. I am booting from drive 0, which loads to 07c00h. Boot works fine, displays message and such. The boot then read a few sectors which contain my loader program which right now is just a simple options program. I have been reading my RBIL memory map and trying to tighten up exactly where I want my loader to reside in order to leave maximum area for my transient programs to load in the future. It appears from the memory map that the last useful information leaves off around 080c0h, so is a byte after that a safe starting area to load my code and assume that anything up to my stack area at the top of that segment is fair game for loading transient code?
jon
Except for very old systems, you can generally depend on being able to use anything above the BIOS data area and below 'A' block. IIRC depending on the system configuration the BIOS may use some memory directly below 'A' block, I think not more than about 1 KB. So the usable absolute address range should be 500h to 9fbffh. Have you considered using the HMA?
Hi Michael,
When you refer to the 'A' block you are talking about video memory, right? What about 'HMA', is that high memory you are talking about?
jon
Hi Jon,
Yes and yes. The 'A' is the first hex digit of the address of the 64KB block of address space into which the graphics display buffer is mapped. "B" block is also used for video memory (the alphanumeric display buffer, font storage, and at one time mono graphics), and 'C' block contains the VGA BIOS. The High Memory Area (HMA) is a 65520-byte block of "extended" memory that can be accessed from real mode in the address range FFFF:0010 to FFFF:FFFF. To use it you basically just enable the A20 address line and use FFFFh for the segment address.
Hello Michael,
I am familiar with what you are talking about, that is a good thing! At this time I am still working on getting the basics put into place; the loader, and a transient of some sort, and a consistent way to return to the loader once the transient is done. Something along the lines of a really simple int20h or dos's 04ch/21h. I still have to write some ISRs for my file handling so that the experimenting is easier, hacking sectors into memory works but its time consuming without any closed system. Two notes ago you made a mention of high memory. What do you suggest I use the HMA for? My loader? or an extra segment? There is about 64k up there, right?
thank you so much!
jon
You cannot move the loader, the need for it to be at 07c00h is hardcoded. Or you could probably do a bootstrap from 07c00h to HMA. As soon as you set the A20 line it will switch on the address Michael told you and the rest of your loader could be programmed to resume there. But the loader is done and working, why screw around with it.
Paul
Hi Paul,
I think you are missing what we were talking about, my "loader" isn't the bootstrap code, it is more like what you would call the kernel. I have chosen to call it the loader because when the system is running it will be the process that loads user processes. I have no desire to try to change anything about the bootstrap process, it gets up, puts my loader in place, and and goes away. Im talking about options for where to put my "loader" as not to take up any valuable space.
jon
Sorry, your terminology confused me. Michael is the one who really knows this stuff, anyway. My knowledge is really too old these days, senile decay and all that rot.
EDIT: I can't help it, I gotta say something. Now that I know what your loader is and what your intentions are. If you move it into the HMA and then load transients into both lower and upper you will play havoc and have to watch you don't trash your loader. I still say leave it where it is, the memory is NOT precious because you still have the same amount free either way. Of course, if you are going to load transients only into lower memory...
Paul
Hi Paul,
I was sure that I hadn't defined my "loader" as the non traditional so I appologize for making this thread somewhat confusing. I looked up some info on the A20 gate and found that there are quite a few ways to deal with it but Im sure that with the equipment I am working on there will be a BIOS routine for testing and/or setting. I will get to that a little later.
I wanted to comment on one thing that you said that gave me a laugh. You asked, "why screw with it", and it occured to me that the question could be an oxymoron for any assembly language programmer. Thats what we do.
jon
... and we love it. You are right. The A20 line is on the processor so it is available in the BIOS if the BIOS allows you to modify it. Now for the fun part. If it does not allow that, then learn how to write a BIOS Flash Update. NOW THAT WOULD BE SCREWING WITH IT!
Paul
Well done Paul,
I am just on the tip of the iceberg here. I have been reading like crazy but I have so much to test yet. I have successfully implemented most of what we have been talking about, with the exception of messing with high memory. I realized that I my code was like a tennisball in a an open room and that just won't do. I want to understand how to read these memory maps so that I can put my code in all of the right places. I tried to put the loader in too low and I had problems that I couldn't justify by my documentation so I came here for some insight. I am trying the low area of 0000:0500h for my loader (kernel) code to see if it runs over anything. If that is successful then I will move it up 1K and implement my little data area for the loader. Then Im set for now. The loader hangs around just over it's data area and I take commands from the user loading their code just above the loader. Maybe it becomes clear why I decided to call it the loader. I just need to decide how to implement my disk handling ISRs. Im getting there, slowly. One brick at a time, right.
jon
Here is how it lays out on paper: (very simple, don't make fun)
0000:0000h to 0000:04FEh BIOS area (somewhat machine specific)
0000:0500h to 0000:0900h My loader's data area
0000:0901h to 0000:1501h My loader code (stays resident)
0000:1502h to 0000:FDFEh Transient program area
0000:FDFFh to 0000:FFFEh Stack (512Bytes)
That is my simple single-segment (16bit) setup. I will venture out from there. Where could I put my ISR's ?
jon
Jon,
There is some A20 address line stuff here:
http://www.masmforum.com/simple/index.php?topic=339.15
The low memory area from absolute address 000 to 3FFh is the interrupt vector table, the lower half of which is pretty much essential, and from 400h to 4FFh is the BIOS data area, essential for BIOS operation, some of it even if you do not call any BIOS functions.
The complete source code (including device drivers, memory manager, etc) for a 32-bit protected mode operating system is available here:
http://www.sensorypublishing.com/mmurtl.html
Wow Michael,
Good stuff. I just got my loader back on line. It didn't like that way that I was addressing. Im still new at this. Thank you for all of your help.
jon
Michael,
Did you have a chance to see my post a few replies ago, the memory map? Is that solid? It worked, but Im no so sure that my stack was in the right place, and was it large enough?
jon
Jon,
I read it, but I didn't comment because it looked reasonable to me, at least for a start. You put the stack where I would have put it, and the size is reasonable for an OS that fits in 64KB. I do think that you will probably run out of space when (or if?) you try to implement any sort of efficient disk/file management, and you will definitely run out when you add the GUI :lol
Actually, the disk and hardware management are next in line after I start experimenting with writing my own ISR's. I appreciate you looking at what I have so far. Remember, this is a just an experimental project, and a very interesting one. On the subject of ISRs, where would I put mine? I have no desire to take out any BIOS code, so where do you put it?
jon
If by ISR's you mean your interrupt handlers, you should be able to put them anywhere in memory, but since you will have to code them and load them they should probably be part of your executable image. I definitely do not understand what you mean my "take out any BIOS code".
Hi Michael,
I didn't mean BIOS code although that is what I wrote. Im not sure what had crossed my mind at that point. I was trying to say that I didn't have any weird ideas about bumping anything existing out of memory in order to seat my handler code. Yes, ISR (Interrupt Service Routine) I read too many books. :)
On the subject of the actual ISR code, I was planning on extending my boot process to 3 stages
accomodating for initialization & placement of interrupt routines without trying my hand at magic with the (512B) boot image.
Here is the sequence: Boot image does it's thing, then loads an executable with the interrupt routines package from disk, jump to it. Routines package loads routines to (wherever memory USA) , modifies the vector table accordingly, then loads the "kernel" image from disk to my low memory spot. After a little verification here and there everything should be solid. Kernel takes over and thats that. The kernel, or "loader", program will not do any start-up specific tasks
With that all in mind where would you suggest putting my interrupt routines? And, of course, if you see any underlying crack-pot aspect to my idea here. Im convinced of the potential size of my interrupt code so Im a little lost on where to put it. This is, afterall, an experimental system but it matters to me that its reasonable well done. Thank you for your insight.
jon
keep it real simple.
It seems that your "loader" is evolving into an OS, so I'll call it that. I think for a small test OS I would keep the load/initialization process as simple as possible:
1. Boot sector loads at normal location.
2. Boot sector moves itself to some out of the way location.
3. Boot sector loads the OS image at its final location.
4. Boot sector jumps to the entry point of the OS image.
5. The OS image initializes itself and starts the command processor.
The OS image would be an exact memory image, with the interrupt handlers, static data, stack, and any necessary buffers included. Assuming you were running in real mode, on recent hardware, booting from a diskette and not using any other drives, at least initially you would have no hardware variations to deal with. With only a few exceptions you could use the BIOS to interface with the hardware. With the aid of a jump table at a fixed memory location your API could be accessed by far call, instead of the clumsy software interrupt mechanism that DOS used. If you were using MASM, this could be done very cleanly with macros.
Simple as possible? - Is there any real need for step 2?
It does chop up your memory space a little, but with a bit of arrangement, you can easily overwrite it once the os image has been loaded.
I suppose it all depends on where the "kernel" image is going to reside, as not to overrun the boot image before it gets control, right?
jon
Yup.
But if you're using seg7000 for data space, you just have to make sure the boot-sector doesn't overwrite itself before it finishes - which shouldn't be too difficult.
If the kernel image will not overlap the boot sector then there is no reason to move it. But if the kernel image will overlap the boot sector, which seems to be the case here, then it would probably be easier to move the boot sector than to rearrange the kernel image to avoid it. Moving the boot sector after it is loaded is the established norm. Even using straightforward, easy to understand code no more than 9 instructions and two words of data would be required.
Michael,
You made mention of not using clumsy interrupt routines and it has stuck with me but I guess I don't understand what you meant. I mean, it occurs to me that since the interrupt mechanism is built into the IA-32 machine they would be considered a fairly bullet-proof system for making far calls to resident procedures to be called from any application, and API system if you will. I would really like, for perspective, if you would elaborate on your feelings towards the software interrupt system.
jon
Hello? Does anyone have any insight on the post just above?
thanks,
jon
Hi Jon, I've been busy recently solving other people's self-inflicted computer problems.
AFAIK the interrupt mechanism was designed primarily as an efficient method of handling hardware interrupts and exceptions. I think the software interrupt mechanism was just a convenient adaptation that provided additional functionality without requiring any significant increase in processor complexity or transistor count. IMO the decision to base the BIOS and PC-DOS APIs on software interrupts was just another one of the mistakes made by IBM on the PC. The convention of passing parameters in the processor registers limits the number of parameters, and because of the small size of the register set, it substantially increases the complexity of the code on both ends of the call. Preserving the flags around an interrupt call makes sense for a hardware interrupt or exception, but it increases the complexity of the code in functions that return error/status information in the flags. Given the fixed size of the interrupt vector table, APIs with more than roughly 224 functions must hang multiple functions off of at least some of the interrupts, using at least one additional register for these interrupts, and increasing the complexity of the handler.
Contrast these things with a far call interface the passes the parameters on the stack and use a jump table at a fixed memory location. The number of parameters that could be passed would be limited only by available stack space. Because the registers would not be used to pass parameters, and because at least some operations could be performed on the parameters without using registers, the pressure on the register set would be greatly reduced. The flags would not be automatically preserved, so a function could return error/status information in the flags directly, instead of having to modify the preserved flags on the stack. The size of the jump table would be limited only by the available memory, so in most cases each function could have its own table address and code.
You would still need to support hardware interrupts and exceptions, so you would still need an interrupt vector table, and because the BIOS is coded to use software interrupts, you would still need to use software interrupts. But for an OS API, I think a far call interface would be a better choice.
Hi Michael,
Excellent. I am very happy to finally hear back from you. Inisght like yours and from the others in this forum makes the impossible possible. I am assuming that the jump table would be at a set-in-stone location that the programmer would use by calculating the start point of the table + some sort of index value (probably double word*routine#) to hook the correct routine. In that, the "routine" at the end of the line would know how many of the stack layers to pop in order to satisfy it's parameter requirements. Finally, on return, the "routine" would push in the same number of arguments to satisfy the expectations of the caller. Am I in the ballpark here? If so, it reminds me of when I used some assembly with quickbasic years ago.
jon
In the most common situation where you were chaining to the previous "handler" at the end of your handler you could do it with a far jump. You would need to chain with a far call only when your handler needed to do further processing after the call returned. Unlike a far jump a far call would disturb the stack, so to pass the original parameters in the call you would need to push copies of the parameters onto the stack at the correct locations, easily done with an invoke statement. In either case, the called handler would be completely unaware of the handler chain, and it would access the parameters in the normal manner.
Why is it that a regular jmp is limited to only 128 bytes, or whatever, forward or backward from current IP?
jon
Unconditional jumps can be short, near, or far. For a short jump the displacement is encoded as a signed byte, with a range of -128 to +127. For a near jump, depending on the memory model and processor, the displacement can be encoded as a signed word, with a range of -32768 to +32767, or a signed dword, with a range of -2147483648 to +2147483647. Conditional jumps can be short, with a range of -128 to +127, or for the more recent processors, near, with a range of -32768 to +32767. MASM generates the shortest jump possible, unless the jump size is explicitly specified.
Michael,
I hope you enjoy answering these questions because you are filling in the gaps for me like you wouldn't believe. I didn't really want to mention it but I don't use the Microsoft Assembler but the content of this forum goes worlds beyond MASM syntax which is where my interest is. Im not really into the high-level pseudo code. My interest is in really plain and simple assembly because I feel that it is much more educational for me. I use NASM, and I really like it. I know that probably isn't very acceptable to many of you but I really like the simplicity AND I like the fact that every procedure that I write it extermely close to the way that the machine actually operates. If I was in a production environment of some sort Im sure that MASM would be a lot easier, but this is an educational process for me. I own a CNC machine shop and I am a class A machinist. I have been a machine programmer for 12 years (G-Code) and I have been a BASIC programmer since the early 80's, nowadays VisualBasic. I write all of our PC software for the shop for process control, communicating with our CNC equipment, and some office control. I have studied quite extensively in serial communications and I am working on procedures, in asm, for communicating with my CNC equipment, just to learn more.
Thank you so much!
excuse, indicate me as I can publish everything what it is related to the BIOS
what?