18.4 Using Interrupts with PModeLib Functions

In Section 17.3, we looked at how interrupts are called from protected mode. The concepts are still the same with PModeLib, but many of the details are integrated into PModeLib (such as the DPMI_Regs, _Transfer_Buf, and _Transfer_Buf_Seg variables). Also, PModeLib provides a nice wrapper function around DPMI function 0300h, aptly named DPMI_Int.

Here is the same print string program we looked at in Section 17.3.4.2, written using PModeLib:

Example 18-2. PModeLib "dspmsg" Program

%include "lib291.inc"

BITS 32

GLOBAL _main

SECTION .data

; String to print to screen
hello   db 'Hello, World!',13,10,'$'

SECTION .text

_main

        call    _LibInit         ; Initialize PModeLib

        ; PModeLib allocated the DOS memory for us; just
        ;  copy the string into it.  Note that while a loop is
        ;  used here, for larger amounts data (or data of fixed
        ;  size), it would probably make more sense to use a
        ;  string instruction such as rep movsd here.
        push    es
        mov     es, [_Transfer_Buf]     ; Put the selector into es.
        xor     ebx, ebx                ; Offset into string
.loop:
        mov     al, [hello+ebx] ; Copy from string
        mov     [es:ebx], al    ; Into DOS memory area, starting at offset 0
        cmp     al, '$'         ; Stop at the '$' marker.
        jne     .loop
        pop     es

        ; Set up the input registers for the DOS interrupt in the PModeLib
        ;  DPMI_Regs structure.
        mov     word [DPMI_EAX], 0900h  ; Neat trick: even though DPMI_EAX
                                        ;  is a dword, thanks to little
                                        ;  endian storing just a word works.
                                        ; Note that AH=09h, not AL.
                                        ; mov byte [DPMI_EAX+1], 09h would
                                        ;  have worked here too.  Why? :)
        mov     word [DPMI_EDX], 0      ; Again, only care about the low 16
                                        ;  bits (DX).  Set to 0 because that's
                                        ;  where the string was copied to!
                                        ;  sees.
        ; It's not necessary to set DPMI_DS because LibInit sets it.

        ; Now simulate the interrupt using the PModeLib function DPMI_Int:
        mov     bx, 21h         ; Real mode interrupt number (DOS interrupt)
        call    DPMI_Int

        ; PModeLib's LibExit frees the DOS memory block.
        call    _LibExit

        ; We're done, exit back to DOS.
        ret