17

I have two snippets of 8086 assembly code, both of which are supposed to do the same thing: make the mouse appear on the screen.

Show_Mouse:
    push ax
    mov ax,0        ;Reset Mouse
    int 33h
        
    mov ax,1        ;Display Mouse
    int 33h
    pop ax
    ret
Show_Mouse:
    push ax
    mov ax,0        ;Reset Mouse
    int 33h
        
    inc ax          ;Display Mouse
    int 33h
    pop ax
    ret

The first one does what it's supposed to do when called as a subroutine, it makes the mouse appear on screen. However, the second one, whose only change is inc ax, does not!

I'm familiar with the concept of memory-mapped ports on other systems like the NES, which have to be written to in a very specific way or the write won't "take" (e.g. the command INC $2005 will not increase the scroll offset by 1, you actually have to use STA to write to it), but this scenario in MS-DOS is quite peculiar, since it's a software interrupt that takes the value in AX as an argument. As such, it shouldn't matter that I used INC to get AX to equal 1. Right?

0

3 Answers 3

46

When calling the mouse driver interrupt with AX = 0, it returns 0xFFFF in AX if a mouse driver is installed.

So if it is installed, the code with INC AX will increment AX back to 0 and then it will just reset the mouse driver a second time.

It is very typical that interfaces that use software interrupts give you back a status code in AL or AX, so this is no exception.

30

Calling an interrupt service is more like invoking a system call than it is like writing to a memory-mapped register.

That is, when you invoke a software interrupt, there is no guarantee that the register values will be the same that they have been before the interrupt call. In fact, most of the time, they will not be, unless the interrupt service routine is a no-op placeholder. Modifying register state is how the interrupt handler will communicate its presence, adherence to the expected calling protocol and any potential result to the caller. (Hardware interrupt handlers are another story: since they can be invoked at unpredictable times, they must restore all registers to their original values before returning.) The comparison to syscalls is not a mere metaphor, as software interrupts have in fact been used as a syscall mechanism on a number of x86 operating systems: interrupt 0x80 in Linux, interrupt 0x21 in MS-DOS, and the VxDcall/VxDjmp pseudo-instruction in VMM32 (Windows 3.x Enhanced Mode and 9x), which is a specially-formatted interrupt 0x20 instruction.

Ralf Brown’s entry on interrupt 0x33 service 0 has this to say on the AX register:

Return: AX = status

  • 0000h hardware/driver not installed
  • FFFFh hardware/driver installed

which means that the AX register will be only left unmodified if the mouse driver is absent, demonstrating the principle above. If you increment the FFFFh value returned in AX by service 0, you get zero (thanks to 16-bit wrap-around), which means you end up invoking service 0 again. This is easily seen in a DEBUG session:

C:\>debug
-a
1E8A:0100 mov ax,0
1E8A:0103 int 33
1E8A:0105 inc ax
1E8A:0106 int 33
1E8A:0108
-rax
AX 0000
:abcd
-r
AX=ABCD  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000  
DS=1E8A  ES=1E8A  SS=1E8A  CS=1E8A  IP=0100   NV UP EI PL NZ NA PO NC 
1E8A:0100 B80000        MOV     AX,0000                            
-p

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000  
DS=1E8A  ES=1E8A  SS=1E8A  CS=1E8A  IP=0103   NV UP EI PL NZ NA PO NC 
1E8A:0103 CD33          INT     33                                 
-p

AX=FFFF  BX=0005  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000  
DS=1E8A  ES=1E8A  SS=1E8A  CS=1E8A  IP=0105   NV UP EI PL NZ NA PO NC 
1E8A:0105 40            INC     AX                                 
-p

AX=0000  BX=0005  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000  
DS=1E8A  ES=1E8A  SS=1E8A  CS=1E8A  IP=0106   NV UP EI PL ZR AC PE NC 
1E8A:0106 CD33          INT     33                                 
-

The same RBIL entry notes that the interrupt 0x33 call also modifies BX. You should probably make Show_Mouse preserve the value of the BX register, or document that the call may modify it. Since you use the stack to preserve AX, I presume you intend to do the former.

6
  • Thank you, I wasn't aware that the interrupt returned an error code. Perhaps I can use that to display an error message like so: mov ax,0 int 33h jz MouseDriverNotInstalled Commented Aug 21, 2021 at 17:42
  • 3
    You will need to place test ax, ax before the jz. RBIL only mentions that the interrupt may update the AX register, not condition flags. (If interrupt 0x33 is a pure no-op, it certainly will not update them.) Commented Aug 21, 2021 at 17:46
  • 7
    @puppydrum64, Re, "I wasn't aware that the interrupt returned an error code." The name, "software interrupt," is misleading, but we're stuck with it. Software interrupts don't interrupt the program; they are requested by the program. They usually accept args and/or return results. They are much more like function calls than like hardware interrupts, which happen without warning, and which must not disturb the program. The thing called "software interrupt" in x86 nomenclature has better names in some other architectures ("trap," "syscall," "supervisor call (SVC)," ...) Commented Aug 22, 2021 at 10:56
  • I see! Honestly I should have known better - I'm used to BIOS calls on the NEOGEO and GBA which often use certain registers or have values returned in certain registers, and the inner workings of which are often hidden from the programmer. My assumption was that such a routine would also set the flags accordingly, which can't be guaranteed either. Commented Aug 22, 2021 at 15:12
  • 3
    An x86 int instruction pushes condition flags on the stack, and the corresponding iret restores them, so unless the interrupt handler specifically modifies the flags word, the condition codes will generally be preserved. Although it’s pretty common to modify condition codes anyway; CF is often used as an error indicator, for example. But if it’s not part of the documented API, better not rely on it. Commented Aug 22, 2021 at 15:30
6

Simple answer: Because you don't know the value stored in AX after a return.

A lot of system routines change the value (not only the AX register but generally any register). See system call documentation, sometimes there are remarks like "Preserves: CX, DX. Returns: BX=0, BP=unpredictable, AX=return value" or so.

Sometimes you have guaranteed the value in some register, but the best practice is to define all the information system call needs. Otherwise, you are doing a "premature optimization".

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .