top of page
  • Writer's picturerehsd

Troubleshooting CPU Flags Register Between BIOS and FreeDOS

Updated: May 1, 2023

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; = 0xaabb;
    init_call_intr(0x15, &regs);
    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.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.

	; 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

	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)
	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
	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

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


  • 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).

  • ...


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
		; bp + 0 = saved bp
		; bp + 2 = ip
		; bp + 4 = cs
		; bp + 6 = fl
		mov byte [bp + 6], ah
		pop ax
		pop bp

38 views0 comments

Recent Posts

See All


bottom of page