Page 1 of 1

int modifying flags register

Posted: Tue Dec 29, 2009 10:00 pm
by Firestryke31
I have been bored recently and have decided to try my hand at a simple DOS clone, and have been working on implementing the int 21h functions. Many of them, like the BIOS, modify the FLAGS register to indicate various statuses like "there are no keys available" or "this call failed." My question is, since FLAGS is popped from the stack, how do they return these? Right now I modify the FLAGS value on the stack, but I was hoping there might be a more simple way to do this.

Re: int modifying flags register

Posted: Tue Dec 29, 2009 11:37 pm
by ~
I think that in 16-bit x86 mode, the ordinary software INT instruction pushes the 16-bit EFLAGS, CS and IP onto the stack (in that order).

If you need that a particular ISR modifies the EFLAGS, you could do something like:

Code: Select all

myisr:
 .........
 .........

  ;Extract the return address and the EFLAGS
  ;just as the IRET instruction would
  ;;;
    pop cx   ;Pop 16-bit EIP
    pop bx   ;Pop CS
    pop ax   ;pop 16-bit EFLAGS

 .........
 .........
 .........

  ;Modify the copy of EFLAGS that is in AX
  ;;;
   ........
   ........


  ;Push the original stack contents before IRET (with updated EFLAGS)
  ;;;
    push ax     ;Push 16-bit EFLAGS
    push bx     ;Push CS
    push cx     ;Push 16-bit EIP

iret

Remember that in Real Mode, the INT instruction clears automatically the IF (Interrupt Flag), TF (Trap Flag) and AC (Alignment Check). Once IRET is executed, the EFLAGS are automatically restored in the same way as with the instruction POPF (to pop 16 or 32-bit of flags into the EFLAGS register directly) and you are able to modify those flags before IRET by the method above or by having the value out of the stack and updating it before pushing it for IRET.

Re: int modifying flags register

Posted: Wed Dec 30, 2009 12:37 am
by Combuster
You don't need to pop the entire stack to fix a flag, you can just access the stack's memory, somewhat like this (lifted from my kernel):

Code: Select all

                        JC .setcarry
                        AND dword [ESP+16], ~ EFLAGS_CF
                        JMP .continue
.setcarry:              OR dword [ESP+16], EFLAGS_CF
.continue:

Re: int modifying flags register

Posted: Wed Dec 30, 2009 11:00 am
by Firestryke31
Combuster wrote:You don't need to pop the entire stack to fix a flag, you can just access the stack's memory, somewhat like this (lifted from my kernel):

Code: Select all

                        JC .setcarry
                        AND dword [ESP+16], ~ EFLAGS_CF
                        JMP .continue
.setcarry:              OR dword [ESP+16], EFLAGS_CF
.continue:
This is basically what I am doing right now, so it seems I had the right idea. It's not too hard to do, and I guess I could write a few return stubs that automatically set/unset the flags.

Re: int modifying flags register

Posted: Thu Dec 31, 2009 2:09 am
by qw
Combuster wrote:You don't need to pop the entire stack to fix a flag, you can just access the stack's memory, somewhat like this (lifted from my kernel):

Code: Select all

                        JC .setcarry
                        AND dword [ESP+16], ~ EFLAGS_CF
                        JMP .continue
.setcarry:              OR dword [ESP+16], EFLAGS_CF
.continue:
Maybe this advice is superfluous, but in real mode you must be sure that the top 16 bits of ESP are zero.

Re: int modifying flags register

Posted: Thu Dec 31, 2009 2:23 am
by Combuster
Better idea: don't use code written for 32-bits protected mode in real mode :wink:

Re: int modifying flags register

Posted: Thu Dec 31, 2009 12:08 pm
by Firestryke31
Yeah, I forgot to mention that I'm using SP and not ESP.

Here's the ISR stub I have so far:

Code: Select all

int21h:
	push bp
	mov bp, sp
	push intReturn
	cmp ah, 0x01
	je getKeyWithEcho
	cmp ah, 0x06
	je directIO
	cmp ah, 0x09
	je simpleWriteStr
	cmp ah, 0x25
	je setIntVector
	cmp ah, 0x48
	je malloc
	cmp ah, 0x49
	je free
intReturn:
	mov sp, bp
	pop bp
	iret
Here are the two flag setting functions I have so far:

Code: Select all

paramFlags equ bp+10
retNZ:
	push ax
	mov al, [paramFlags]
	and al, 0xFE
	pop ax
	ret

retZ:
	push ax
	mov al, [paramFlags]
	or al, 1
	pop ax
	ret
This could probably actually be done better, but hey.

Re: int modifying flags register

Posted: Fri Jan 08, 2010 1:21 pm
by ~
Maybe you could use instructions like CLC, STC, etc., to work directly with the real FLAGS in the ISR. Then when you get the state of flags you need, you can:

- Read the first byte of FLAGS from the stack into, say AL register.

- Apply AND mask to AL with the corresponding bits set to 0 to clear the old flags you need. For instance, if you need Carry flag the AND mask should be 11111110b.

- Now that you have "emptied" the old flags, you can use the LAHF instruction to load the real current first 8 bits of FLAGS into AH.

- Apply AND mask to AH with the correspondign bits set to 1 to save only the new flags you need. For instance, if you need Carry flag the AND mask should be 00000001b

- Now apply OR AL,AH

- Store the updated flags in the proper stack position

In a more natural way, I think you could do normal operations and based on them, again do something similar and just store the state of the current FLAGS. For example, maybe you could use something like XOR AX,AX to set the Zero Flag to 1 to indicate Zero, or use RCR/RCL to alter the Carry Flag in interesting ways.

Or you could make a function that does all of that automatically and all you have to do is pass it a bitmask with the bits set to 1 for the flags you want updated in the stack, and all you would need to concentrate in would be to keep the intended flags up to date and then call this function to update the flags in the stack. Something like:

Code: Select all

.....
do something
.....
update_stack_flags(00000001b, stack_flags_ptr_offset);  //only update the CF
But you would also need perfectly at all times where in the stack is your EFLAGS copy before calling such function as specified in the stack_flags_ptr_offset address parameter or attempting to update it "by hand".

The LAHF instruction stores these flags in AH:

Code: Select all

Bits in AH:
      0 -- (CF) Carry Flag
      1 -- (1)  Always set to 1
      2 -- (PF) Parity Flag
      3 -- (0)  Always set to 0
      4 -- (AF) Auxiliary Carry Flag
      5 -- (0)  Always set to 0
      6 -- (ZF) Zero Flag
      7 -- (SF) Sign Flag

The only warning is that the LAHF instruction (0x9F opcode) may or may not be supported in 64-bit mode.