Search code examples
assemblyx86dosinterrupt

INT 21h is not working with AH = 09h


I'm trying to program a driver to provide services in 55h. I'm making some tries to be sure everything works but I've found that when I call INT 21h with AH = 09h to print a string, it doesn't print anything, even with the right offset in DX, and I don't have any idea how this can be. In other sections of the code, it works fine. Here's my code:

codigo SEGMENT
ASSUME CS :codigo
ORG 256

inicio:
    MOV AL, DS:[83H] ;Parameters: when call with '/I' installs driver
    CMP AL, 49H
    JE inst
    CMP AL, 44H ;Parameters: when call with '/D', uninstalls
    JE desinstalar_55h
    JE rsi

    PRUEBA DB "THIS IS A TEST",0AH,'$'

rsi PROC FAR
    STI
    CMP AH, 00
    JE firm ;used to check if driver is install
    MOV CX, 0
    CMP AH, 11H
    JE codificar
volver:
    IRET
desinst:
    JE desinstalar_55h
inst: 
    CALL instalador
firm:
    CALL firma
rsi ENDP


firma PROC NEAR
    MOV AX, 0F0F0H
    RET
firma ENDP

desinstalar_55h PROC
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DS
    PUSH ES
    CALL check_driver
    CMP AX, 1
    JE no_instalado
    MOV CX, 0
    MOV DS, CX
    MOV ES, DS:[55H*4+2]
    MOV BX, ES:[2CH]
    MOV AH, 49H
    INT 21H
    MOV ES, BX
    INT 21H
    CLI
    MOV DS:[55H*4], CX
    MOV DS:[55H*4+2], CX
    STI
no_instalado:
    POP ES
    POP DS
    POP CX
    POP BX
    POP AX
    RET
desinstalar_55h ENDP


instalador PROC
    CALL check_driver
    CMP AX, 1
    JNE ya_instalado
    MOV DX, OFFSET PRUEBA
    MOV AH, 09H
    INT 21H ;here it works fine
    MOV AX, 0
    MOV ES, AX
    MOV AX, OFFSET rsi
    MOV BX, CS
    CLI
    MOV ES:[55h*4], AX
    MOV ES:[55h*4+2], BX
    STI
    MOV DX, OFFSET instalador
ya_instalado:
    INT 27H
instalador ENDP

codificar PROC
    CLD
    MOV DX, OFFSET PRUEBA
    MOV AH, 09H
    INT 21H ;here it doens't print anything
    RET
codificar ENDP

check_driver PROC
    MOV AX, 0
    MOV ES, AX
    MOV AX, OFFSET rsi
    CMP WORD PTR ES:[55H*4], 0
    JNE detectar_mi_driver
    CMP WORD PTR ES:[55H*4 + 2], 0
    JE no_driver
detectar_mi_driver:
    MOV AH, 0
    INT 55H
    CMP AX, 0F0F0H
    JE fin_check
no_driver: 
    MOV AX, 1
fin_check: RET
ENDP check_driver

codigo ENDS
END inicio

For example, inside 'instalador' rutine, which install the driver, I make a call to INT 21h and it works fine, printing the string. But when I call the interruption 55h with AH = 11h from another .EXE program, it should execute rsi process and jump to 'codificar' rutine, which calls INT 21h, but in this case, it doens't print anything, although it has the same offset in DX. Anyone knows why this can be? Thanks in advance for your help.

EDIT: I've tried to debugged it with TD and works fine, prints the string. However, when I call the interruption 55h with AH = 11h from another program, it doesn't print anything or just got stuck and never returns.


Solution

  • Several problems exist in this program.

    inicio:
     MOV AL, DS:[83H] ;Parameters: when call with '/I' installs driver
     CMP AL, 49H
     JE inst
     CMP AL, 44H ;Parameters: when call with '/D', uninstalls
     JE desinstalar_55h
     JE rsi
    
     PRUEBA DB "THIS IS A TEST",0AH,'$'
    

    In the previous code snippet the JE rsi is useless and should be replaced by a program termination. You can use RET because the program is a .COM

    rsi PROC FAR
     STI
     CMP AH, 00
     JE firm ;used to check if driver is install
     MOV CX, 0
     CMP AH, 11H
     JNE volver      <------------ codificar is a routine that ends with
     CALL codificar  <------------ a mere RET so call it (don't jump to it!)
    volver:          <------------ and fall through in the IRET.
     IRET
    ;;;;desinst:              \
    ;;;; JE desinstalar_55h   |  Move this code outside the proc!!!
    ;;;;inst:                 |  Here it has no sense.
    ;;;; CALL instalador      /
    firm:
     CALL firma
     IRET         <-- Add this because the RETF provided by ENDP is not enough!
    rsi ENDP
    

    The rsi procedure has the most problems. You have declared it to be a FAR PROC but in fact it is an interrupt handler and thus all of its exits should use the IRET instruction.

    instalador PROC
     CALL check_driver
     CMP AX, 1
     JNE ya_instalado
    

    At the label ya_instalado you have a terminate and stay resident call BUT the check made it clear that you are already installed! Solve this via

     int 27h
    ya_instalado:
     int 20h
    

    The last but most visible problem sits in the printing routine. When called from the executing .COM the DS register is setup by DOS. When called from within the interrupt handler 55h you have to setup DS manually. Just copy it from CS. Also you don't need the CLD instruction. And since this is after all an interrupt handler you are supposed to preserve registers.

    codificar PROC
     ;;;;CLD
     PUSH AX
     PUSH DX
     PUSH DS
     PUSH CS
     POP DS
     MOV DX, OFFSET PRUEBA
     MOV AH, 09H
     INT 21H
     POP DS
     POP DX
     POP AX
     RET
    codificar ENDP