RayeR's homepage/ROMOS Project (english)
: 27The ROMOS is a stand-alone x86 code allows you to load and run your own binary code or 3rd-party code. ROMOS rely on BIOS functions only so it can be executed directly without any operating system. The main purpose of ROMOS is to be placed in a ROM, from where it can load/run other software (e.g. bootmanager, HW diagnostics, special controlling software...) during POST (Power-On Self Test) while your PC is booting up. It can also load DOS-based operating systems (may be other OSes) such as FreeDOS stored in ROM together with ROMOS. This mean that any floppy/harddisk/CD-ROM drive is not needed. It may be very useful in various embedded diskless systems. Or simply as reserve OS for rescue use. Other applications are on you.
The ROMOS can be placed in two ways. One way is to insert it as ISA ROM module into your system BIOS located in FlashROM memory chip on your motherboard. In this case any additional hardware is not needed. But there's limitation due to free space in FlashROM. Other way is to burn ROMOS in separated FlashROM / EPROM and place it on special ISA card. But cheaper solution is to use any of existing network adapter with a socket for BootROM. Then you may need to enable BootROM by vendor specific network adapter configuration utility - it will store your setting to small onboard EEPROM together with your MAC and other settings.
Here's system requirements:
-PC with BIOS stored in FlashROM (EPROM cannot be [easy] modified)
-running MS-DOS 7.0+ (included in Win9X) or FreeDOS operating system
-enough free space in FlashROM (min. 2 kB for ROMOS itself + your code)
(sample ROMOS build with FreeDOS requires 64 kB)
how to make more space in FlashROM see below.
-Award BIOS 4.51/6.00 (I did't tried it with AMI BIOS but newer versions don't support ISA ROM modules)
-your motherboard BIOS file (can be dumped with awdflash.exe)
-cbrom.exe and awdflash.exe programs to insert ROMOS and dump/burn your BIOS.
If you go 2nd way you'll only need:
-ISA card which supports up to 64kB ROM chips
-FlashROM or EPROM burner
If you want to make own ROMOS build you'll additionally need:
-1,44MB floppy disk drive and one diskette (for modifying virtual ROM disk image)
-romchk.exe and zalign.exe for making ROM image
-fdimg.exe for manipulating with disk image
(all needed software tools are included in ZIP file package)
Before installing ROMOS you will need to get your current BIOS image. You can download it from your hardware vendor or dump it via awdflash.exe /pn /sy bios.bin. Place bios.bin in ROMOS directory (where you extracted ZIP package) and also make a backup copy. If you want to modify content of virtual ROM disk insert 1,44MB floppy diskette without bad sectors in drive A: (for B: update paths in batch files) and run putimg.bat (from DOS not Windows). FDIMG utility will transfer romdisk.img to your floppy. Due to floppy specific format (63 kB with only one FAT) MS-DOS 7.0+ or FreeDOS is required to read/write this disk. Then you can add/delete files, transfer OS via sys command or everything else like with normal floppy. Also you can try to boot from the floppy to test it. When the floppy will be successfully tested you can take image file running getimg.bat (backup original romdisk.img before).
Then you may edit romos.asm source. There are few important options:
line 17 uncomment the %define MAKE_PCI_MODULE to insert additional PCIR and $PnP header to make PCI compatible ROM module. Some values in these headers have to be adjusted with bromcfg.exe utility.
line 18 uncomment the %define HOOK_INT19H to allow hook INT 19h.
line 19 defines ROMOS module total size in kB (with included romdisk.img), minimum size is 2 kB.
line 20 defines what drive will ROMOS emulate. If drive exists it will be overrided. If you have one FDD you should choose B: or A: for none.
lines 21-23 define floppy image format. If you use format same as original romdisk.img you needn't to modify it.
line 24 defines the offset of bootdrive byte in bootsector. Don't care if you use DOS - this byte is automatically modified according to ROMDISK_DRIVE defined in line 14.
lines 25-26 define address where will be bootsector copied before running it. BIOS do this normally at 0000:07C00h.
line 27 defines duration of displaying ROMOS boot message in 55ms timer ticks.
line 28 defines a hotkey to activate ROMOS (ScrollLock/NumLock/CapsLock).
line 34 uncomment the %define USE_PCI_REG_PATCH to apply a patch that modifies the specified PCI configuration register in the specified PCI device (executed during every boot), see below in changelog.
After editing romos.asm run rcompile.bat to compile it. It will produce ISA ROM module romos.bin with included romdisk.img, aligned by 512B and patched checksum. This image may be directly burned to EPROM for your ISA board or included to your system BIOS. If you copied your bios image as bios.bin to ROMOS directory just run insromos.bat and CBROM will place it in as ISA ROM module at D000:0000h (otherwise modify batch file for proper address). See CBROM screen output if ROMOS module was successfully inserted. If you get 'not enough space' message see technical details section how to make some space by removing unnecessary BIOS modules.
Finally flash your modified bios.bin via awdflash.exe bios.bin and reboot your machine. If you use ROMOS PCI as replacement of onboard network adapter BOOTROM don't forget to enable BOOTROM option in SETUP and if there is a choice to allow hook INT 19h enable it. After restart and BIOS POST done you should see white 'Press [ScrollLock] to boot ROMOS !' message at the top of your screen. It will be displayed for 1,6 s. If you do nothing BIOS will continue with booting from your drives and will free ROM space (which will be then available for UMB or EMS frame, etc.). If you pressed ScrollLock (you may press it before message displays) ROMOS will print some debug messages, install own INT 13h handler (which allows access to virtual ROM disk), copy bootsector from virtual ROM disk image at specified address and gives control to it. Bootsector then load operating system stored in virtual ROM disk image. Then you will have read-only access to emulated drive assigned with letter as you specified ROMDISK_DRIVE variable.
Here you can download last ROMOS version. It is a free software, you can use and modify it as you want but please don't remove my copyright. Any software based on ROMOS should be also distributed free with included sources. If you will make some serious modification or find a bug please let me know.
romos.asm [35 kB] - ROMOS 1.06 ISA/PCI assembler source code, last updated 7.12.2017
romos.101 [28 kB] - ROMOS 1.01 assembler source code, older version
romos.094 [14 kB] - ROMOS 0.94 assembler source code, older version
romos.001 [7 kB] - ROMOS 0.01 assembler source code, even older version ;-)
romos.bin [64 kB], romospci.bin [64 kB] - ROMOS 1.05 ISA and PCI compiled module (to be placed at D000:0000h, emulating drive B:) including FreeDOS kernel 2035, Micro Manager 1.0 eng. (now about 2 kB smaller after recompression), Dynaload for dynamic drivers loading, SeeMem for exploring memory and few other small tools. The PCI variant has to be configured with bromcfg.exe (part of the romos.zip package) before using it.
romos16k.bin [16 kB] - ROMOS 1.02 PCI compiled module (to be placed at D000 - DC00:0000h, configured for PCI network adapter Realtek 8029, emulating drive A:) including DF-DOS 0.04b - www.dftech.cwc.net/osdev, memory viewer and some small demos. Here is a video showing DF-DOS running some old games.
rjdos.zip [12 kB] - RJDOS 0.01a by Richard L. James is a small replacement of command.com (9746 bytes) that can be placed in romdisk.
If you want to see how it works just now click below to see the animation showing ROMOS booting on my PC. It was reanimated by Borg Number One from my screenshots. It should be safe for your computer ;-)
22.2.2004 Version 0.95 contains major changes. The main ROMOS code is now as INT 19h handler. In 1st phase during POST, when BIOS is doing ROM-scan, it only displays choice if you want to run ROMOS. If positive answer is got then ROMOS install new INT 13h handler and redirect INT 19h vector to the main code. Then ROMOS gives control back to BIOS which correctly finish POST. After full initialization BIOS call INT 19h and ROMOS will be run again (if INT 19h vector was redirected) and will load operating system from virtual ROM disk.
4.3.2004 was updated FreeDOS kernel to version 2033 (included in romos.bin and romos.zip), which was compiled by Luchezar Georgiev using BC for 386. This made kernel little bit smaller - 42518 B, so you have about 1 kB more space for your files in ROM disk image.
22.9.2004 New version 0.97 contains a lot of changes and code optimalizations. The most important improvement is change of storage of few needed permanent data from ROMOS segment to unused place in interrupt vector table. This allows ROMOS to run from true read-only memory (such as EPROM on ISA board without shadowing) and under BOCHS PC emulator where ROMOS can be comfortable and without risks tuned. Now ROMOS is not fixed to given ROM segment but practically we have only few choices in PC: D000h or E000h (only for older PCs). Also structure of INT 13h routine was changed for easier adding of new subfunctions. Subfunction 03 (write sector) was added and return proper errorcode. For faster orientation in code I placed a brief state diagram at end of source file. Later I updated kernel to version 2035 in virtual ROM disk image.
23.10.2004 Version 1.00 contains further code optimalisations and INT 13h handler was extended to support function 15h. Then I dealed with FreeDOS kernel 2035 sources and compilation. Due to removing some unnecessary things (fdconfig.sys - only config.sys, break, numlock, echo, switches, country, menus), optimalization of compiler and packer I reached the size only 39390 B for kernel with FAT32 support. So then I can fit it together with USB stack driver for mass-storage class devices (flashdisk, etc.), from where can I load other files. It would be possible to load Linux from flashdisk via loadlin.exen - but I cannot test it. ROMDISK image with USB driver is stored in complette zip package as romdusb.img together with standard romdisk.img.
18.3.2006 New version 1.02 brings support of Plug&Play PCI ROM. Most of new motherboards without ISA slots lost support of ISA ROM modules and this was the reason why ROMOS didn't work there. Now it's possible to compile ROMOS as PCI ROM module (PCI ROM also can be used as ISA ROM but not vice versa). PCI ROM headers contains specific vendor ID, device ID and device class which must match some real PCI device present in the system (otherwise ROM will not run). Some BIOSes require that assigned PCI device have a real ROM or socket for ROM (e.g. network adapter). In my system it works only assigned to network adapter (I also tried IDs of PIIX4, PCI to PCI bridge, USB controller... - without success). For this setting you can use bromcfg.exe utility from Arne which is included in zip package.
15.7.2007 There are only minor changes in version 1.03. Now you can use definition HOOK_INT19H to turn on/off INT 19h hook. When it's commented out then ROMOS will start immediatelly after query and hit activation key (keep in mind that POST may not be finished yet but this feature might be usefull). Also some programs on virtual ROMDISK was replaced. I successfully tested ROMOS on AMI BIOS of Asus P5LD2 motherboard and I added essential tool AMI MMTool 3.12 in zip package for placing ROMOS module into BIOS image.
22.6.2009 Version 1.04 contains only minor improvement in source - an optimization of routine for printing hexadecimal numbers and related routines. This saved 17 B of machine code.
13.5.2012 Version 1.05 contains further code optimizations that saved 16 B of machine code. I also changed the method of calling chained old ISR of INT 13h. Instead of modified FAR JMP I call it via another INT. This saved one entry in IVT for INT 84h where I placed the FAR JMP opcode directly before INT 85h vector. So now only the INT 85h IVT entry is used to store old INT 13h vector (this entry number can be easily cahnged in source code).
7.12.2017 Into version 1.06 I have implemented a special function to modify the specified PCI configuration register in the specified PCI device that can be executed during every boot. It's intended to fix a wrongly configured or unconfigured PCI device that was missed by the BIOS and we need to initialize it by our own way just before OS will boot. I wrote this code specially dedicated as a workaround to misconfigured PCI2PCI bridge on my motherboard Gigabyte GA-P67-DS3-B3 where the VGA 16-bit I/O Space Decoding Enable bit was not set properly in Bridge Control Register but I believe it may help to someone else with similar kind of problem with a PCI device. The patch function is configured by following definitions of constants: PPCI_BUS, PPCI_DEV, PPCI_FUNC defines the PCI address of the device (you can get it by lspci or SMB /pci etc.), PPCI_REG defines the address of 32-bit PCI configuration register (a Byte offset in range 0 - 252 that must be 4-Byte aligned) in the specified device and PPCI_REG_ORMSK defines the mask of bits that should be set to 1 in the 32-bit PCI configuration register. If you need to set some bits to 0 then use the PPCI_REG_ANDMSK and uncomment related code (the line AND EAX,PPCI_REG_ANDMSK) lower in the source code.
Technical details (BIOS hacking, ROM modules, ASM...)I'll expect that you have some knowledge about PC hardware, BIOS (I will implicitly talking about AWARD BIOS), DOS and assembly language. If you're windows clicking dummie you should go to play with Mr. Paperclip von da M$ Mord... :)
O.K. let's sum some facts about the BIOS (Basic Input Output System). This is the basic software (or we should say firmware) stored in nonvolatile memory on your motherboard providing basic functions such as disk/screen/keyboard/ports/timer/... access. Operating system may or may not use this functions. DOS based systems always use it. BIOS take control every time you turn-on (or cold reboot) your PC at it's entrypoint address F000:FFFEh. It makes POST (Power-On Self Test) - various hardware initializations and tests (initialization chipset registers, CPU speed (PLL), testing RAM, seeking for IDE drives, seeking for other ROMs and lot of other things). BIOS also include SETUP program allows you to set various HW options and store them to CMOS memory backuped with a battery.
In the past the BIOS was stored in one or two EPROM chips. Usually one 27C512 (64 kB) in 486 systems or two 27C256 (32 kB - odd and even part [due to 16bit addressing]) in 286 and 386 systems EPROM chips was used. Note that EPROM chips can be erased only by UV light irradiation - high energy photons (for this purpose has chip a silica glass window sealed with some label, there is also cheap EPROM version without glass window, yes that in epoxide package, which can be erased only by X-ray radiation but I don't recommend you to do this at home :) so BIOS upgrade was very difficult if you don't have an EPROM eraser and burner. It usually contains raw binary BIOS code which resides at physical address range F0000 - FFFFFh.
Pentium (also some modern 486 and 5x86) systems brought new important feature - BIOS has been moved to Flash EPROM. It allows user to electrically erase and reprogram the BIOS code inside motherboard without removing the chip. First FlashROM chips, such as 28F001, required extra 12 V for erasing and programming. Newer chips, such as 29F020, requires only single 5V supply. Now there's are also LV versions for 3,3 V usually used on graphics cards. Very old motherboards support only 12V FlashROM chips, newer has jumper for selecting 5 / 12 V and the newest supports only 5V or 3,3V chips. FlashROM chip capacity has been growing from 128 kB -> 256 kB -> 512 kB... On some newest motherboards PLCC SMD package is used instead classic DIL package. In extreme cases the BIOS chip is soldered directly on PCB so you can't simply remove it and reprogram in a programmer if something screws up. I think this is a bad trend. Yes, it's cheaper for manufacturers and enable some HW rescue service firms to get fucking easy money. To prevent unwanted BIOS modification there's usually a 'programming enable' or 'write protect' jumper close to FlashROM chip so enable it now.
Also the BIOS code structure has changed. Instead raw binary form it is divided in two parts - 8 kB uncompressed bootblock and LHA compressed module(s). Bootblock takes control first and check'n'decompress the LHA packed modules at specified RAM addresses and then if everything is OK gives control to main module. If some module is corrupted and checksum doesn't match bootblock will beep to warn you. But don't panic, if bootblock is working it allows you to reflash BIOS using DOS bootable diskette with working BIOS file, flashing tool and properly made autoexec.bat file. You usually will see nothing on the screen because bootblock don't initialize PCI and AGP then watch FDD LED activity. If you use an old ISA VGA you should see BIOS messages normally. Just insert the diskette and reboot. Bootblock boots the DOS from the diskette and autoexec.bat calls awdflash.exe to reflash your bios file and you should be saved :).
If all else fails you will need to pull out your FlashROM chip from motherboard and program it in a FlashROM programmer. Or you can do the hotflash if you have your backup FlashROM chip. I also using this method because chip programmers are expansive. But there is a major or minor risk that you blow your chip or motherboard. It depends on how strong do you believe Murphy's laws ;-). I have made about 50 hotflashes and blow one FlashROM chip. It is a 98% effectivity - great result, isn't it? But new chip costs me about 200CZK. The hotflash is simple in the fact. To make a backup FlashROM first you will need an empty FlashROM chip same capacity as your current chip. It may be from other manufacturer. Turn off your PC and pull out the chip from the socket and reinsert it softly. Remove unnecessary cards and cables to make easy access to the chip. Turn-on your PC and boot up plain DOS. Now gently pull out the chip (if you reinserted it before, as I suggested, you will not need brute force now) and insert empty one. System will not crash because BIOS code is shadowed in RAM. Flash your working BIOS file into empty chip and reboot the computer. Now you have a backup chip. If something heavily fucks up you simply swap the chips and do this again.
Now something about BIOS modules. You can list/add/remove them by cbrom.exe program. You can also extract all modules in one time with awarddec.exe. For AMI BIOSes there are similar tools called amibcp.exe for DOS & Windows and mmtool.exe for Windows. Here's listing of my BIOS file:
No. Item-Name Original-Size Compressed-Size Original-File-Name ================================================================================ 0. System BIOS 20000h(128.00K) 14E93h(83.64K) original.tmp 1. XGROUP CODE 082F0h(32.73K) 05877h(22.12K) awardext.rom 2. ACPI table 02214h(8.52K) 00E51h(3.58K) ACPITBL.BIN 3. VRS ROM 02280h(8.62K) 014BBh(5.18K) ANTI_VIR.BIN 4. EPA LOGO 0279Ch(9.90K) 0132Ah(4.79K) SKULL4.EPA 5. CPU micro code 0A800h(42.00K) 060D8h(24.21K) cpucode.bin 6. ISA ROM[A] 10000h(64.00K) 0F437h(61.05K) romos.bin Total compress code space = 34E93h(211.64K) Total compressed code size = 3324Fh(204.58K) Remain compress code space = 01C44h(7.07K) ** Micro Code Information ** Update ID CPUID | Update ID CPUID | Update ID CPUID | Update ID CPUID ------------------+--------------------+--------------------+------------------- SLOT1 13 0630 | SLOT1 20 0632 | SLOT1 36 0633 | SLOT1 37 0634 SLOT1 40 0650 | SLOT1 40 0651 | MOBILE 2D 0652 | SLOT1 10 0653 SLOT1 0A 0660 | PPGA 03 0665 | MOBILE 07 0670 | SLOT1 03 0671 SLOT1 10 0672 | SLOT1 0E 0673 | SLOT1 14 0680 | PPGA 14 0681 PPGA 14 0683 | PPGA 08 0686 | PPGA 01 068A | SLOT1 07 06B0 SLOT1 1D 06B1 |
The main module is original.tmp (at line 0.). It contains resident code, SETUP program and POST routines. It's always 128 kB long. The first 64 kB of original.tmp is loaded temporarily at E000:0000 - E000:FFFFh. After POST this memory range is freed. The second 64 kB of original.tmp is permanently loaded at F000:0000 - F000:FFFFh and become write protected after POST. CBROM doesn't allow you to extract/insert original.tmp module. It can be done with awarddec.exe (extraction only) or modbin.exe. Modifying original.tmp is not easy because it is protected by two checksums but MODBIN can calculate them. Also inserting modified file back into BIOS file is not easy and I don't have reliable way how to do it. But I succeeded this job using awdhack.exe in my case.
As BIOS becoming more complex and larger all the code doesn't fit into original.tmp so it has been splited to XGROUP and YGROUP CODE modules. My BIOS is quite old so it contains only XGROUP code listed as awardext.rom. This module is usually decompressed at address 4100:0000h. This code may be easily modified but after inserting your modified module remember to load and update it with MODBIN to update checksums.
Next we can see CPU micro code module as cpucode.bin. It's an update of microcode that can modify behavior ( fix bugs) of some complex CPU instruction that consists of many microinstructions e.g. fsin. Simple instructions are probably hardwired for speed reason. Cpucode.bin consists of many blocks, each for a specific CPUID that MB supports that are min. 2 kB long with 1 kB granularity, simply glued together (except ASUS BIOSes). Every microcode block have a header:
dd 000000001h ; Header Version dd 000000013h ; BIOS Update ID dd 008271996h ; Date (BCD) dd 000000630h ; CPUID of target processor dd 0f316fc3bh ; Checksum (zero-align to DWord sum) dd 000000001h ; Loader Version dd 000000010h ; CPU Flags dd 000000000h ; Size of pure microcode data (for >2048 B only) dd 000000000h ; Total size (for >2048 B only) dd 000000000h ; reserved dd 000000000h ; reserved dd 000000000h ; reserved
Then follows SHA1/SHA2 encrypted microcode data signed by RSA 2048. Of course there was many attempts to hack it but nobody was successful yet. Intel keeps this informations top secret. Here you can read an interesting summary of microcode analysis. It was mentioned that there exists 2 various microcode formats for older and newer CPUs (changed in new Core 2 era). It's interesting how microcode update size can change during years. E.g. the 1st microcode update for CPUID 206A7h dated 28.12.2010 has 8192 B and the latest one dated 12.6.2013 has 10240 B. So intel made patch RAM with some spare space for future updates but how big it really is? For older AMD CPUs AMD K7 - K10 that didn't have encrypted microcode yet it was reverse engineered the CPU architecture internals and even it was demonstrated creating and loading of custom microprograms into the CPU.
You need a microcode block for your currently installed CPUID only. Also it is possible to remove it completely because modern operating systems like Windows/Linux loads necessary microcode update from a file during boot. UPDATE: it's no longer possible because a lot of modern CPUs have several bugs that prevent them normal boot without microcode update. So here's a way how to make some free space in FlashROM. After inserting modified cpucode.bin via CBROM you don't need to update any checksums. I wrote two tools to manipulate with microcode files. CPU Microcode Loader report used microcode ID and allows you to load microcode block file into CPU (this take effect until you turn-off or cold reboot your PC). Microcode is uploaded to CPU by following sequence: linear address to microcode data loaded from a file (including header) is stored to EAX, 00000079h constant is stored to ECX, 0 constant is stored to EDX and finally a privileged instruction WRMSR is executed. CPU checks the microcode data and if it finds any mismatch it will reject the update. Some BIOSes may support extended services of INT 15h (function D042h) which allows you to write/read microcode data directly to/from FlashROM. See more details in: Pentium Pro Processor BIOS Writer’s Guide. The second tool Binary CPU Microcode file info allows you to view microcode file information and split cpucode.bin file to particular files named by corresponding CPUID for each block. Codemake.bat will join your selected files to one which you can then use as new cpucode.bin.
ACPI table module is here for ACPI support. I don't have more information. VRS ROM module is needed for BIOS antivirus feature which I have always disabled. I think that it can be removed if you don't use it. EPA LOGO is an Energy Star logo displayed in the right top corner of the screen during POST. In older BIOSes it has only 2 colors (EPA 1.0) in newer 16 colors (EPA 2.0 with "AWBM" signature at the beginning of file). You can replace it by your own logo. I wrote BMP -> EPA convertor based on knowledge which I got when hacking EPA 2.0 format (I didn't find any format description that time so I did it myself). In AMI BIOSes, we can meet e.g. on newer Asus P4/C2D motherboards, are used 16 color logos in intel GRFX format. I wrote also my own BMP -> GRFX convertor. You can download it here /PROGRAMMING. OEM LOGO module is a fullscreen bitmap displayed during POST instead normal text output. It can be seen on some compaq, intel,... motherboards. You can free a lot of FlashROM space by removing it. There's also displayed small blue 'Award medal' logo in the left top corner. It is not stored is any extra module but probably in original.tmp but I don't know where.
There can be also other modules such as VGA videobios for integrated graphics adapters (you can use this feature when you bad flash your graphics card BIOS), network adapter bootrom, IDE/SCSI raid controller BIOS and more... I also found similar project called ATA Security eXtension BIOS, which extends main BIOS by harddisk security module. The most of modern IDE drives supports ATA commands for setting user and master password which can effectively secure the content of the drive. But BIOS programmers utilize this advanced features very rare yet (except notebooks).
If you want to execute your own code during POST you can make easily an ISA ROM module and insert it in your BIOS file. You have to choose address which will not collide with other ROM or system parts. Here you can see usual memory map of 1st megabyte in PC:
Here is usually free space at D0000 - DFFFFh (64 kB) which can be used for our own ISA ROM module. During POST the BIOS is doing a ROM-scan - seeking for external adaptor's ROMs. ISA ROM must begin with first two bytes 55 AAh followed by 3dr byte telling ROM size in 512B blocks. But minimum ISA ROM module length is 2 kB. BIOS checks addresses with 2kB increment so ROM address must be a multiple of 2048 (800h). ISA ROM module has checksum. If we sum up all bytes (including 55 AA header) into one byte variable (we don't care overflow) it must be zero. If checksum doesn't match BIOS skip this ROM and continue seeking at next 2 kB else ISA ROM module code will be executed from 4th byte. Here we can place our code compiled with NASM as a flat binary. Code must end with RETF instruction. ROM file should be aligned with zeros to corresponding size. Look at the ROM template:
DB 55 ; header 1st byte DB AA ; header 2nd byte DB xx ; size in 512B blocks JMP xxxx ; jump to our code DB 0 ; reserved byte for checksum xxxx ; our code . . . RETF ; return from ROM code
Be sure you are storing and restoring all CPU registers. The stack is usually set too small so I set it at 4000:FFFEh. Remember if you make a mistake in your code and PC freeze you cannot disable ISA ROM module execution and you will have to reflash your BIOS in programmer or using hotflash! I also make some mistakes because it's impossible to debug the code. So I first write a part of code (label BEGIN) which give me a choice to continue ROM code execution. This is done by testing ScrollLock key flag at address 0000:0417h bit 4. If ScrollLock is not pressed ROMOS restore all registers and stack and return control to BIOS. Otherwise executing continues. If there is a bug in this part (after label BOOT) I simply reboot PC and fix it.
ROMOS providing some basic routines: DELAY waits for AH*55ms, GOTOXY sets cursor at DL,DH screen position, WHEREXY read cursor position to DL,DH, WRITE prints zero terminated string at CS:SI by given attribute in BL on current cursor position, WCRLF prints CR,LF to set cursor at new line, WHEXW prints a hexa word in DX, WHEXB prints a hexa byte in AL, WREGS prints F, CS, SS, ES, DS, DI, SI, BP, SP registers in hexa at one line, SET_KBD_FAST set up 30 CPS keyboard repeat rate and 250 ms delay after first key, INSTALL_NEW_INT13 set up new interrupt vector INT 13h to new routine NEW_INT13 and save old INT 13h vector. Main program is between labels BOOT and EXIT. NEW_INT13 handler supports only function 02 - read sectors. But this is enough to be able to boot and read-only access the virtual ROM disk which is placed behind label IMG_BEGIN (after ROMOS code). NEW_INT13 just catch every INT 13h call, checks if given drive belong to virtual ROM disk and function is 02. Then it calculates linear memory address from given CHS disk parameters and copy required sectors (512B blocks) from virtual disk image (RAM) to given buffer at ES:BX. Also * character is displayed in the right top corner of the screen to have a visual feedback that the INT 13h call was served successfully. If other drive or other function is caught it is passed to old INT 13h handler (if drive match but function not then ! character is displayed). There will not be problem to program read-write access, may be in future ROMOS version.
If we exit ROM code with RETF without clearing the 55 AA header then the address space stay occupied with unused code (which would be otherwise used by HMA, EMS frame,... to keep more conventional memory for DOS). This is reason why ROMOS normally write DW 0 over header. But you may leave it (and keep code in ROM address space) and hook INT 18h or INT 19h by ROMOS to catch this calls to enter ROMOS after boot. If you want to use this feature simply uncomment the %define HOOK_INT18_BY_ROMOS line.
And how the virtual ROM disk image is made? I wanted to use maximum of 64 kB ROM space for virtual disk. Normal 1,44MB diskette is formatted like this: 80 cylinders, 2 heads, 18 sectors per track and 512B per sector. Simply CHS = 80 / 2 / 18. If we multiply all this numbers we got 1474560 bytes. I was playing with reducing C, H parameters to get optimal disk size and found this: CHS = 7 / 1 / 18 (126 sectors) this mean 64512 bytes. One 1 kB is reserved for the ROMOS code. For effective utilization of the disk I use 1 sector per cluster and 16 root entries. From total 126 sectors is one sector occupied by bootsector second and third by two FAT copies and 4th one by root directory. So there remains 62464 bytes (122 sectors) for user files. To gain this space little bit I removed redundant FAT copy via DISKEDITOR and edited bootsector's values. So then I got 62976 bytes (123 sectors) for user files. But this disk format can be read only under newer MS-DOS than 6.22 (use DOS of Win9X or FreeDOS). Remember that if a file is only one byte long it occupy whole sector.
I hope that I described it understandable. I could write more details but it would be too long. And the ideas would lost in too large amount of words. I hope that it helps for people interested in this things, for beginners probably not. But don't be worry if you don't understand it just now. I have been gathering this information for some years from various Internet sources. Thanks to Google search engine, Darmawan Mappatutu Salihun - Award BIOS Reverse Engineering, Jan Steunebrink, Daemorhedron, Sunil Shrestha, Luchezar Georgiev, Eric Auer, Jakub Chalupník, FreeDOS community, Miroslav Němeček (MicroManager), Datalight, Rebels Haven BIOS modding forum.
Recently I was reading very interesting article describing that a modern PC contains some auxiliary MCU called EC (Embedded Controller) beside the main CPU which resides inside the chipset on mainboard (northbridge - MCH, later integrated into the main CPU). Intel use 32-bit ARC architecture processor inside P35 chipset. AMD use some 51 clone or LatticeMico32. The EC is running even in S3 sleep mode. This is part of intel VPro technology for remote PC administration (BTW EC can also run a small webserver). The level on which the firmware of EC is running is classified as ring -3 (ring -2 is SMI handler of system BIOS, ring -1 is hypervisor used for running virtualized OSes and ring 0 is well known OS kernel). Firmware is physically stored in SPI FlashROM together with system BIOS. You can see more info about AMD EC FW analysis on this video of my friend Ruik that he presented on CCC 2014. EC has interesting function that it can freely access host CPU RAM and reset host CPU at any time regardless on running OS. Every such incredible BigBrother-like function can be abused. More info about it you can read in this presentation: Introducing Ring -3 Rootkits.
FAQ, underline notesQ: Why doesn't ROMOS work under PC emulator BOCHS?
A: It's caused by BOCHS (let's say its BIOS) that doesn't support ROM shadowing feature (copying ROM content to RAM). Then ROM area, where ROMOS store original INT 13h, INT 19h vector and SS:SP during start, is accesible for reading only. When restoring SS:SP or calling old interrupt vectors it use wrong address and fatal error occur. It's not problem to store this important data to RAM area ( 00000 - 9FFFFh) but I don't know what safe address should I choose to be procected against owerwriting by operating system, drivers or applications loaded after ROMOS.
Newly since version 0.97 storing of these data has been changed (now using free space in interrupt vector table) and write access to ROM segment is not required now. So ROMOS works now in BOCHS emulator.
Q: Is possible to enlarge ROM disk size to more than 62 kB?
A: Yes, it's possible. I can [EDIT:] cannot get additional 32 kB by quite easy way by splitting ROM disk image to two parts and using empty ROM hole at B0000 - B7FFFh. I plan to implement this in future version of ROMOS. Further ROM disk enlarging would be possible by this way: ROMOS would load ROM disk image (splitted on a few of ROM modules-due to max. 64 kB / module size limitation) somewhere above 1 MB to high address space during start and then it would paging just needed sectors to frame in low address space under 1 MB. But it would require programming in protected mode and I don't have any experiences with it under assembler. There's a question if it would payoff because we are still limited by relative small free space in FlashROM. It's preferable to use CompactFlash card with IDE adapter for more exacting applications.
Q: Can be address range B0000 - B7FFFh used or not?
A: Unfortunately I found out that can't. When I loaded 4kB dummy ROM (contained only RETF instruction) at address B000:0000h (I tried B400h too) my PC froze and I had to do hotflash recovery. So usable address range is D0000 - DFFFFh (64 kB) only, nothing more.