assemblyvirtualboxinterruptx86-16bios

Why is BH not used as an argument in the BIOS teletype function (`ah=0Eh` `int 10h`)?


The interrupt ah=0Eh int 10h displays a character on the screen, it has several parameters including BH, which is supposed to indicate the page number, except that after several tests I realized that BH is absolutely useless. Even if I change the page number the character will still display on the active page no matter what.

Ralf Brown's interrupt list says

"IBM PC ROMs dated 1981/4/24 and 1981/10/19 require that BH be the same as the current active page"

And a page on the internet puts this : https://www.ic.unicamp.br/~celio/mc404-2004/service_interrupts#int10h_0Eh

This functions displays a character on the screen, advancing the cursor and scrolling the screen as necessary. The printing is always done to current active page.

I have to trust which source, I use virtual box without operating system, and I use a bootloader program.

I specify that I do not understand this information

IBM PC ROMs dated 1981/4/24 and 1981/10/19 require that BH be the same as the current active page

Knowing that I use virtual box, I do not think that virtual box uses ROMs from IMG PCs dating from 1981?


Solution

  • Service ah = 0x0E, int 0x10 only prints on active page. You can change active page using service ah = 0x05, al = page number, int 0x10.

    BH isn't supposed to be an input to this BIOS function. Ralf Brown's interrupt list only shows it as part of documenting the bug in the two early IBM-PC BIOS versions it mentions (1981/4/24 and 1981/10/19) where BH needs to hold the active page number. Otherwise it's not an input at all.


    I looked at the code of BIOS 1981-04-24, 1981-10-19 and 1982-10-27 and the service ah = 0x0E (write_tty) in the first two BIOSes starts like this:

    WRITE_TTY PROC    NEAR
      PUSH    AX      ; SAVE REGISTERS
      PUSH    AX      ; SAVE CHAR TO WRITE
      MOV AH,3
      INT 10H         ; READ THE CURRENT CURSOR POSITION
      POP AX          ; RECOVER CHAR
    
      . . .
    
    ;------ WRITE THE CHAR TO THE SCREEN
    
      MOV BH,ACTIVE_PAGE  ; GET THE CURRENT ACTIVE PAGE
      MOV AH,10       ; WRITE CHAR ONLY
      MOV CX,1        ; ONLY ONE CHAR
      INT 10H
    

    There is service ah = 0x03 (read_cursor) at the beginning which needs bh = page number to calculate offset to cursor position on active page, this value is returned in DX and stored here:

      CURSOR_POSN DW  8 DUP(?)    ; CURSOR FOR EACH OF UP TO 8 PAGES
    

    And later we have instruction mov bh,active_page. For example we want to write to page 0, we calculate cursor position on page 0, but active_page = 1 so we have mismatch. So maybe that's why RBIL page talk about it, BH should be same as active_page.

    IBM PC ROMs dated 1981/4/24 and 1981/10/19 require that BH be the same as the current active page

    In last BIOS 1982-10-27, line MOV BH,ACTIVE_PAGE changed position

    WRITE_TTY PROC    NEAR
      PUSH    AX          ; SAVE REGISTERS
      PUSH    AX          ; SAVE CHAR TO WRITE
      MOV AH,3
      MOV BH,ACTIVE_PAGE      ; GET THE CURRENT ACTIVE PAGE
      INT 10H         ; READ THE CURRENT CURSOR POSITION
      POP AX          ; RECOVER CHAR
    

    BH is same as active_page. So program calculates position for correct page.


    Here's an example of using it in a BIOS MBR bootloader. This works well in QEMU:

    [org 0x7C00]
    
    section .text
        global main
    
    main:   
        mov ax,0
        mov ds,ax
        mov ss,ax
        mov sp,0x7C00
    
        mov ax,0x0003
        int 0x10
    
        mov ah, 0x0E
        mov si, msg1    
        
    Msg_1:
        lodsb                
        or al,al
        jz WaitKeyPress1
        int 0x10        
        jmp Msg_1
    
    WaitKeyPress1:
        mov ah,0x00
        int 0x16
        
        mov ah,0x05
        mov al,0x01
        int 0x10
        
        mov ah,0x0E
        mov si,msg2
        
    Msg_2:
        lodsb                
        or al,al
        jz WaitKeyPress2
        int 0x10        
        jmp Msg_2   
    
    WaitKeyPress2:
        mov ah,0x00
        int 0x16
        
        mov ah,0x05
        mov al,0x00
        int 0x10
    
    Done:   
        jmp Done
    
    msg1 db "This message is printed on page 0."
            db 13,10,"Press any key to change page to 1...",0
        msg2 db "And this message is printed on page 1."
            db 13,10,"Press any key to go back to page 0.",0
    
        
        times 510 - ($ - $$) db 0
        dw 0xAA55
    
    

    This works from the command line, as a DOS .COM:

    [org 100h]
        
    section .data
        msg1 db "This message is printed on page 0."
            db 13,10,"Press any key to change page to 1...",0
        msg2 db "And this message is printed on page 1."
            db 13,10,"Press any key to go back to page 0.",0
            
    
    section .text
        global main
    
    main:   
        mov ax,0x0003
        int 0x10
    
        mov ah, 0x0E  
        mov si, msg1    
        
    Msg_1:
        lodsb                
        or al,al
        jz WaitKeyPress1
        int 0x10        
        jmp Msg_1
    
    WaitKeyPress1:
        mov ah,0x08
        int 0x21
        
        mov ah,0x05
        mov al,0x01
        int 0x10
        
        mov ah,0x0E
        mov si,msg2
        
    Msg_2:
        lodsb                
        or al,al
        jz WaitKeyPress2
        int 0x10        
        jmp Msg_2   
    
    WaitKeyPress2:
        mov ah,0x08
        int 0x21
        
        mov ah,0x05
        mov al,0x00
        int 0x10
    
    Done:   
        mov ax,4C00h
        int 21h