What is i.h.ah, o.h.ah and int86?
Asked Answered
C

2

6

I am trying to understand a program to get arrow keys. Here is the code:

int getkeys( )
{
       union REGS i,o;
       while(!kbhit( ));
            i.h.ah=0;
       int86(22,&i,&o);
            return(o.h.ah);
 } 

can someone please explain me this code. It is to get arrow keys but i am not getting this code.

Chesser answered 7/9, 2019 at 14:11 Comment(2)
i and o are the input and output registers for the int86 call. ah is a register. On input it contains the function code which is 0 for "Wait for Keypress and Read Character". On output it contains the scancode read.Bleeding
The spacing here is a bit misleading. As written, it looks like the i.h.ah=0; is being done inside the while loop (which is not the case). Some people put the semicolon after the while on a line by itself to help avoid this kind of confusion.Laburnum
C
8

Turbo-C/C++'s int86 function is used to make system interrupt calls to DOS and BIOS services. The REGS union is a way to address individual registers in the interrupt context data structure. In order to make a DOS or BIOS call you need to set up values in the registers based on the requirements of the system call you want to make. Ralph Brown's Interrupt List (RBIL) is an excellent source of DOS and BIOS system calls, their parameters and their return values. The Turbo-C documentation defines int86 this way:

Name            int86  - general 8086 software interrupt interface

Usage           int int86(int intr_num, union REGS *inregs,
                          union REGS *outregs);

Prototype in    dos.h

Description     Both of these functions execute an 8086 software
                interrupt specified by the argument intr_num.

                Before executing the software interrupt, both functions
                copy register values from inregs into the registers.

                In addition, int86x copies the segregs->x.ds and
                segregs->x.es values into the corresponding registers
                before executing the software interrupt. This feature
                allows programs that use far pointers, or that use a
                large data memory model, to specify which segment is
                to be used during the software interrupt.

                After the software interrupt returns, both functions
                copy the current register values to outregs, copy the
                status of the system carry flag to the x.cflag field
                in outregs, and copy the value of the 8086 flags register
                to the x.flags field in outregs.  In addition, int86x
                restores DS, and sets the segregs->es and segregs->ds
                fields to the values of the corresponding segment
                registers.

                If the carry flag is set, it indicates that an error
                occurred.

                int86x allows you to invoke an 8086 software interrupt
                that takes a value of DS different from the default data
                segment, and/or that takes an argument in ES.

                Note that inregs can point to the same structure that
                outregs points to.

Return value    int86 and int86x return the value of AX after completion
                of the software interrupt. If the carry flag is set
                (outregs->x.cflag != 0), indicating an error, these
                functions set _doserrno to the error code.

int86 takes the interrupt number to call and two REGS union pointers. The first one contains the values that need to be set upon entry to the interrupt handler and the second is a way to retrieve the register values that are returned by the interrupt.

Turbo-C/C++'s definition of REGS looks similar to:

struct WORDREGS {
    unsigned int    ax, bx, cx, dx, si, di, cflag, flags;
};

struct BYTEREGS {
    unsigned char   al, ah, bl, bh, cl, ch, dl, dh;
};

union   REGS    {
    struct  WORDREGS x;
    struct  BYTEREGS h;
};

The h part of the union is simply a mechanism of addressing the high and low 8-bit registers of the 16-bit registers AX, BX, CX, and DX rather than through the full 16-bit register via x. This code:

i.h.ah=0;

simply sets the AH register (The top 8-bit register of AX) to 0 to pass into the int86 function. This code:

int86(22,&i,&o);

Is making a call to software interrupt 22 (0x16). If you look at RBIL Int 0x16/AH=x00 you find that it is the BIOS call for:

KEYBOARD - GET KEYSTROKE
AH = 00h

Return:
AH = BIOS scan code
AL = ASCII character

You will see that this BIOS call returns the BIOS scan code of the next character pressed in AH and AL is the ASCII code.

The line:

return(o.h.ah);

returns the BIOS scan code of the character pressed from getkeys

Note: The code while(!kbhit( )); waits in a loop until a key press is detected. The Int 0x16/AH=0x00 call is being used to retrieve the BIOS scan code of that key press.

Calendra answered 7/9, 2019 at 14:36 Comment(7)
@SepRoland : took my header from TCC301 dos.h which is pretty much how all releases to that point defined it.Calendra
@SepRoland : h was for half words lol. But b and w would have probably made more sense from a common sense usage perspective given that the structs use byte and word in their namesCalendra
@SepRoland : A footnote would be that Watcom made a change. What they did was say in 16-bit code that x and w are the same and h was half. So the full register is x and w (word) are the same and h is the half. In 32-bit code they amended x so it was a DWORD (full register), w was a word and h the half.Calendra
So a change brought about out of necessity to deal with the 32-bit registers. Out of curiosity: Does Watcom exclude the use of 32-bit registers in 16-bit code then? (Treating x and w the same)Ineffectual
@SepRoland Yes, watcom excludes the use of 32-bit registers in 16-bit code, which makes for calling BIOS functions that require 32-bit registers (even in 16-bit real mode) a bit difficult. I'm pretty sure though that when writing 32-bit code with a 386 DOS Extender (ie Pharlap) calling down to real mode/v8086 mode to make a BIOS/DOS call - the data structures have x as dwords and w as words and the underlying function saves/restores 32-bit registers.Calendra
Should have probably said DOS/4G as that was what Watcom shipped with their compilers at one time (Not Pharlap). Didn't notice that brainphart until too late lolCalendra
This helped me a lot.Chesser
P
1

It calls BIOS interrupt 16h, function 00h, which is "Read key press".

When this interrupt call returns, AH will contain the scan code of the pressed key.

int86 means "call interrupt". 22 is the interrupt number (16h, means hexadecimal 16).

i means the input register set to the interrupt. AH contains the function call number: 0 (There are other calls as well: https://en.wikipedia.org/wiki/INT_16H)

o means the output register set from the interrupt. AH is specified (by BIOS) to contain the scan code of the pressed key.

Pol answered 7/9, 2019 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.