Author Topic: x86 assembly, uart-16450, simple putch  (Read 1942 times)

0 Members and 1 Guest are viewing this topic.

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
x86 assembly, uart-16450, simple putch
« on: May 04, 2023, 07:24:36 pm »
Code: [Select]
; Serial Port 16450 IO
; ====================

;base DLAB access Abbreviation  Register Nam
; +0   0   Write      THR **    Transmitter Holding Buffer
; +0   0   Read       RBR **    Receiver Buffer
; +0   1   Read/Write DLL **    Divisor Latch Low Byte
; +1   1   Read/Write DLM **    Divisor Latch High Byte
; +1   0   Read/Write IER       Interrupt Enable Registe
; +2   x   Read       IIR       Interrupt Identification Register
; +3   x   Read/Write LCR **    Line Control Register
; +4   x   Read/Write MCR **    Modem Control Register
; +5   x   Read       LSR       Line Status Register
; +6   x   Read       MSR       Modem Status Register

; baud rate setting
; latch low byte
; 0x00 0x01 = 115200 bps
; 0x00 0x02 =  56700 bps
; 0x00 0x03 =  38400 bps
; 0x00 0x06 =  19200 bps
; 0x00 0x0C =   9600 bps
; 0x00 0x18 =   4800 bps
; 0x00 0x30 =   2400 bps

serial_base     equ (0x3f8)
serial_cmd0     equ (serial_base + 0)
serial_cmd1     equ (serial_base + 1)
serial_cmd2     equ (serial_base + 2)
serial_cmd3     equ (serial_base + 3)
serial_cmd4     equ (serial_base + 4)
serial_status   equ (serial_base + 5)

console_init:
                push   dx
                push   ax
                                         ; Settings
                mov    dx, serial_cmd3
                mov    al, 0x80          ; DLAB ON
                out    dx, al            ; get it on its way.

                mov    dx, serial_cmd0   ; 115200 bps
                mov    al, 0x01          ; baud rate - Divisor Latch low byte
                out    dx, al            ; get it on its way.

                mov    dx, serial_cmd1
                mov    al, 0x00          ; baud rate - divisor latch high byte
                out    dx, al            ; get it on its way.

                mov    dx, serial_cmd3
                mov    al, 0x03          ; 8 bits, No parity, 1 stop bit
                out    dx, al            ; get it on its way.

                mov    dx, serial_cmd2
                mov    al, 0xC7          ; FIFO control register
                out    dx, al            ; get it on its way.

                mov    dx, serial_cmd4
                mov    al, 0x0B          ; turn on DTR, RTS, and OUT2
                out    dx, al            ; get it on its way.

; clear the Divisor Latch Access Bit
; in order to use the Transmitter Holding Register

                mov    dx, serial_cmd3   ; Line Control Register
                in     al, dx
                and    al, 0x7f          ; 0111_1111b, Set DLAB=0, DLAP is bit7
                out    dx, al

                pop    ax
                pop    dx
                ret

; reg al: received character
ch_get:
                ; to be implemented
                mov    al, 0x00
                ret

; reg al: char to transmit
ch_put:
                push   dx
                push   ax
                push   cx
ch_put_wait:
                mov dx, serial_status    ; Line Status Register
                mov cx, ax
                in al, dx
                test cx, 0x20            ; 0010_0000b, use bit 5 to see if THR is empty
                jz ch_put_wait

                mov ax, cx
                mov dx, serial_cmd0      ; Transmitter Holding Register
                out dx, al

                pop    cx
                pop    ax
                pop    dx
                ret
(serial.s)

Code: [Select]
            cli                        ; disable interrupts

            mov bp, 0x9000
            mov sp, bp

            call console_init

main:
            mov al, 'h'
            call ch_put
            mov al, 'a'
            call ch_put
            mov al, 'l'
            call ch_put
            mov al, 'l'
            call ch_put
            mov al, 'o'
            call ch_put

            jmp main
(main.s)

do you see any error with this simple code?
The serial doesn't correctly show chars  :o :o :o

edit:
nasm syntax
« Last Edit: May 04, 2023, 08:19:50 pm by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Online Andy Watson

  • Super Contributor
  • ***
  • Posts: 2049
Re: x86 assembly, uart-16450, simple putch
« Reply #1 on: May 04, 2023, 08:39:06 pm »
I'm not familiar with the 16450, however, one common problem is not waiting for the character flag of the transmit register. Once the character is put into the Tx register the tx-full flag will be set, BUT usually this is a state controlled operation that is operating at the speed of the UART clock. Hence it is possible to put a character into tx reg, return from the subroutine, and then re-enter with the next character before the flag is set. The solution, is to use interupts or to wait for the flag to be set after putting the character in the Tx reg. If using the non-interrupt version, you might want to bracket the tx insertion and subsequent wait loop with disabled interupts - so that you don't miss the flag setting completely.
 
The following users thanked this post: DiTBho

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #2 on: May 04, 2023, 09:52:37 pm »
Code: [Select]
ch_put_wait:
                mov dx, serial_status    ; Line Status Register
                mov cx, ax
                in al, dx
                test cx, 0x20            ; 0010_0000b, use bit 5 to see if THR is empty
                jz ch_put_wait

this part? it waits and loops until the status register reports "THR is empty"
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Online Andy Watson

  • Super Contributor
  • ***
  • Posts: 2049
Re: x86 assembly, uart-16450, simple putch
« Reply #3 on: May 04, 2023, 10:17:12 pm »
You still need the empty check.

Consider what happens when you have multiple characters to transmit. Your present code is:

Loop until Tx empty - fill Tx 1 - go away and fetch next character - Loop until Tx empty - fill Tx 2 - etc..

The problem is that the setting of the UART flags is asynchronos and can be much slower than the "go away and fetch next charater" such that when the code returns with the second character it can find the Tx is still being flagged as empty.
The non-interrupt solution is:
Loop until Tx empty - (disable interrupts) - fill Tx 1 -  Loop until Tx full - (re-eneable interrupts) - go away to fetch nect character - etc..
You need the fill tx and loop until full to be atomic, hence the disabling/enabling of interrupts - if they are being used.
 
The following users thanked this post: DiTBho

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #4 on: May 04, 2023, 10:22:55 pm »
but interrupts are always disabled in that code.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline mwb1100

  • Frequent Contributor
  • **
  • Posts: 450
  • Country: us
Re: x86 assembly, uart-16450, simple putch
« Reply #5 on: May 04, 2023, 10:24:51 pm »
There's a couple problems in the loop that waits for THR empty:

Code: [Select]
ch_put_wait:
                mov dx, serial_status    ; Line Status Register
                mov cx, ax
                in al, dx                ; <--- al is trashed so you have lost the character you want to transmit, if a jump back to ch_put_wait happens then CX is trashed too
                test cx, 0x20            ; <--- cx doesn't have the serial status register's value - al does.    // 0010_0000b, use bit 5 to see if THR is empty
                jz ch_put_wait

Disclaimer - my x86 assembly is very rusty.  I can't remember the last time I had to do anything with it.

Same goes for my 16x50 family UART knowledge.

« Last Edit: May 04, 2023, 10:29:08 pm by mwb1100 »
 
The following users thanked this post: DiTBho

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #6 on: May 04, 2023, 10:26:50 pm »
ok, let's also try a different approach, just in case ...

Unfortunately, I don't have a serious x86 machine with a physical 16460 chip handy, my mac-mini/x86 doesn't have it, and my Soekris Net5501's serial line "may be" damaged (its firmware doesn't correctly output chars on the serial line), or it's just that its flash is corrupted, which is why I'd like to reprogram it with u-boot


Can anyone with a BIOS-x86 && physical 16460 uart test/confirm that this code works?
Code: [Select]

; Serial Port Initialization
; ==========================
;
; ah=0x00 ; serial BIOS service (request, init)
; serial port status is returned in ax
;
; lets set the baud rate, parity modes, stop bits, bits tx/rx
;
; Bits    Function
; 5..7    baud rate
;  000      110 bps
;  001      150 bps
;  010      300 bps
;  011      600 bps
;  100     1200 bps
;  101     2400 bps
;  110     4800 bps
;  111     9600 bps
;
; Bits    Function
; 3..4    parity
;   00    No parity
;   01    Odd parity
;   10    No parity
;   11    Even parity
;
; Bits    Function
;    2    Stop bits
;    0    One stop bit
;    1    Two stop bits
;
; Bits    Function
; 0..1    Character Size
;   10    7 bits
;   11    8 bits
;
; Although the standard PC serial port hardware supports 19200 bps
; some BIOSes may not support this speed.
;
; Example: COM1,2400,no parity,8data,1stop
;
;                mov     ah, 0           ;Initialize opcode
;                mov     al, 10100111b   ;Parameter data.
;                mov     dx, 0           ;COM1: port.
;                int     14h
;
; -----------------------------------------------------

; reg al: character to transmit
ch_put:
     pusha ; saves all the registers
     mov     dx, 0           ; Select COM1:
     mov     ah, 0x01        ; serial BIOS service (request, putch)
     int     0x14            ; serial BIOS service (exec)
;    test    ah, 0x80h       ; Check for error
;    jnz     SerialError
     popa ; restores all the registers
     ret



; ----------------------------------------------------------------
;
; Serial Port Status
; ==================
;
; ah=0x03      ; serial BIOS service (request, status)
; This call returns status information about the serial port
; including whether or not an error has occurred
; if a character has been received in the receive buffer
; if the transmit buffer is empty
; other pieces of useful information
; On entry into this routine, the dx register contains the serial port number
; On exit, the ax register contains the following values:
;
;  reg ax:     Bit Meaning
;  =================================================
;  bit.15      Time out error
;  bit.14      Transmitter shift register empty
;  bit.13      Transmitter holding register empty
;  bit.12      Break detection error
;  bit.11      Framing error
;  bit.10      Parity error
;  bit.09      Overrun error
;  bit.08      Data available
;  bit.07      Receive line signal detect
;  bit.06      Ring indicator
;  bit.05      Data set ready (DSR)
;  bit.04      Clear to send (CTS)
;  bit.03      Delta receive line signal detect
;  bit.02      Trailing edge ring detector
;  bit.01      Delta data set ready
;  bit.00      Delta clear to send

The UART has been available since the early 1980s, constantly refined rather than reinvented, it has shown little change over the years in its general mode of operation, but modern UARTs like 16450 and 16550 are traceable to early classics like 8250 included on the original IBM PC motherboard to provide communications with modems and serial printers.

The original IBM PC introduced BIOS functions to initialize (max 9600bps) and transfer/receive char.

I am not an expert in BIOS programming (x86/16bit mode), but the above code should be much simpler than the previous uart-chip direct approach :o :o :o
« Last Edit: May 04, 2023, 10:32:52 pm by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #7 on: May 04, 2023, 10:29:11 pm »
There's a couple problems in the loop that waits for THR empty:

Code: [Select]
                mov cx, ax
                in al, dx                ; <--- al is trashed so you have lost the character you want to transmit
                test cx, 0x20            ; <--- cx doesn't have the serial status register's value - al does.    //

"mov cx, ax" saves ax, ax is { ah:al }  :D
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline mwb1100

  • Frequent Contributor
  • **
  • Posts: 450
  • Country: us
Re: x86 assembly, uart-16450, simple putch
« Reply #8 on: May 05, 2023, 12:22:18 am »
"mov cx, ax" saves ax, ax is { ah:al }  :D

Yes, but:

Code: [Select]
ch_put_wait:
                mov dx, serial_status    ; Line Status Register
                mov cx, ax          ; /* 3 */
                in al, dx           ; /* 1 */
                test cx, 0x20         
                jz ch_put_wait      ; /* 2 */

  - the "in al, dx" at /* 1 */ modifies AL
  - if the "jz ch_put_wait" at /* 2 */ results in a jump (which isn't necessarily determined by the read of the status register, since that's in AL not CX), then
  - the "mov cx, ax" at /* 3 */ saves into CX an AL register that has been changed by the instruction at /* 1 */ from the previous loop iteration
 
The following users thanked this post: DiTBho

Offline mwb1100

  • Frequent Contributor
  • **
  • Posts: 450
  • Country: us
Re: x86 assembly, uart-16450, simple putch
« Reply #9 on: May 05, 2023, 12:29:34 am »
I'd suggest a wait loop like:

Code: [Select]
ch_put_wait:
                mv cx, ax     ; save ax
                mov dx, serial_status    ; Line Status Register
ch_put_wait_loop:
                in al, dx
                test al, 0x20         
                jz ch_put_wait_loop
               
                mov ax, cx    ; restore ax
 
The following users thanked this post: DiTBho

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #10 on: May 05, 2023, 07:29:40 am »
This code is still not working, but I am now 100% sure it's a hardware problem with the serial of my net5501, and at least I know I'd better try with on my T23 old IBM laptop.


Anyway, see the silly bug? This is what we call "mind vision": you look at the code, but you don't look at the actual code, but rather what your mind has placed on your eyes.

x86-assembly is terrible, especially if you are used to programming m68k or mips, architectures that have much more orthogonality.

With the x86/16bit mode, these only accept al as a target
Code: [Select]
; in al,imm8      Input byte from imm8 I/O port address into al
; in ax,imm8      Input byte from imm8 I/O port address into ax
; in al,dx        Input byte from I/O port in dx into al
; in ax,dx        Input word from I/O port in dx into ax
and that's the reason behind that wrong line of code: I wrote it initially as
Code: [Select]
                in cx, dx
                test cx, 0x20       
but it's illegal x86 code, so I fixed the first line, and forgot the second line.

And behind the bug in the code itself, lurks the most powerful bug of all: the one with how human beings interpret reality, something wrong pre-polarized in the brain that makes the difference between what the brain wants to see rather than what it actually sees.



Thank you guys  :-+ :-+ :-+
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #11 on: May 06, 2023, 11:09:15 am »
So, yesterday I installed qemu-i386 and I am simulating the serial port on tty/stdio
Code: [Select]
qemu running ...
CPU in Real Mode

hAllo
confirmed: the put_ch code does work!

This morning I looked at the physical port of the net5511 and found the ground pin of the serial0 was floating, causing bad contact. By re-soldering the pin with fresh lead it's not perfectly working.

Solved  :-+ :-+ :-+
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 
The following users thanked this post: mwb1100

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4164
  • Country: us
Re: x86 assembly, uart-16450, simple putch
« Reply #12 on: May 14, 2023, 02:14:25 am »
Quote
                mov    dx, serial_cmd3
Does whatever assembler you're using interpret that at "move immediate value (address of serial_cmd30 into dx", or is it going to load dx with the contents of that address?  MOV on x86 could be one of about a dozen different opcodes.


The intel assembler would probably want "serial_cmd3" to be strongly typed (strongly typed assembler.  Hmmph.)
I'm not sure what gas does (guessing that you're using gas, based on the .s file extension.)
(and wait - isn't the operand order wrong?  Shouldn't it be more like:
Code: [Select]
  mov $serial_cmd3, dx(this probably means you're using some other assembler...)
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #13 on: May 14, 2023, 11:08:12 am »
as=nasm

Code: [Select]
serial_base     equ (0x3f8)
serial_cmd0     equ (serial_base + 0)
serial_cmd1     equ (serial_base + 1)
serial_cmd2     equ (serial_base + 2)
serial_cmd3     equ (serial_base + 3)
serial_cmd4     equ (serial_base + 4)
serial_status   equ (serial_base + 5)
Code: [Select]
ch_put:
                pusha                    ; saves all registers
                mov cx, ax               ; save al, char to transmit
                ;------------------------------------------------------------------------
ch_put_wait:
                mov dx, serial_status    ; Line Status Register
                in al, dx
                test al, 0x20            ; 0010_0000b, use bit 5 to see if THR is empty
                jz ch_put_wait
                ;------------------------------------------------------------------------
                mov ax, cx               ; restore al, char to transmit
                mov dx, serial_cmd0      ; Transmitter Holding Register
                out dx, al               ; tramsmit(char)

                popa                     ; restore all registers
                ret

this works with nasm

The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #14 on: May 14, 2023, 11:17:06 am »
Code: [Select]
dev-lang/nasm-v2.15.05
      Homepage:      [url]https://www.nasm.us/[/url]
      Description:   groovy little assembler
      License:       BSD-2
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 5792
  • Country: fi
    • My home page and email address
Re: x86 assembly, uart-16450, simple putch
« Reply #15 on: May 14, 2023, 04:51:37 pm »
If I may, assuming this is protected mode 32-bit code,
Code: [Select]
SERIAL_BASE             EQU     (0x3F8)
SERIAL_THR              EQU     (SERIAL_BASE+0)         ; Transmit Hold Register (w)
SERIAL_RBR              EQU     (SERIAL_BASE+0)         ; Receive Buffer Register (r)
SERIAL_LSR              EQU     (SERIAL_BASE+5)         ; Line Status Register (r)

SERIAL_IS_DATA_READY    EQU     (0x01)
SERIAL_IS_OVERRUN       EQU     (0x02)
SERIAL_IS_PARITY_ERROR  EQU     (0x04)
SERIAL_IS_FRAMING_ERROR EQU     (0x08)
SERIAL_IS_BREAK         EQU     (0x10)
SERIAL_IS_THR_EMPTY     EQU     (0x20)
SERIAL_IS_EMPTY         EQU     (0x40)
SERIAL_IS_FIFO_ERROR    EQU     (0x80)


        ; Character to be sent in al
        ; Clobbers ah
serial_putc:
        push    edx
        mov     ah, al
        movzx   edx, SERIAL_LSR

.serial_putc_wait:
        in      al, dx
        test    al, SERIAL_IS_THR_EMPTY
        jz      .serial_putc_wait

        movzx   edx, SERIAL_THR
        mov     al, ah
        out     dx, al
        pop     edx
        ret


        ; Character to be sent in al
        ; Carry flag is clear if nothing sent, set if character sent
        ; Clobbers ah
serial_putc_nowait:
        push    edx
        mov     ah, al
        movzx   edx, SERIAL_LSR
        in      al, dx
        test    al, SERIAL_IS_THR_EMPTY
        mov     al, ah
        jnz     .serial_putc_nowait_send
        pop     edx
        clc
        ret

.serial_putc_nowait_send:
        movzx   edx, SERIAL_THR
        out     dx, al
        pop     edx
        stc
        ret
The serial_putc_nowait is useful, if you have a control loop and don't want to set up the serial interrupt.
When you have a string to be sent, just load the next character to al, and call serial_putc_nowait.
If carry flag is set, it was sent; otherwise a previous transmission is still in progress and carry flag is clear.
If you loaded al using a pointer, you can simply add-with-carry zero to the pointer register after the call (often esi, i.e: call serial_putc_nowait followed by adc esi, 0), and save the pointer value.  Just remember to check if al is zero (end of string) before calling serial_putc_nowait.

As usual, labels starting with . are local (and re-definable), and won't be added to the symbol table.
 
The following users thanked this post: DiTBho

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #16 on: May 14, 2023, 08:31:17 pm »
If I may, assuming this is protected mode 32-bit code

real-mode, because it's what the PC_BIOS initializes.

The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 5792
  • Country: fi
    • My home page and email address
Re: x86 assembly, uart-16450, simple putch
« Reply #17 on: May 14, 2023, 09:29:40 pm »
If I may, assuming this is protected mode 32-bit code

real-mode, because it's what the PC_BIOS initializes.
Ah, so make that
Code: [Select]
SERIAL_BASE             EQU     (0x3F8)
SERIAL_THR              EQU     (SERIAL_BASE+0)         ; Transmit Hold Register (w)
SERIAL_RBR              EQU     (SERIAL_BASE+0)         ; Receive Buffer Register (r)
SERIAL_LSR              EQU     (SERIAL_BASE+5)         ; Line Status Register (r)

SERIAL_IS_DATA_READY    EQU     (0x01)
SERIAL_IS_OVERRUN       EQU     (0x02)
SERIAL_IS_PARITY_ERROR  EQU     (0x04)
SERIAL_IS_FRAMING_ERROR EQU     (0x08)
SERIAL_IS_BREAK         EQU     (0x10)
SERIAL_IS_THR_EMPTY     EQU     (0x20)
SERIAL_IS_EMPTY         EQU     (0x40)
SERIAL_IS_FIFO_ERROR    EQU     (0x80)


        ; Character to be sent in al
        ; Clobbers ah
serial_putc:
        push    dx
        mov     ah, al
        mov     dx, SERIAL_LSR

.serial_putc_wait:
        in      al, dx
        test    al, SERIAL_IS_THR_EMPTY
        jz      .serial_putc_wait

        mov     dx, SERIAL_THR
        mov     al, ah
        out     dx, al
        pop     dx
        ret


        ; Character to be sent in al
        ; Carry flag is clear if nothing sent, set if character sent
        ; Clobbers ah
serial_putc_nowait:
        push    dx
        mov     ah, al
        mov     dx, SERIAL_LSR
        in      al, dx
        test    al, SERIAL_IS_THR_EMPTY
        mov     al, ah
        jnz     .serial_putc_nowait_send
        pop     dx
        clc
        ret

.serial_putc_nowait_send:
        mov     dx, SERIAL_THR
        out     dx, al
        pop     dx
        stc
        ret

In NASM syntax, raw numbers are literals.  (A $ prefix does nothing; $0x3F8 is exactly the same thing as 0x3F8 or 1016.)

Memory access is always in square brackets, so for example [0x3F8] refers to the contents of address 0x3F8, and [dx] the contents at address pointed to by the dx register.
 
The following users thanked this post: DiTBho

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4164
  • Country: us
Re: x86 assembly, uart-16450, simple putch
« Reply #18 on: May 15, 2023, 01:05:52 am »
Quote
[size=0px][dx][/size][/size][size=0px] the contents at address pointed to by the dx register.[/size]
But it's not "in al, [dx]" ?  :-)


Thanks for the explanations, although I'm not entirely happy that there are apaprently three common ASM syntaxes for x86 these days!  (?)  (hmm.  online says that NASM does use the Intel syntax.  But it doesn't look much like the MASM code I used to write; wasn't MASM "real" Intel syntax?)
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #19 on: May 15, 2023, 11:16:56 am »
But it's not "in al, [dx]" ?  :-)

z80, 8080 and x86 have two spaces: { port_IO, mem }

{ in, out } instructions serve port_IO

"in y, x" can target to reg, read data from IO_port(x), and put it into reg.y
"out x, y" can source data from a reg, read reg.y, and put it into IO_port(x)

three common ASM syntaxes for x86 these days

eh, many more for 68k and 68hc11  ;D
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 5792
  • Country: fi
    • My home page and email address
Re: x86 assembly, uart-16450, simple putch
« Reply #20 on: May 15, 2023, 04:44:35 pm »
Quote
[size=0px][dx][/size][/size][size=0px] the contents at address pointed to by the dx register.[/size]
But it's not "in al, [dx]" ?  :-)
No, because the I/O port space on x86 is special, like DiTBho said above.  The angle bracket notation applies exclusively to memory addresses.

You can only read/write to/from I/O ports from/to AL (8-bit) or AX (16-bit) register (or EAX on 32-bit mode).
You can use an immediate byte to access the first 256 ports, but for ports 256-65535, you have to use the DX register.
See in, out.

This is the same in NASM, MASM, and TASM; i.e., this is the Intel syntax.

In 16-bit mode on 8086, the angle bracket notation is very limited:
  • [constant] for exact address
  • [register] for address specified in register SI, DI, or BX
  • [register+constant] for address specified in register SI, DI, BX, or BP plus a fixed offset
  • [register1+register2] for address as a sum of registers, register1 being either BX or BP, and register2 either SI or DI
  • [register1+register2+constant] for address as a sum of registers plus a fixed constant, register1 being either BX or BP, and register2 either SI or DI
The string instructions (LODSB, LODSW, STOSB, STOSW, MOVSB, MOVSW, CMPSB, CMPSWSCASB, and SCASW) use intrinsic addressing (load/compare/scan from DS:SI, store to ES:DI), auto-incrementing (if D flag clear) or auto-decrementing (if D flag set) the registers (SI and/or DI) afterwards.  For 80186 and later processors in 16-bit mode, add INSB, INSW, OUTSB, and OUTSW for I/O port string instructions; the port number in DX stays unchanged.  Some assemblers allow a correct-looking parameter, but don't even check it, leading to confusion since the parameter may not match what the instruction does.  Essentially, ignore all parameters for these instructions!

The REP instruction prefix repeats the following instruction CX register value number of times.  The prefixes REPZ/REPE, REPNZ/REPNE repeat the following instruction as long as Z flag is set or clear, respectively.  They are only reliable across processor models with the string instructions, as the microcode often uses the REP prefix bit for other uses for non-string instructions.
« Last Edit: May 15, 2023, 04:47:44 pm by Nominal Animal »
 

Offline DiTBhoTopic starter

  • Super Contributor
  • ***
  • Posts: 3555
  • Country: gb
Re: x86 assembly, uart-16450, simple putch
« Reply #21 on: May 15, 2023, 05:13:23 pm »
I usually use GNU binutils/{ as, ld, * } for { 68k, 68hc11, mips, powerpc }
and the Avocet ProTools line of Compilers, Assemblers, and Simulators for 64-bit { mips64, RISC-V }

So why "nasm" for x86?  :o :o :o

well, I don't like x86 asm programming, I need to prepare a special bootloader and nasm offers this

Code: [Select]
; 16 bit app
; -----------------------------------------------------------------------------
[bits 16]
[org 0x7c00] ; boot sector startup
begin_16bit:
            mov bp, stack_base         ; base of the stack
            mov sp, stack_base         ; top  of the stack

            call console_init

            mov bx, msg_16bit_mode
            call print16

...


; 32 bit app
; -----------------------------------------------------------------------------
[bits 32]
begin_32bit:
            mov ebx, msg_32bit_mode
            call print32

            call app_start         ; Give control to the app
            jmp $                  ; halt if app returns control

...

; padding
; -----------------------------------------------------------------------------
; The first 512 bytes of a disk are known as the bootsector
; The boot sector is an area of the disk reserved for booting purposes
; If the bootsector of a disk contains a valid boot sector
; the last word of the sector must contain the signature 0xaa55
; then the disk is treated by the BIOS as bootable.
; -----------------------------------------------------------------------------
times 510 - ($-$$) db 0
dw 0xaa55

so "nasm" because for this specific need, these nasm tricks make it simpler than with g/as  ;D
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 5792
  • Country: fi
    • My home page and email address
Re: x86 assembly, uart-16450, simple putch
« Reply #22 on: May 15, 2023, 08:27:36 pm »
You can also supply -masm=intel to gcc to use Intel syntax in assembly output (-S) and in extended inline assembly (asm volatile ("instructions" : outputs : inputs : clobbers);).

Similarly, with GNU as (part of binutils), you can use .intel_syntax noprefix at the beginning of the source file, to use Intel syntax.

I don't remember exactly which versions of as and gcc started supporting these, though.  They aren't new, but they were not initially supported either.

To get the same machine code from as as my previous listing produces using nasm, you do need slight changes:
Code: [Select]
    .arch           i386
    .intel_syntax   noprefix
    .code16

    .set    SERIAL_BASE,            (0x3F8)             # Base I/O
    .set    SERIAL_THR,             (SERIAL_BASE+0)     # Transmit Hold Register (w)
    .set    SERIAL_LSR,             (SERIAL_BASE+5)     # Line Status Register (r)
    .set    SERIAL_IS_THR_EMPTY,    (0x20)


    # Character to be sent in al
    # Clobbers ah
    .global serial_putc
serial_putc:
    push    dx
    mov     ah, al
    mov     dx, SERIAL_LSR

serial_putc_wait:
    in      al, dx
    test    al, SERIAL_IS_THR_EMPTY
    jz      serial_putc_wait

    mov     dx, SERIAL_THR
    mov     al, ah
    out     dx, al
    pop     dx
    ret


    # Character to be sent in al
    # Carry flag is clear if nothing sent, set if character sent
    # Clobbers ah
    .global serial_putc_nowait
serial_putc_nowait:
    push    dx
    mov     ah, al
    mov     dx, SERIAL_LSR
    in      al, dx
    test    al, SERIAL_IS_THR_EMPTY
    mov     al, ah
    jnz     serial_putc_nowait_send
    pop     dx
    clc
    ret

serial_putc_nowait_send:
    mov     dx, SERIAL_THR
    out     dx, al
    pop     dx
    stc
    ret
To switch to 32-bit code generation, use .code32 directive.
You can compile the above using as source.s -o target.o.

The main differences are that dot (.) starts a directive, and # a comment; ; is just a separator like newline.  All symbols are local by default, so you need to declare the global ones.  Constants are defined using .set NAME, value instead of EQU.
 
The following users thanked this post: DiTBho

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 13519
  • Country: fr
Re: x86 assembly, uart-16450, simple putch
« Reply #23 on: May 15, 2023, 08:49:33 pm »
NASM is way better than gas if you actually write assembly yourself.
 
The following users thanked this post: DiTBho

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 5792
  • Country: fi
    • My home page and email address
Re: x86 assembly, uart-16450, simple putch
« Reply #24 on: May 16, 2023, 12:08:01 am »
NASM is way better than gas if you actually write assembly yourself.
Agreed.  If I was writing a bootloader or similar freestanding code in assembly for 16/32-bit x86, I'd use NASM too, and not GNU as.
I just wanted to show that this is possible to do also in (recent enough) GNU as; NASM isn't explicitly required here.

After switching to 32-bit mode, things become much more interesting, since there are a few function attributes that GCC and Clang support for the calling convention; the default "everything on stack" is pretty annoying in my opinion.  Basically, if you have a function that takes its parameters in eax, edx, and ecx registers (in that order), you can just declare the prototype with __attribute__((regparm (3)), and the compiler will pass the parameters in said registers in calls to that function iff the prototype is visible.
 
The following users thanked this post: DiTBho


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf