Back when I was a kid, in the days before Google, you tried to figure out how things worked just by poking around your computer. For example, I noticed that professional programs were always .com/.exe files, but I only knew how to edit simple batch scripts. So of course, the "logical" thing to try was to write a .bat and save it with a different extension and see what happened.
.exe files created this way wouldn't launch, but a .com created this way would instantly reboot the computer. Even more weirdly, this only worked for the specific command that happened to enter in my first attempt -- it might have been something like "cd games". If I changed anything at all, it no longer worked. Only years later I realized that the characters I typed were directly executed as machine code, and that I must have stumbled upon a set of instructions that caused a CPU exception!
For several years, this weird file was the only way I knew to programmatically reboot a computer, so I renamed it "reset.com" and kept it around. I think it stopped working only in the Windows XP era when DOS programs were finally sandboxed to some extent.
>So of course, the "logical" thing to try was to write a .bat and save it with a different extension and see what happened.
Ha ha, same here. Also the next thing I did was to open exe files with notepad and conclude that this is what real code looked like and that the programmers that wrote the programs had written it that way.
When a little bit later I learned that programs were written in languages like C++, I got a book about C++ while on vacation and I read a bit in that book and I wrote down the Hello World program on paper and wrote variations of it and a couple of programs that would ask questions and say something. When I got back from vacation I sat down in front of my computer, typed in hello world and used a compiler called Bloodshed C++ which the book had recommended and compiled the program and double-clicked the exe file, only to have it open a window and immediately close. I had failed to understand that I needed to run the program via cmd.exe because of course Hello World exits immediately when it's printed the text.
This discouraged me a bit so I stayed away from programming languages for several years after that but at least I kept learning HTML and a bit about reverse engineering and a couple of other things and eventually I got back to programming when I started high school and we were using Texas Instruments TI-84 Plus calculators and we were given a couple of programs written in TI-BASIC which we were told to store on our calculators. Once I'd written some programs of my own in TI-BASIC I then read a book about PHP and wrote some PHP.
Such was the beginning of my experience with programming.
> I had failed to understand that I needed to run the program via cmd.exe because of course Hello World exits immediately when it's printed the text.
And till this day, people writing command-line programs in an IDE like Visual Studio stumble on this fact, and the workaround is still the same: adding an stdin read or something like system("PAUSE") at the end, pausing the execution until the user hits Enter.
'cd ' diassembles into arpl [si+0x20],sp which seems like privileged instruction but actually isn't, on the other hand it is not even valid instruction in real/virtual mode and thus causes illegal instruction exception.
It is interesting to note that code for the warmest of warm reboots on PC is 0xcd 0x19 (int 0x19). This jumps back into the point in BIOS code when it starts looking for the operating system to boot. Needles to say, this works reliably only in pure real mode and confuses NT's NTLDR in really interesting way (the thing was obviously originally designed to be run from DOS and contains some left over logic from that time).
The best part was sneaking that instruction sequence somewhere in the middle of an innocuously looking C program (pointer arithmetic in DOS was truly a power to behold; and string literals are mutable in real mode!), and then submitting it to the teacher for grading.
It produces error message along the lines of "you computer does not have enough free conventional memory to run windows NT". It is interesting that the message is actually translated into language of the NT install, says that you need 639kB of free memory for windows NT and reports whatever was the amount of free conventional memory in DOS before the "reboot" (which makes sense given the fact that at that point the original DOS is in memory in probably completely working state).
Interesting about this is that there probably is not any other way to get the same message, because NTLDR actually is not valid as anything that DOS can execute (IIRC it is not MZ image and is too large to be loaded as COM).
It is designed to run in real mode. The NT bootblock probably even loads it as if it was .COM, but it is too large for DOS's .COM loader. The actual binary is concatenation of two files, there is realmode part (on XP SP3 it is 0x4cd0 bytes long, most of which are either zeros or text strings, including the aforementioned error message) and rest of the file is 32 bit PE binary, complete with "This program cannot be run in DOS mode" stub. The PE binary part is obviously somewhat special because it has to be able to switch to real or vm86 mode in order to call BIOS and run NTDETECT.COM, which is valid DOS .COM (from quick look at the code it even looks like it takes command line arguments) except the obvious fact that it does not do any DOS calls.
Edit:
The message in the XP's NTLDR says that it needs 512kB of conventional memory (I think that I've seen the 639kB number on NT4 or 2000) and there is also message along the lines "Windows NT needs 7MB of expanded memory", which is also somewhat interesting given that BugCheck 0x0000007D INSTALL_MORE_MEMORY is defined as "more than 5MB" and the fact that when you boot NT normally on computer with 4MB RAM you get the 0x0000007D BSOD, not the message from NTLDR.
>a .com created this way would instantly reboot the computer. Even more weirdly, this only worked for the specific command that happened to enter in my first attempt
Interesting that you did that by chance, since the probability was somewhat low. but possible, of course.
I used to assemble a tiny .COM program on new computers sometimes, using just DEBUG.COM (the built-in rudimentary debugger that came with DOS), using something like the following (the dash is the DEBUG prompt):
DEBUG reboot.com
-A
JMP F000:FFF0
-.
-W
-Q
Meaning of the above: A to enter Assembly mode - to enter assembly language instructions, the JMP jumps to that address (Intel x86 segment:offset format), which was the location of the instruction in ROM that would reboot the PC, the dot to signal end of assembly input, W to write the code to reboot.com, Q to quit DEBUG.
This!! And as I recall, if we replace -W with -G (for 'go') and hit Enter, it would immediately reboot. Then, there also used to be a flag (boolean) that could be used to effect either a warm or a cold boot. Nostalgic. :-)
In the context of the current thread, your handle is cool too!! As I recall, the code for up to INT 19H (or 20?) was stored in the ROM BIOS and for INT 21H and up, in the hidden system files (io.sys/msdos.sys) which would get loaded just before command.com at boot.
If I remember correctly, the lowest DOS interrupt was 20h. But I don't know if all interrupts below that were actually used. The highest that I remember is 1Ch for the timer interrupt (for which the stock BIOS interrupt handler just returns immediately, but which was commonly overriden by TSRs to simulate multitasking).
Also there were two groups of interrupts, IIRC: DOS interrupts and BIOS interrupts. BIOS ones were lower level and I guess some of the DOS interrupts used some BIOS ones. I remember a friend using BIOS interrupts to write a program to format a floppy disk and we sat there watching the drive make sounds as it did it.
CPU interrupts - handlers for CPU errors like division by zero (0h) or overflow (4h).
IRQ interrupts - handlers for hardware events; e.g. hardware timer (8h) or keyboard (9h).
BIOS interrupts - for BIOS-implemented syscalls; e.g. video output (10h) or sector-oriented disk I/O (13h). Also callbacks to be invoked by BIOS, like the aforementioned 1Ch for configurable timer.
DOS interrupts - for DOS-implemented syscalls; mostly this was 21h with actual syscall ID in the register. This is where you got things like file-oriented I/O, and some basic memory management.
And then there was "everything else", meaning things installed by third party TSRs and drivers. For example, 33h for the Microsoft mouse driver (which all other mouse drivers promptly copied, so it became the de facto standard mouse API in DOS), or 3Eh for Borland's software floating point emulator.
Of these, only the first two are actually "special" as far as hardware is concerned - the rest is just convention.
Interesting, didn't know that there were 4 groups. I do remember INT 21H and setting a value in a register to specify what syscall you want run. Turbo Pascal had some support for assembly and even register handling, IIRC, so you could write such low-level programs in it. Also had absolute memory addressing which could be used for anything where you need to manipulate fixed, known memory addresses, such as video RAM.
> using BIOS interrupts to write a program to format a floppy disk and we sat there watching the drive make sounds...
There used to be a virus/trojan that would execute the floppy drive head re-calibration interrupt (BIOS) in an infinite loop to cause the head alignment to go out of whack, rendering the drive defunct.
I'd imagine that wouldn't have worked on NT or 2000 either. You just likely jumped from the 9x series of OSs to XP which was the first one to offer "good" memory protection. Full explanation for how you could do this here[0].
It really was the wild west back when 9x was popular. I'm genuinely surprised they ran as well as they did. NT was a vast improvement.
While NT was vast improvement, the 3.1/9x was pretty significant feat of engineering and in comparison NT is "just another OS".
In my opinion the lacking memory protection was not about the architecture itself but about the fact that it was meant to be single-user and thus there were no permissions. IIRC from Win32 code you had to jump though some hoops to be able to access whatever memory/hardware you wanted (ie. you could not overwrite random non-owned memory by mistake), the fact that you could do almost whatever you wanted in vm86 code was essentially an feature and that Win16 code could access any other Win16 process data was weird design decision that is also shared by all NT versions up to 2000 (where it is configurable).
> the fact that you could do almost whatever you wanted in vm86 code was essentially an feature and that Win16 code could access any other Win16 process data was weird design decision that is also shared by all NT versions up to 2000 (where it is configurable).
Hey, I actually liked that! Back when Internet was young and the OS was single-user, I felt like I had more control over my own machine than I do now. Being able to mess with other processes' memory was a feature for me :).
.exe files created this way wouldn't launch, but a .com created this way would instantly reboot the computer. Even more weirdly, this only worked for the specific command that happened to enter in my first attempt -- it might have been something like "cd games". If I changed anything at all, it no longer worked. Only years later I realized that the characters I typed were directly executed as machine code, and that I must have stumbled upon a set of instructions that caused a CPU exception!
For several years, this weird file was the only way I knew to programmatically reboot a computer, so I renamed it "reset.com" and kept it around. I think it stopped working only in the Windows XP era when DOS programs were finally sandboxed to some extent.