• Help with COM1 serial port interrupt in x86 assembly

    From Robert Pengelly@robertapengelly@gmail.com to comp.lang.asm.x86 on Fri Nov 10 22:22:17 2023
    From Newsgroup: comp.lang.asm.x86

    Do anyone know how I get the COM1 serial port interrupt to work in a 16-bit operating system? I have the following:

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Disable all interrupts.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F9)
    xor al, al
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Enable DLAB (set baud rate divisor).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FB)
    mov al, HEX (80)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Set divisor.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F8)
    mov al, HEX (03) ; 3 (lo byte) 38400 baud.
    out dx, al

    mov dx, HEX (03F9)
    xor al, al ; (hi byte)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; 8 bits, no parity, one stop bit.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FB)
    mov al, HEX (03)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Enable FIF0, clear them, with 14-byte threshold.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FA)
    mov al, HEX (C7)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; IRQs enabled, RTS/DSR set.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FC)
    mov al, HEX (0B)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Set in loopback mode, test the serial chip.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FC)
    mov al, HEX (1E)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Test serial chip (send byte 0xAE and check if serial
    ;; returns same byte).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F8)
    mov al, HEX (AE)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Check if serial is faulty (i.e. not same byte as sent).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F8)
    xor ax, ax
    in al, dx

    cmp al, HEX (AE)
    jne .bad

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; If serial is not faulty set it in normal operation mode
    ;; (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FC)
    mov al, HEX (0F)
    out dx, al

    which as far as I can tell is the assembly equivalent of the example shown towards the bottom of https://wiki.osdev.org/Serial_Ports but I can't figure out how to get an interrupt working.

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Branimir Maksimovic@branimir.maksimovic@icloud.com to comp.lang.asm.x86 on Sun Nov 12 00:30:40 2023
    From Newsgroup: comp.lang.asm.x86

    On 2023-11-11, Robert Pengelly <robertapengelly@gmail.com> wrote:
    Do anyone know how I get the COM1 serial port interrupt to work in a 16-bit operating system? I have the following:

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Disable all interrupts.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F9)
    xor al, al
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Enable DLAB (set baud rate divisor).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FB)
    mov al, HEX (80)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Set divisor.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F8)
    mov al, HEX (03) ; 3 (lo byte) 38400 baud.
    out dx, al

    mov dx, HEX (03F9)
    xor al, al ; (hi byte)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; 8 bits, no parity, one stop bit.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FB)
    mov al, HEX (03)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Enable FIF0, clear them, with 14-byte threshold.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FA)
    mov al, HEX (C7)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; IRQs enabled, RTS/DSR set.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FC)
    mov al, HEX (0B)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Set in loopback mode, test the serial chip.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FC)
    mov al, HEX (1E)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Test serial chip (send byte 0xAE and check if serial
    ;; returns same byte).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F8)
    mov al, HEX (AE)
    out dx, al

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Check if serial is faulty (i.e. not same byte as sent).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03F8)
    xor ax, ax
    in al, dx

    cmp al, HEX (AE)
    jne .bad

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; If serial is not faulty set it in normal operation mode
    ;; (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled).
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov dx, HEX (03FC)
    mov al, HEX (0F)
    out dx, al

    which as far as I can tell is the assembly equivalent of the example shown towards the bottom of https://wiki.osdev.org/Serial_Ports but I can't figure out how to get an interrupt working.

    You have to tell PIC and UART to trigger interrupt. Just out instructions, I guess that now since there
    is internet, you can find info easilly. Last time I programmed that was 1994... --

    7-77-777, Evil Sinner! https://www.linkedin.com/in/branimir-maksimovic-6762bbaa/

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Borax Man@rotflol2@nospicedham.hotmail.com to comp.lang.asm.x86 on Sun Nov 12 12:14:58 2023
    From Newsgroup: comp.lang.asm.x86

    On Sun, 12 Nov 2023 00:30:40 GMT
    Branimir Maksimovic <branimir.maksimovic@icloud.com> wrote:

    You have to tell PIC and UART to trigger interrupt. Just out instructions, I guess that now since there
    is internet, you can find info easilly. Last time I programmed that was 1994...

    Yes, you will need to write an interrupt handler which gets called
    when the interrupt is triggered. I haven't done this for a serial
    port, but an example of an interrupt handler I wrote using the PC
    timer is below. Its a bit more complex than it needs to be, because
    it also makes sound through the PC speaker.

    OurInt08 is the interrupt handler.

    This section saves the old interrupts then sets the interrupt vector to point to our handler. This is just for demonstration purposes.

    cli ;Turn off interrupts!
    mov word ax, [es:08h*4]
    mov word [cs:OldInt8], ax
    mov word ax, [es:08h*4 + 2]
    mov word [cs:OldInt8+2], ax

    mov ax, OurInt08
    mov word [es:08h*4], ax
    mov ax,cs
    mov word [es:(08h*4)+2], ax

    Then don't forget to restore the old hander...


    mov ax, word [cs:OldInt8]
    mov word [es:08h*4], ax
    mov word ax, word [cs:OldInt8+2]
    mov word [es:08h*4 + 2], ax


    The full program is below and it will compile with FASM.


    ; A working example of how to use an interrupt handler
    ; in DOS. This source file is for FASM.
    ;

    FORMAT MZ
    ; DOS 16-bit EXE format
    STACK 400h
    HEAP 0 ; No extra heap required.
    ENTRY CODE:Init

    ; Quick example of using interrupts and chaning the PIT
    ; tick rate
    use16

    freq = 42940
    inputlen = 160

    SEGMENT CODE USE16

    Init:
    mov ax,DATA2
    mov ds,ax
    mov fs,ax ; Set FS to point to data segment.
    ; DS will change during the program
    ; but FS won't as DOS doesn't touch this.
    mov ax, oldscreen ; Create far pointer.
    mov word [dptr],ax
    mov word [dptr+2],ds

    mov ah,09h
    mov dx,intro
    int 21h ; Print introduction message.

    mov cx,inputlen ; Input length
    xor ax,ax
    mov ah,3fh
    mov bx,0
    mov dx,input ; Where to store
    int 21h
    jc endp
    cmp ax,8
    jg inputerror ; Anything more than 6 characters we reject

    mov bx, ax ; BX will store the offset as we go through the input data.
    cmp [bx+input-1],0ah ; If this is not the last character
    ; It means we didn't fill all the input
    ; and some may be lost. Best to end with
    ; an error. Besides, if it was a valid
    ; number, it would have been well over the
    ; maximum number of stars we could handle.
    jne inputerror

    xor bx, bx ; Start index at 0.
    sub ax, 2 ; We don't want the CR/LF duo at the end.
    mov cx, ax ; CX will be used to compare the index
    ; to determine whether we have reached the end of the user data
    ; we want to convert.
    xor ax, ax ; Start tally at 0.
    mov si, 10 ; Multiply each number we get by 10, as we push it up
    ; as a significant digit.
    mov di, input
    .getnum:
    cmp byte [di+bx], '0' ; If less than '0', its not a numeral.
    jl inputerror
    cmp byte [di+bx], '9' ; If more than '9', its not a numeral.'
    jg inputerror
    sub byte [di+bx], 30h ; Sub 30h to convert the ascii numeral
    ; to the number
    mul si ; Multiply what we have added so far by 10
    push dx
    mov dl, byte [di+bx]
    add ax,dx ; And add the next digit
    pop dx
    inc bx
    cmp bx, cx
    jl .getnum ; If more digits, go again.
    mov cx, ax
    begindemo:
    mov al, 0x36
    out 0x43, al ;tell the PIT which channel we're setting
    mov ax, cx
    out 0x40, al ;send low byte
    mov al, ah
    out 0x40, al ;send high byte
    xor dx,dx
    mov es,dx

    cli ;Turn off interrupts!
    mov word ax, [es:08h*4]
    mov word [cs:OldInt8], ax
    mov word ax, [es:08h*4 + 2]
    mov word [cs:OldInt8+2], ax

    mov ax, OurInt08
    mov word [es:08h*4], ax
    mov ax,cs
    mov word [es:(08h*4)+2], ax
    mov ax, 0B800h
    mov es,ax
    call savescreen
    ; The interrupt may update the screen prior to us saving it.
    call soundon
    sti

    inputloop:

    mov cx,inputlen ; Input name
    mov ax,3f00h ; Input call
    xor bx,bx ; From keyboard (stdin)
    mov dx,keybuff ; Save at keybuff
    int 21h
    cmp [fs:keybuff],'q'
    jne inputloop
    xor dx,dx
    mov es,dx
    call soundonoff

    mov es,dx
    cli
    mov ax, word [cs:OldInt8]
    mov word [es:08h*4], ax
    mov word ax, word [cs:OldInt8+2]
    mov word [es:08h*4 + 2], ax

    xor ax,ax
    out 0x43, al ;tell the PIT which channel we're setting
    out 0x40, al ;send low byte
    out 0x40, al ;send high byte

    call restorescreen

    mov ax,DATA3
    mov ds,ax
    mov ax,0b800h
    mov es,ax

    mov ah,09h
    mov dx,data3msg
    int 21h ; Print introduction


    mov cx,800
    mov bx,1
    loop55:
    mov byte [es:bx],2
    add bx,2
    dec cx
    jnz loop55
    endp:
    mov ax,4c00h ; Send exit code to dos
    int 21h ; Send command to DOS

    soundon:
    push ax
    in al,61h ;-Bits D1-D0 of "port 61h" are unmasked
    or al,03h ; or masked !
    out 61h,al
    pop ax
    ret
    soundonoff:
    push ax
    in al,61h ;-Bits D1-D0 of "port 61h" are unmasked
    xor al,03h ; or masked !
    out 61h,al
    pop ax
    ret


    OurInt08:
    ; This interrupt handler could be called in any context
    ; so we cannot assume anything about the state of the registers
    ; including the segment registers. As we are calling for user input
    ; this is likely to be called during DOS function 3F, and DOS
    ; may very well have set DS and ES to different values.
    pusha
    pushf
    add word [fs:elapsed], tickrate
    jnc skipint

    cli
    mov al,0b6h
    mov dx, 043h
    out dx,al

    mov ax,[fs:elapsed]
    mov dx, 042h
    out dx,al
    mov al, ah
    out dx,al
    sti
    popf
    popa
    jmp far [cs:OldInt8] ; using INT 08H emulation
    skipint:
    push 0b800h
    pop gs
    mov bx,(160*20)-2
    loop1:
    inc word [gs:bx]
    sub bx,2
    jnc loop1
    ll2:
    mov al,20h ; EOI code for PIC
    out 20h,al ; Send
    popf
    popa
    iret

    savescreen:
    push ax
    mov cx,2000-2
    xor si,si
    les di, [dptr]
    cld
    push 0b800h
    pop ds

    rep movsw
    push ds
    pop es
    mov ax,DATA2
    mov ds,ax

    pop ax
    ret

    restorescreen:
    cld
    push ds
    mov ax,DATA2
    mov ds,ax
    push 0b800h
    pop es
    push ax
    mov cx,2000-2
    lds si, [dptr] ; This is unnecessary, we don't need to store the pointer
    ; But I wanted to use the lds and les commands as an example
    xor di,di
    rep movsw
    pop ax
    pop ds
    ret
    inputerror:
    mov dx,invalid ;Tell user about invalid input
    mov ah,09h
    int 21h
    mov ah,10h
    int 16h ; Wait for keypress
    jmp begindemo


    OldInt8 dd 0


    SEGMENT DATA3 USE16
    data3msg db "Now we are at the next part of the demonstration."
    db 13,10,"I will change some text colours.",13,10,24h


    SEGMENT DATA2 USE16
    invalid db "Invalid input or number too high. ",\
    "Using default number of stars.",0ah,0dh,\
    "Press any key.",0ah,0dh,24h

    numstars dw 10000
    elapsed dw 0
    intro db "Interrupt handler demo, by Borax Man",13,10
    db "September, 2020",13,10
    db 13,10,"Type 'q' and hit enter to exit while demo is running.",13,10,10
    db "Enter a ticrate between 0 - 65536",13,10
    db "Numbers lower than 3000 may cause problems on older machines",13,10,24h
    input rb 10

    keybuff rb inputlen ; This must be immediately after message
    dptr: rb 4
    oldscreen: rb 4000


    SEGMENT ENDSEG
    --

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Branimir Maksimovic@branimir.maksimovic@nospicedham.icloud.com to comp.lang.asm.x86 on Mon Nov 13 03:11:14 2023
    From Newsgroup: comp.lang.asm.x86

    On 2023-11-12, Borax Man <rotflol2@nospicedham.hotmail.com> wrote:
    On Sun, 12 Nov 2023 00:30:40 GMT
    Branimir Maksimovic <branimir.maksimovic@icloud.com> wrote:

    You have to tell PIC and UART to trigger interrupt. Just out instructions, I guess that now since there
    is internet, you can find info easilly. Last time I programmed that was 1994...

    Yes, you will need to write an interrupt handler which gets called
    when the interrupt is triggered. I haven't done this for a serial
    port, but an example of an interrupt handler I wrote using the PC
    timer is below. Its a bit more complex than it needs to be, because
    it also makes sound through the PC speaker.


    Here it is nicely explained; https://www.activexperts.com/serial-port-component/tutorials/uart/
    --

    7-77-777, Evil Sinner! https://www.linkedin.com/in/branimir-maksimovic-6762bbaa/

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Terje Mathisen@terje.mathisen@nospicedham.tmsw.no to comp.lang.asm.x86 on Mon Nov 13 16:30:50 2023
    From Newsgroup: comp.lang.asm.x86

    Branimir Maksimovic wrote:
    On 2023-11-12, Borax Man <rotflol2@nospicedham.hotmail.com> wrote:
    On Sun, 12 Nov 2023 00:30:40 GMT
    Branimir Maksimovic <branimir.maksimovic@icloud.com> wrote:

    You have to tell PIC and UART to trigger interrupt. Just out instructions, I guess that now since there
    is internet, you can find info easilly. Last time I programmed that was 1994...

    Yes, you will need to write an interrupt handler which gets called
    when the interrupt is triggered. I haven't done this for a serial
    port, but an example of an interrupt handler I wrote using the PC
    timer is below. Its a bit more complex than it needs to be, because
    it also makes sound through the PC speaker.


    Here it is nicely explained; https://www.activexperts.com/serial-port-component/tutorials/uart/


    Borax Man is lucky indeed!

    I first had to implement this stuff in 1982, writing the interrupt
    handler code inside DEBUG.COM and then copy the generated bytes into my mainline (Modula2) program.

    I repeated that exact process a year or two later with Turbo Pascal 1.0.

    Having a proper 16-byte buffered uart makes it all much simpler!

    Terje
    --
    - <Terje.Mathisen at tmsw.no>
    "almost all programming can be viewed as an exercise in caching"

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Borax Man@bob@nospicedham.zerosignal.strangled.net to comp.lang.asm.x86 on Tue Nov 14 10:11:13 2023
    From Newsgroup: comp.lang.asm.x86

    On 2023-11-13, Terje Mathisen <terje.mathisen@nospicedham.tmsw.no> wrote:
    Branimir Maksimovic wrote:
    Borax Man is lucky indeed!

    I first had to implement this stuff in 1982, writing the interrupt
    handler code inside DEBUG.COM and then copy the generated bytes into my mainline (Modula2) program.

    I repeated that exact process a year or two later with Turbo Pascal 1.0.

    Having a proper 16-byte buffered uart makes it all much simpler!

    Terje


    My first experience with assembly was typing in assembly source code
    listings from Australian Personal Computer magazine. They were
    essentially debug scripts. You'd feed it into debug and it would
    create a .COM file. Had little idea what the code actually meant, but
    I sought information on how to write my own 'debug scripts' so I could
    create my own machine language programs.

    Then I found MASM and it made things quite a bit easier.

    --- Synchronet 3.21d-Linux NewsLink 1.2