• Please review our updated Terms and Rules here

"Fun with BIOS" Programming Thread

Do you happen to have an updated version of your ROM I could play with?

Attached is the version in the video, if that's what you meant by 'updated'. The code isn't (currently) under version control, and I don't feel comfortable recompiling it at this moment, since I made some minor changes.

This version is also, of course, unfinished... writing the line disassembler is probably the longest, though I have an idea of how to do that (pity it requires a 512-byte jump table- one for each opcode).

View attachment monitor.zip
 
Last edited:
EDIT: If you read the post I had here, ignore it! Sorry. I realized I was testing that in a version of the emulator from early 2011 that had known problems. It's not the case in the current, it just calls function 1h then 0h and sits there.

It calls func 0h immediately after 1h without a key even being pressed though!
 
EDIT: If you read the post I had here, ignore it! Sorry. I realized I was testing that in a version of the emulator from early 2011 that had known problems. It's not the case in the current, it just calls function 1h then 0h and sits there.

It calls func 0h immediately after 1h without a key even being pressed though!

I did not read the post before you edited it, so this post seems out of context to me, sorry :p. To what are you referring?
 
Here's what it used to say:

I did a little "debugging" (okay, just dumping int 16h calls to stdout) of it in Fake86, and what happens when I press a key for the first time is it calls these int 16h functions in this order: 2h, 1Ch, Ah, 8h, 6h, 4h, 2h, 0h. Some of these aren't standard BIOS functions according to RBIL. 1Ch and 8h aren't in the list, 6h is for some TSR called AAKEYS, and 4h is for the Tandy 2000. Maybe this will help?

The second time I press a key at the prompt, it simply seems to keep calling int 16h function 2h repeatedly.

If it matters, I use this BIOS in the emu: http://www.phatcode.net/downloads.php?id=101

But now it just makes me look stupid. :)
 
EDIT: If you read the post I had here, ignore it! Sorry. I realized I was testing that in a version of the emulator from early 2011 that had known problems. It's not the case in the current, it just calls function 1h then 0h and sits there.

It calls func 0h immediately after 1h without a key even being pressed though!

The debugger deliberately triggers int3 just before returning. This is to test that the revectoring was successful, but "waiting for a key" is just to help me test my int 0x09/0x16 handlers prior to boot.

Below is my wait_for_key subroutine:
Code:
;Returns: AL- ASCII character, AH- BIOS Scancode
wait_for_key proc near
	xor ax, ax
	mov ah, 0x01
keep_waiting:
	int 0x16 ;See if keystroke is available.
	jz keep_waiting
	xor ax, ax
	;dec ah ;ah => 0x00, which is the next function we need.
	int 0x16 ;Remove key from buffer.
	ret
wait_for_key endp

The subroutine should "block" until a key has arrived. When ZF is set, we are supposed to keep waiting. So the check is failing on an original PC, but not in Bochs... hmmm...
 
Well then this just doesn't make a lot of sense to me, it looks like it's up to the BIOS code. What in the would could cause this?
 
Well then this just doesn't make a lot of sense to me, it looks like it's up to the BIOS code. What in the would could cause this?

I'll download Fake86, organize my code and create a git repository. I've been putting it off (the repo part) mainly because I have to learn how to do it all over again every. single. time...
 
I'll download Fake86, organize my code and create a git repository. I've been putting it off (the repo part) mainly because I have to learn how to do it all over again every. single. time...

It doesn't have a real debugging interface, I just kinda hacked in some code to dump the interrupt calls and recompiled. ;)

Have a look at www.emu8086.com - this will let you single step and examine everything closely... and I hate git too.

EDIT: I ran it emu8086, and it works there. There's no real BIOS in emu8086 though, so any calls to one are high-level emulated. So it works in that, Bochs, but not in Fake86 or real machines... hmm. Interesting. What does your int 9h handler look like? I don't believe emu8086 even uses int 9h on keypresses. It seems to just directly put key input into the "BIOS" buffer. My guess at this point is that you may be doing something incorrectly in that handler.
 
Last edited:
It doesn't have a real debugging interface, I just kinda hacked in some code to dump the interrupt calls and recompiled. ;)

Have a look at www.emu8086.com - this will let you single step and examine everything closely... and I hate git too.

EDIT: I ran it emu8086, and it works there. There's no real BIOS in emu8086 though, so any calls to one are high-level emulated. So it works in that, Bochs, but not in Fake86 or real machines... hmm. Interesting. What does your int 9h handler look like? I don't believe emu8086 even uses int 9h on keypresses. It seems to just directly put key input into the "BIOS" buffer. My guess at this point is that you may be doing something incorrectly in that handler.

How'd you add my monitor expansion ROM to Fake86, btw :p? I don't see a command line option to do it...
 
How'd you add my monitor expansion ROM to Fake86, btw :p? I don't see a command line option to do it...

There isn't one. I really should add that. You'll have to recompile.

Find this line in main.c:
Code:
		loadrom (0xF6000UL, PATH_DATAFILES "rombasic.bin", 0);

...and put this line above it:
Code:
		loadrom (0xF4000UL, PATH_DATAFILES "monitor.bin", 0);


I could just give you my binary if you like. At least, if you're on Windows.

EDIT: As I mentioned though, it wouldn't be very useful for serious debugging. It couldn't hurt as another test platform, though.
 
Last edited:
There isn't one. I really should add that. You'll have to recompile.

Find this line in main.c:
Code:
		loadrom (0xF6000UL, PATH_DATAFILES "rombasic.bin", 0);

...and put this line above it:
Code:
		loadrom (0xF4000UL, PATH_DATAFILES "monitor.bin", 0);


I could just give you my binary if you like. At least, if you're on Windows.

I already built it myself using MinGW. BIOS boots, but after memory test, the emulator closes without error, even when I specify a floppy disk at the command line. Any ideas?
I set PATH_DATAFILES to null.

I just want a 8088-PC emulator that allows one to add their own ROMs easily.
 
Last edited:
I've never tested with MinGW. It works in gcc just fine, but there's probably something in the makefile that's screwing it up on Windows, it was meant for Linux. It works great in VS2010. Hang on a bit though, I'm adding that feature for the command line option. I've been meaning to anyway. I'll give you a fresh binary when done. :)
 
Here you go: http://rubbermallet.org/fake86-0.13.9.2-win32.zip

This should do it: fake86 -oprom F4000 monitor.bin

Sorry, I went to sleep for a bit. But... you are awesome, thanks! Now I don't have to burn an EPROM every time I want to test on 8088-era hardware. And yes, the monitor still halts the machine :p, but since you said it's never returning from int 0x16, subfunction 0x00 (correct or not?), I know at least where the problem is.
 
No prob! And yes, that's where it seems to hang up on. What is it that you're doing when you receive an int 9h?
 
No prob! And yes, that's where it seems to hang up on. What is it that you're doing when you receive an int 9h?

This:
Code:
int9_handler proc far
		;We need to prepare for the possibility that we will return to
		;the debugger!
		pushf
		push cs
		add sp, -0x02 ;We can't use any other registers now!
				;The original SP can be calculated later.
		push bp
		mov bp, sp
		;mov ss:[0x02 + bp], offset cs:[main_loop]- Crashes spectacularly
		mov ss:[0x02 + bp], offset main_loop
		
		;Not sure which registers are used.
		push ax
		push cx
		push dx
		push bx
		push si
		push di
		push ds
		push es
		
		int 0x12 ;Needs to be the modified ISR
		inc ax
		mov cl, 6 ;Multiply by 64 to get segment-equivalent (1024/16)
		shl ax, cl
		mov es, ax ;Data area is now prepared
		
		
		pushf ;Simulate interrupt
		call es:[old_int9_handler] ;Absolute indirect
		
		
		
		;Has the debugger been defeated?
		;If so, restore the interrupts.
		
		
		;Is key combination valid?
		
		;If so, are we in main_loop already?
		
		;if not call main_loop. It is the ma
		
		;otherwise restore program state, regardless of whether we are
		;in debugger or not.
		
		;push ax
		;push si		
		;push ds
		mov ax, cs
		mov ds, ax
		
		;Test message
		mov si, offset key_message
		call print_str
		
		
		assume ds:DGROUP
		xor ax, ax
		mov ah, 0x02
		int 0x16
		cmp al, 0x0C ;CTRL+ALT+ESCAPE pressed?
		
		;pop si
		pop es
		pop ds
		pop di
		pop si
		pop bx
		pop dx
		pop cx
		pop ax
		
		mov sp, bp
		pop bp
		
		;The POPped registers did not affect the comparison
		jnz exit_int
		iret ;Otherwise, use IRET to jump into the main loop!
exit_int:
		add sp, 0x06 ;Skip going into the main debug loop
		iret
	int9_handler endp

I posted my int 0x09 handler a few pages ago, but can't remember if anything at all changed :p. In any case, I now know that my int 0x09 is breaking on 8088-hardware, by removing the int 0x03 invocation- any keypress crashes the machine. My guess is that the jump to the old handler is crashing and burning.

Some context.
The int 0x09 handler can invoke the debugger if a special keypress is detected (akin to some other ROM monitors), and so it modifies the return address in preparation for calling the debugger's main loop. The code is position-independent, hence why CS is pushed. And yes, the comment doesn't match the actual keypress :p... waiting till I actually fix the routine to update the comments.

The debugger hooks int 0x12 to return 1kB less of memory than actually exists... the debugger then allocates the top kB of memory for itself. The modified int 0x12 invocation plus 1 tells the debugger the start of its own allocated memory area, which solves the problem of having to store a pointer to the start of the memory area. The old interrupt vector for the keyboard handler* is stored in this memory area. On Bochs, this is working fine... on real hardware/your emulator, not so much.

I'm still not sure why the wait_for_key loop is failing though- it should be blocking until a key arrives, not exiting the loop and crashing even if no key is present.

*This address is the same for most PC-compatible BIOSes, but I wanted to target semi-compatible machines as well. There are ways around this of course, by hardcoding the addresses, if the end user is so inclined to compile their own version for a semi-compatible machine.
 
Last edited:
pushf ;Simulate interrupt
call es:[old_int9_handler] ;Absolute indirect[/CODE]

There's the problem. That's a near call. You need to push the original handler's segment after the flags as well for the IRET to work.

EDIT: At least, that's how emu8086 is assembling it. Opcode FF (GRP5) sub-op 2, and it only pushes one word to the stack.
 
Last edited:
Okay, I see what you're saying. What I'm trying to do is perform a far call with an absolute address (0xEA), when the address is not known ahead of time. But yes, my assembler is also assembling that as 0xFF with a segment override 0x26 (ES), according to my listings.

Code:
0071    9C                        pushf       
0072    26 FF 1E 08 00            call        dword ptr es:L$7
 
Okay, I see what you're saying. What I'm trying to do is perform a far call with an absolute address (0xEA), when the address is not known ahead of time. But yes, my assembler is also assembling that as 0xFF with a segment override 0x26 (ES), according to my listings.

Code:
0071    9C                        pushf       
0072    26 FF 1E 08 00            call        dword ptr es:L$7

Oh, your assembler is doing FF 1E which is a far absolute indirect. So that's correct. Mine was giving an FF 16.
 
Back
Top