Products > Programming

x86 assembly, uart-16450, simple putch

<< < (6/6)

westfw:

--- Quote ---NASM is way better than gas if you actually write assembly yourself.
--- End quote ---
I have no doubt.  gas is mostly for use by the compiler, with perhaps its only advantage for a programmer being that you maintain a lot of pseudo-ops and cli properties across many different architectures.


Sometimes I think it's been made intentionally ugly to discourage assembly language programming.  "@" for a comment character (ARM)?  What were they thinking?

DiTBho:
LOL, crazy, see this trick


--- Code: ---; -----------------------------------------------------------------------------
; console_out the EOS terminated string which follows the call
;    eg:
;            call say
;            db "hAllo", EOS
; -----------------------------------------------------------------------------
say:
                push    bp
                mov     bp,sp
                push    ds
                push    si
                push    ax
                mov     si, [bp+2]
                push    cs
                pop     ds
say_body:
                lodsb               ; Load byte at address DS:SI into AL.
                cmp al, EOS
                je      say_done
                call    ch_put
                jmp     say_body
say_done:
                mov     [bp+2], si
                pop     ax
                pop     si
                pop     ds
                pop     bp
                ret

--- End code ---

unbelievable that you can easily mix code and data this wild way, but it actually works!!!  :o :o :o

ok, seriously it takes too many opcode, let's implement the classic print_string with a string pointer as argument

DiTBho:
(
you can't do anything like this with a MIPS

unless you force strict align=4 for the string (zero padding string_size mod 4)
and there are some implementations that physically separate data from code

x86 is a very primitive beast :o :o :o
)

Nominal Animal:

--- Quote from: DiTBho on May 16, 2023, 06:42:54 am ---LOL, crazy, see this trick
--- End quote ---
At the time when 8086/80186/80286-class machines had less than 640kB of RAM, it was common for games to use self-modifying code.
Starflight (1986) used this heavily, including storing the modifications on-disk: to restart the game from scratch, you had to reinstall it (create new copies of the game disks)!


--- Quote from: DiTBho on May 16, 2023, 06:42:54 am ---ok, seriously it takes too many opcode, let's implement the classic print_string with a string pointer as argument

--- End quote ---

Well, this takes 33 bytes, noting that now the character to be sent is in AH (and not AL) register,

--- Code: ---        [bits 16]

        EOS                     EQU     (0)

        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 AH
        ; Clobbers AL
serial_putc:
        push    dx
        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


        ; String to be sent in DS:SI
        ; SI will point to EOS
serial_puts:
        push    ax

.serial_puts_next:
        mov     ah, [si]
        cmp     ah, EOS
        je      .serial_puts_done
        call    serial_putc
        inc     si
        jmp     .serial_puts_next

.serial_puts_done:
        pop     ax
        ret

--- End code ---

but if you don't need the single-character one, this takes only 28 bytes:

--- Code: ---        [bits 16]

        EOS                     EQU     (0)

        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)

        ; String to be sent in DS:SI
        ; SI will point to EOS
serial_puts:
        push    ax
        push    dx

.serial_puts_next:
        mov     ah, [si]
        cmp     ah, EOS
        jnz     .serial_puts_one
        pop     dx
        pop     ax
        ret

.serial_puts_one:
        inc     si
        mov     dx, SERIAL_LSR

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

        mov     dx, SERIAL_THR
        mov     al, ah
        out     dx, al
        jmp     .serial_puts_next

--- End code ---
Often, preserving AX isn't useful, so if you let it clobber that too, omit the push/pop AX, and it'll shrink to just 26 bytes.

The reason I didn't use lodsb in either, is because lodsb; mov ah, al and mov ah, [si] ; inc si are both 3 bytes, but the former depends on the direction flag (D), and the latter does not.

If you look at the compare-to-EOS parts, you'll find that adding other characters for true printf-like functionality (in a separate function!) isn't too difficult.  I just recommend that you make it caller-cleanup (i.e., you only look at the parameters on stack, not pop them off).  For integers, I recommend you use the slow DIV/IDIV-by-radix method (with value in DX*65536+AX), constructing it from right to left in a temporary buffer on stack (16 chars including EOS suffices for 32-bit integers in octal, decimal, and hexadecimal).

Navigation

[0] Message Index

[*] Previous page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod