Your premise, of a 1:1 correspondence is false. It assumes that all possible operations can be represented by only one opcode. That is not true.
Not all assemblers will necessarily produce exactly the same code from a given program. Indeed, one assembler may fail to assemble a program that another is happy with. This could not happen if there truely was a 1:1 correspondence between assembly code and machine code.
How to make a real stand-alone system?
- DavidCooper
- Member
- Posts: 1150
- Joined: Wed Oct 27, 2010 4:53 pm
- Location: Scotland
Re: How to make a real stand-alone system?
Hi Brendan,
You're a living database of knowledge which stuns just about everyone who encounters you here. You're also extraordinarily generous with your time in helping others, sharing your expertise with them at enormous cost (I have no doubt) to the development of your own OS. You ought to step back for a moment though ask yourself why you've just spent so much of your valuable time attempting to rubbish the code of someone you shouldn't (if you believed yourself) even regard as a serious programmer. What is it within you that drives you to launch into an attack of this kind every time you spot a chance to stick the knife in?
My code does the job asked of it, and there's lots of scope for improving it as you've realised: I particularly have to thank you for pointing out the direction flag issue as that may actually matter on some machines. This was never a competition entry - I cobbled it together in a couple of hours for fun when the temptation to meet the remit of this thread finally became too great. I built it to suit the way I program, but also to suit the way I used to program in the early days when I used a more reduced subset of the instruction set. I wanted to build something that I might have been able to build way back at the start and from which I could have done everything else in much the same way that I did the first time round. I think that's what it does.
I never put this forward as an example of code for others to worship or copy - it's code that does the job sufficiently well that I don't care about "improving" it any more than I care about sanding down and putting French polish on the internal wooden frame of my sofa.
ECX has to be cleared to 0 in case the upper 16 bits aren't 0. When the LBA location of the VBR is added to the total being built in ECX, it's vital that the upper 16 bits are 0.
You're a living database of knowledge which stuns just about everyone who encounters you here. You're also extraordinarily generous with your time in helping others, sharing your expertise with them at enormous cost (I have no doubt) to the development of your own OS. You ought to step back for a moment though ask yourself why you've just spent so much of your valuable time attempting to rubbish the code of someone you shouldn't (if you believed yourself) even regard as a serious programmer. What is it within you that drives you to launch into an attack of this kind every time you spot a chance to stick the knife in?
My code does the job asked of it, and there's lots of scope for improving it as you've realised: I particularly have to thank you for pointing out the direction flag issue as that may actually matter on some machines. This was never a competition entry - I cobbled it together in a couple of hours for fun when the temptation to meet the remit of this thread finally became too great. I built it to suit the way I program, but also to suit the way I used to program in the early days when I used a more reduced subset of the instruction set. I wanted to build something that I might have been able to build way back at the start and from which I could have done everything else in much the same way that I did the first time round. I think that's what it does.
Says who? It may not be normal, but running with the interrupts disabled is perfectly viable. I have never bothered to use the BIOS for reading the keyboard and couldn't be bothered looking up the way to do so for BwtSecOS either, but anyone who wants to use the BIOS to get keyboard input can simply enable the interrupts. [I've just wasted fifteen minutes trying to find out how you read key input via the BIOS in Ralf Brown's interrupt list and elsewhere without success - I have better things to do with my time than go on hunting for things I don't even need.]Interrupts shouldn't be disabled by default;
I've got a 486 gathering dust which appeared to have its keyboard ports and some FDC ports damaged by relentless machine-gun polling. Everyone here initially suggested that it was impossible to do such damage, but then someone said it could happen if it had been upgraded from a 386. I checked and discovered that it was indeed an upgraded 386. (I rescued it when its owner was throwing it out because I was looking for a cheap, expendible machine to build my OS on, so to me it was just a 486.) Now, it may just have been chance, but there is no harm in playing it safe to avoid the risk of someone else damaging another ancient machine in the same way.Wrong. I was specifically thinking of old Cyrix CPUs when I wrote this (Cyrix CPUs caused Linux developers similar problems with their old/broken tiny delays).DavidCooper wrote:Delays are not idiotic on the rare type of machine these delays are designed to protect,Brendan wrote:ready for a broken/idiotic delay...Broken/idiotic delay (may take 0 cycles on some CPUs)...ready for a broken/idiotic delay (contents of CL is undefined)
I made three errors in that reply, two by not looking up the code in question: one was a typing error (65535 was intended rather than 15535), another was forgetting that I then set CH to 1 before running the loop, and the third was not noticing the case where the second delay loop runs without the first delay loop having done so first, meaning that CL could be anything. Putting the second delay loop there is certainly overkill, but to play it safe again I thought it worth putting some kind of delay between reading the status and data ports. I couldn't care less what value is in CL when using CX as a count in this instance - all I wanted to do was create a little extra delay which is at worst completely harmless. I could have saved some space there instead, but there was no pressure to do so as I was filling in a fixed size hole below the load_or_save routine which I didn't want to move. These were decisions made on the fly and never looked back at - there wasn't the need or the time.Wrong. If the branch at 0x7C46 ("jnz 0x7C4E ;jump if new input") is taken, then nothing sets CL to anything before the loop at 0x7C50.DavidCooper wrote:and CL=0 after the loops run out, so the loop instruction decs it to 15535 before comparing with zero on the kind of machine this is intended to protect.
A missing tool is not a broken tool - if you want to build from BwtSecOS there are going to have a lot of missing tools, so far calls save a lot of trouble. I didn't do it to save me the trouble of calculating two jump distances - my indexing system can calculate them even for real mode. If I was to go on developing an OS from BwtSecOS, I would have soon wanted to be able to type in bytes digit by digit, so that would involve writing code that would do far calls to the keyboard reading routine. Anyone else using it might prefer to use the BIOS to get the input for them instead, and they are free to do just that, but I wrote it primarily for me, imagining how I might have grown my existing OS out of it.Ignorant fool would be using broken tools that fail to relieve the programmer from the hassle of calculating jump distances.
It was written once, debugged, modified later to add LBA capability, worked first go, and I never found the time to to back to tinker with it - it is reasonably compact, easy for me to follow, and it works just fine: I have much better things to do with my time than craft to some unnecessary level of perfection every bit of code I write along the way.It's not more compact, it's just pointlessly obfusticated.
I have a little tool designed for removing a block from a bicycle wheel. It is supposed to be used in conjunction with a huge wrench which I don't own. I could go to the trouble and expence of acquiring one so that I can use it once every other year, or maybe I could even have a go at manufacturing one myself, but I can get by without it: I simply walk out into the street and use the grating of a drain instead. What actually matters is getting the job done on the few occasions it needs doing.Another "my code is bad because my tools are bad" excuse.DavidCooper wrote:My indexing system is designed to work with 32-bit code only so it doesn't automatically adjust the few sections of real-mode code that I use - it would be overkill to write an indexing system for a mode I almost never use, so using a one extra (harmless) jump in a bootsector is no great deal. Yes I could save one byte of code, and make my work a lot harder.should be "jmp near 0x7cfe", or just shift the code at 0x7CFE here to avoid the need for pointless JMPs
It's compact, it isn't a mess and it works. What you're looking at is a routine which used to do just CHS and which had LBA capability added into it later on. I could have written completely separate code for an LBA version or I could have written a new version of the whole routine from scratch, but I stuck with using what was already there as a base because it already worked, just building the extra capability into it. That is how to maximise progress.Bullshit. It's a bloated mess that could/should be much more compact, starting with hard-coding the DAP (instead of generating it from hard-coded values) and having a function that converts LBA (from DAP) to CHS for the "old int 0x13" functions.You made a fuss about me using one extra byte a moment ago and a billionth of a second of extra boot time, but now you want me to use two extra bytes and waste a billionth of a second here instead! The BIOS functions for CHS and LBA are very different, but fully compatible in terms of what you load the registers with before calling the BIOS. I've saved a lot of space by writing the code the way I have.
I never put this forward as an example of code for others to worship or copy - it's code that does the job sufficiently well that I don't care about "improving" it any more than I care about sanding down and putting French polish on the internal wooden frame of my sofa.
You could stick it in a floppy-disk sized partition at the top of a flash drive - if someone was to use BwtSecOS to build a program which they later wanted to demonstrate on real hardware without having to drag around a USB floppy disk drive with them, that can be done.Also note that they aren't "BIOS functions for CHS and LBA" - one is for small disks (e.g. floppy) and the other is for large disks. The code I can see has no reason to use "int 0x13 extensions" at all (even on large disks it wouldn't use "cylinder > 1023").
Well, not just compactness, but it was also designed to be easy to read in machine code form during debugging. In a case like this, once it's tested and works, there's simply no pressure to rework it to make it faster or more compact as the potential gains aren't worth spending any time on, unless you're building something for a competition.Bullshit.Have you ever compared the number of machine code bytes used by those instructions? My code is designed for compactness rather than pointless bloat.
What you've actually done is required the data to become bloated instead, and you've also neglected to modify SI to line up on the next group of data - that'll cost you four bytes just to do that. Your compaction efforts with your superior code amount to requiring an extra 15 bytes. I'm not clear as to where you want to mov the track no. to either without costing more than the two bytes required for a push and a pop....but it makes it obvious that the layout of the data needs to be improved. With the layout of the data optimised you get this:
Notice that this is 7 bytes less than yours; but doesn't do the "push ax" - instead the "pop ax" would be replaced with a "mov". This would add another few bytes to code elsewhere, making my version 5 bytes less than your slower and harder to read/maintain version.Code: Select all
000000A7 8B5C02 mov bx,[si+0x2] ;fetch addrMSB from data 000000AA 891E147C mov [0x7c14],bx ;post into DAP 000000AE 8B5404 mov dx,[si+0x4] ;fetch head no and drive-number 000000B1 8B4C06 mov cx,[si+0x6] ;fetch track no and start_sec no 000000B4 8A4408 mov al,[si+0x8] ;fetch no_of_secs which stays in AL 000000B7 A2127C mov [0x7c12],al ;post into DAP 000000BA
Do you still not understand the point that if someone was to use BwtSecOS to build something more capable, they'd be starting out with no tools other than those in the bootsector?Another "my code is bad because my tools are bad" excuse.DavidCooper wrote:xchg ax.si uses a single instruction byte. This was done for a very good reason, because when you're learning to program in machine code it takes a while to get up to speed with some of the harder-to-form machine code instructions: anyone trying to use BwtSecOS is likely to prefer doing some indirect loads to begin with, and here I made the machine code more readable for them without losing a byte.
You don't really think, half the time: again you're completely failing to imagine yourself trying to build on top of BwtSecOS where all your own fancy tools (and all of my fancy tools) are out of reach. The address of the byte under the cursor isn't even displayed yet at this primitive stage, so if someone wants to load/save something else from/to disk or needs to read the keyboard, they can do so very easily with far calls without having to count out distances. If they'd rather use the BIOS for keyboard input, they can simply enable the interrupts and use the BIOS.Another "my code is bad because my tools are bad" excuse.DavidCooper wrote:Again this is to make it easier to call the procedure without having to work out a sodding jump distance - it's worth losing two bytes for that.yes, return with a pointless "RETF" that should be a plain "RET"
BwtSecOS is just a proof of concept - a warning not to use it on <386 would suffice.Um, no - you should've tested if the CPU is 80386 or later and warned the user (or avoided using 32-bit registers, which would've been easier given that you ignore the highest 16-bits of the multiplication anyway).DavidCooper wrote:Another point to you - I should have warned people.ecx = 0 (WARNING: assuming 80386 or later)
ECX has to be cleared to 0 in case the upper 16 bits aren't 0. When the LBA location of the VBR is added to the total being built in ECX, it's vital that the upper 16 bits are 0.
That's actually addressing the wrong point - you've just jumped back to something covered above where you ended up with bulkier code.Compare this:DavidCooper wrote:It's function is exactly the same as xor ah,ah here - there's no performance cost, and there may even be a gain.ax = 0 (should be deleted)To this:Code: Select all
00007C87 33C0 xor ax,ax ;ax = 0 (should be deleted) 00007C89 8A4403 mov al,[si+0x3] ;al = something (should be deleted) 00007C8C 03C8 add cx,ax ;cx = something (should be "movzx cx,byte [si+0x3]")
Your version is 7 bytes and 3 instructions, while mine is only 4 bytes and 1 instruction and is faster and easier to read/maintain.Code: Select all
000000A7 0FB64C03 movzx cx,[si+0x3]
If you read what I said carefully, the bit that say's "you've saved processor time" means what it says - it was clearly awarding you a point.Wrong. You've increased processor time because "mov dl,0x24" creates a false dependency on the old value in DX (which has a false dependency on the value of AX, which depends on a load from memory). Basically you've got a large dependency chain that prevents the CPU's "out-of-order execution" and kills performance.DavidCooper wrote:Same end result, but you've saved processor time.mov dx,ax ;dx = something (never used, should be deleted)
mov dl,0x24 ;dx = 0x0024 (should be "mov dx,0x0024")
You're right - I misunderstood the bit you wrote here:-DavidCooper wrote:Those instructions will cost you an extra byte each time.mov ah,al ;ah = something (should probably be "shl ax,8")
mov al,0x0 ;ax = something << 8 (should probably be "shl ax,8")4 bytes vs. 3 bytes.Code: Select all
00000000 88C4 mov ah,al 00000002 B000 mov al,0x0 00000004 C1E008 shl ax,0x8
I didn't realise the two rotates you wanted are actually the same one.00007C72 8AE0 mov ah,al ;ah = highest 8-bits of segment (should probably be "shl ax,8")
00007C74 B000 mov al,0x0 ;ax = highest 8-bits of segment << 8 (should probably be "shl ax,8")
Totally unimportant with this kind of code.Machine code programmer's trick to hamper the CPU's "register renaming" and make code slow.
No, that isn't your conclusion - that was your starting point. You just want to tell me yet again that you think my programming system is rubbish. In reality, my way of programming works very well and it certainly suits my needs better than your tools would meet my needs. Your way of programming is to me a nightmare. My way of programming is to you a nightmare.My conclusion is that you've deluded yourself into believing that your "tools" and abilities are superior, when in practice the "direct machine programming" approach is so fundamentally flawed that it's impossible for anyone to create adequate code (for any definition of "adequate" - size, speed, maintainability, etc) using this approach.
So, you now say "it's just a little challenge for fun", but you haven't exactly treated it as such, have you! As for my "joke" project, a new version will soon be available to look at, so you might want to save up some venom to spit then. Buy a large barrel and start collecting it now.Basically, it's just a little challenge for fun. DavidCooper never really understood these limitations and is still seriously attempting to use machine code. He's not doing it as a fun little challenge. This is sad, as he seems intelligent enough to achieve something better than a joke project.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c
MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
Re: How to make a real stand-alone system?
Hi,
If BwtSecOS was designed specifically for fun/challenge then that would've been fine. However, it wasn't - BwtSecOS was derived from a serious project, and the same development techniques (and even some of the same code) are being used for that serious project. If I wanted to "stick the knife in", I'd encourage you to continue your bizarre/hazardous "machine code" ways (and laugh at you behind your back as you suffer the consequences of actions I failed to warn about).
The main reason for this is that disabling IRQs for longer than you have to screws things up in ways that whatever is in control (BIOS) isn't designed to handle - it can mean lost timer IRQs, failure to turn off the floppy motor, problems handling keyboard/mouse properly, etc.
You've "partially usurped" control over part of the hardware (the keyboard, at least if it's not USB without PS/2 emulation), and this masks some of the problems that disabling IRQs cause; but because you've only "partially usurped" control it's likely there's still plenty of potential problems with other devices plus additional problems caused by the "partial usurping" itself. For example, if the user presses keys fast enough while your code is busy updating the screen then key presses are likely to be lost; and because the BIOS's keyboard IRQ handler is still active (when IRQs are briefly enabled) it's still reading from IO port 0x60 and putting keypresses (that you've already "stolen") into its keyboard buffer (e.g. I wouldn't be surprised if someone using this utility starts getting "keyboard buffer full" beeps from the BIOS after a short while).
I don't understand how someone could look into "int 0x16" for 15 minutes and still not understand how to use the "get keypress" BIOS function.
Cheers,
Brendan
It's sincere concern for others. If you saw a child about to urinate on an electric fence, or take up smoking crack as a hobby, or attempting to launch a fire cracker from their anus; then it would be both cruel and unethical if you don't at least warn them against such hazardous acts. Warning people against the use of machine code (for more than just a challenge/joke) is the same.DavidCooper wrote:You're a living database of knowledge which stuns just about everyone who encounters you here. You're also extraordinarily generous with your time in helping others, sharing your expertise with them at enormous cost (I have no doubt) to the development of your own OS. You ought to step back for a moment though ask yourself why you've just spent so much of your valuable time attempting to rubbish the code of someone you shouldn't (if you believed yourself) even regard as a serious programmer. What is it within you that drives you to launch into an attack of this kind every time you spot a chance to stick the knife in?
If BwtSecOS was designed specifically for fun/challenge then that would've been fine. However, it wasn't - BwtSecOS was derived from a serious project, and the same development techniques (and even some of the same code) are being used for that serious project. If I wanted to "stick the knife in", I'd encourage you to continue your bizarre/hazardous "machine code" ways (and laugh at you behind your back as you suffer the consequences of actions I failed to warn about).
Says everyone. The normal advice is "don't disable IRQs for longer than you have to". This only applies when something is in control of the machine (e.g. the BIOS, or an OS), and doesn't apply when something isn't in control of the machine yet (e.g. before firmware starts its drivers, or after an OS has discarded firmware and before the OS has setup its drivers). It does apply to BwtSecOS where the BIOS remains in control of the machine the entire time.DavidCooper wrote:Says who?Interrupts shouldn't be disabled by default;
The main reason for this is that disabling IRQs for longer than you have to screws things up in ways that whatever is in control (BIOS) isn't designed to handle - it can mean lost timer IRQs, failure to turn off the floppy motor, problems handling keyboard/mouse properly, etc.
You've "partially usurped" control over part of the hardware (the keyboard, at least if it's not USB without PS/2 emulation), and this masks some of the problems that disabling IRQs cause; but because you've only "partially usurped" control it's likely there's still plenty of potential problems with other devices plus additional problems caused by the "partial usurping" itself. For example, if the user presses keys fast enough while your code is busy updating the screen then key presses are likely to be lost; and because the BIOS's keyboard IRQ handler is still active (when IRQs are briefly enabled) it's still reading from IO port 0x60 and putting keypresses (that you've already "stolen") into its keyboard buffer (e.g. I wouldn't be surprised if someone using this utility starts getting "keyboard buffer full" beeps from the BIOS after a short while).
I don't understand how someone could look into "int 0x16" for 15 minutes and still not understand how to use the "get keypress" BIOS function.
Sigh. Do you understand that some CPUs will optimise "0xE2, 0xFE loop $" so that it takes no time at all (effectively doing "mov cx,0" instead, regardless of what CX contained beforehand)? How is a broken "0 cycles" delay supposed to prevent this damage?DavidCooper wrote:I've got a 486 gathering dust which appeared to have its keyboard ports and some FDC ports damaged by relentless machine-gun polling. Everyone here initially suggested that it was impossible to do such damage, but then someone said it could happen if it had been upgraded from a 386. I checked and discovered that it was indeed an upgraded 386. (I rescued it when its owner was throwing it out because I was looking for a cheap, expendible machine to build my OS on, so to me it was just a 486.) Now, it may just have been chance, but there is no harm in playing it safe to avoid the risk of someone else damaging another ancient machine in the same way.Wrong. I was specifically thinking of old Cyrix CPUs when I wrote this (Cyrix CPUs caused Linux developers similar problems with their old/broken tiny delays).DavidCooper wrote:Delays are not idiotic on the rare type of machine these delays are designed to protect,
No. Telling you that your programming system is rubbish isn't my objective. Convincing you that your programming system is "rubbish" (has severe shortcomings) is my main goal. Warning others not to follow your footsteps is an (almost entirely unnecessary) secondary goal. It's sincere concern for others. In the same way, I would attempt to convince you (or anyone else) that urinating on an electric fence is a bad idea.DavidCooper wrote:No, that isn't your conclusion - that was your starting point. You just want to tell me yet again that you think my programming system is rubbish. In reality, my way of programming works very well and it certainly suits my needs better than your tools would meet my needs. Your way of programming is to me a nightmare. My way of programming is to you a nightmare.My conclusion is that you've deluded yourself into believing that your "tools" and abilities are superior, when in practice the "direct machine programming" approach is so fundamentally flawed that it's impossible for anyone to create adequate code (for any definition of "adequate" - size, speed, maintainability, etc) using this approach.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: How to make a real stand-alone system?
Brendan, it may not be a good idea to write directly in machine code, but I bet you that DavidCooper knows quite a bit more than the inner workings of a computer than you as a result.
Programming is 80% Math, 20% Grammar, and 10% Creativity <--- Do not make fun of my joke!
If you're new, check this out.
If you're new, check this out.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: How to make a real stand-alone system?
I'll take that bet - after all I saw proof of the opposite earlier this thread ... *adds a few more invisible tokens to his account*m12 wrote:Brendan, it may not be a good idea to write directly in machine code, but I bet you that DavidCooper knows quite a bit more than the inner workings of a computer than you as a result.
Worst of it all, this thread is derailed by a ton of assumptions of people that seem to lack certain basics in computer science to tell the difference between showoff acts and actual knowledge. What would you prefer: visiting a flashbang wizard that sells the same rabbit out of his top hat every day, or a professional breeder that can get you a fresh rabbit ragout to eat every day? You won't find the latter wearing fancy robes.