I am running into an issue getting expected CPU flags register values back from interrupt calls made in FreeDOS to my BIOS on my 286 homebrew build. I am likely overlooking (or not understanding) something simple!
Update: Followers of my YouTube channel had this cracked with an hour of posting! Wow! (thanks, everyone!) Updates are at the bottom of the page.
The Short Version
FreeDOS interrupt calls do not seem to come back with expected values for the CPU flags register. For a simple test, I have added the following procedure to main.c in FreeDOS.
void test_interrupt()
{
iregs regs;
regs.a.x = 0x1122;
regs.b.x = 0x0033;
regs.c.x = 0x4455;
regs.d.x = 0x6677;
regs.di = 0x8899;
regs.si = 0xaabb;
init_call_intr(0x15, ®s);
printf("\nInt test: ");
printf("ax=%04X bx=%04X cx=%04X dx=%04X di=%04X si=%04X flags=%04X\n",regs.a.x,regs.b.x,regs.c.x,regs.d.x,regs.di,regs.si,regs.flags);
}
In my BIOS code, I have an interrupt service routine to read the values, print the values, and modify the values. The print* routines send data out to my debug PC via serial.
isr_int_15h:
; take in test values on all registers, print, modify, return
push ax
mov al, '1'
call print_char_spi
mov al, '5'
call print_char_spi
mov al, ':'
call print_char_spi
pop ax
call debug_print_interrupt_info_sm
call print_char_newline_spi
call debug_print_word_hex ; print ax
mov ax, '-'
call print_char_spi
mov ax, bx
call debug_print_word_hex ; print bx
mov ax, '-'
call print_char_spi
mov ax, cx
call debug_print_word_hex ; print cx
mov ax, '-'
call print_char_spi
mov ax, dx
call debug_print_word_hex ; print dx
mov ax, ' '
call print_char_spi
mov ax, cs
call debug_print_word_hex ; print cs
mov ax, '-'
call print_char_spi
mov ax, ds
call debug_print_word_hex ; print ds
mov ax, '-'
call print_char_spi
mov ax, es
call debug_print_word_hex ; print es
mov ax, ' '
call print_char_spi
mov ax, di
call debug_print_word_hex ; print di
mov ax, '-'
call print_char_spi
mov ax, si
call debug_print_word_hex ; print si
mov ax, ' '
call print_char_spi
lahf
mov al, ah
call print_char_hex_spi ; print flags
call print_char_newline_spi
; *** test values to return ***
mov ah, 0b01000001 ;SF, ZF, AF, PF, and CF flags (bits 7, 6, 4, 2, and 0, respectively)
sahf
pushf
mov ax, 0x1234
mov bx, 0x5678
mov cx, 0x9abc
mov dx, 0xdef0
mov di, 0x2468
mov si, 0x3579
;pushf ;0 ;push flags, just in case anything in .out modified flags
push ax ;1
push es ;2
push 0x00 ;3
pop es ;3
lahf
mov es:[flags_debug], ah
pop es ;2
pop ax ;1
push ax ;1
call print_char_newline_spi
call debug_print_interrupt_info_sm
call print_char_newline_spi
pop ax ;1
popf ;0
nop
nop
nop
nop
nop
nop
iret
My 286 BIOS logs the following. The incoming registers to the interrupt are listed (duplicated on the 2nd and 3rd line) followed by the register values at the end of the interrupt service routine. The format of the incoming (line 2) and outgoing (line4) is ax|bx|cx|dx_cs|ds|es_si|di_flags. This shows that the flags value is 0x43 leaving the interrupt.
The VGA output below is generated during the startup of FreeDOS (test_interrupt() in main.c). Instead of the expected value of 0x43, the flags register is showing 0x02.
The Long Version
286 Source Code
Configuration
BIOS built with NASM 2.16.01 on Windows 11 development PC. BIOS installed on high/low byte flash ICs on 286 system board.
FreeDOS virtual machine running on Windows 11 development PC. Used for building FreeDOS for 286 system.
FreeDOS kernel source 2.43
NASM 2.15.05
Watcom 1.9
FreeDOS bootloader and kernel on CF Card, connected to ISA IDE adapter in 286 system board.
Next Steps
Update NASM in virtual machine to the latest (2.15.05 >> 2.16.01). Complete, no change in behavior.
Dig deeper into "offsets must match the assembly process" comment in FreeDos's hdr/pcb.h file (along with two different setups for offsets).
...
Resolution
I was using iret in my interrupt service handlers, when I should have been using retf 2. When using iret, the flags are pushed when entering the ISR and popped when leaving the ISR (resulting in a loss of flags information). I quickly tested retf 2, and I got the expected result in FreeDOS!
Update: I swapped out the retf 2 approach with a stack frame manipulation. This suggestion came in on the YouTube comments and also in a FreeDOS listserv.
push bp ; at top
mov bp, sp ; at top
;ISR code...
push ax ; update flags in stack frame for proper return
lahf
; bp + 0 = saved bp
; bp + 2 = ip
; bp + 4 = cs
; bp + 6 = fl
mov byte [bp + 6], ah
pop ax
pop bp
iret
Comments