Search code examples
assemblyiox86x86-64nasm

What does REP INSW do?


I need to read some 16-bit values from the port and save them to the buffer. Tutorial which I'm using suggest using REP INSW instruction, but I don't know how to use it and even how it works...

Is this instruction equivalent to two IN instructions?


Solution

  • The answer is in the Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 1:

    Section 18.3 I/O ADDRESS SPACE

    Any two consecutive 8-bit ports can be treated as a 16-bit port, and any four consecutive ports can be a 32-bit port. In this manner, the processor can transfer 8, 16, or 32 bits to or from a device in the I/O address space. Like words in memory, 16-bit ports should be aligned to even addresses (0, 2, 4, ...) so that all 16 bits can be transferred in a single bus cycle. Likewise, 32-bit ports should be aligned to addresses that are multiples of four (0, 4, 8, ...). The processor supports data transfers to unaligned ports, but there is a performance penalty because one or more extra bus cycle must be used.

    If you review the documentation in the Instruction Set reference in Volume 2A for the INSW instruction it says this:

    INS/INSB/INSW/INSD — Input from Port to String

    INS m16, DX        Input word from I/O port specified in DX into memory location
                       specified in ES:(E)DI or RDI.
    

    INSW reads a 16-bit value from IO port space to the specified memory address. INSW will read a byte starting from the specified PORT specified in DX and an additional byte from PORT+1 and the bytes are stored at ES:E(DI) and ES:E(DI)+1 respectively.

    To answer the question about REP INSW, The REP will repeat this process for the number of times specified in E(CX). REP INSW is documented as:

    REP/REPE/REPZ/REPNE/REPNZ — Repeat String Operation Prefix

    REP INS m16, DX    Input (E)CX words from port DX into ES:[(E)DI]
    

    If you useREP INSW to read 16-bits from port 0x1F0 E(CX) times then effectively you do:

    1. Read a 16-bit value from 0x1F0 and 0x1F1 and store them to memory address pointed to by ES:(E)DI and ES:(E)DI+1.
    2. Increment E(DI) by 2.
    3. Decrement E(CX) by 1.
    4. If E(CX) is not 0 go to step 1 otherwise process is finished.

    Assuming you are writing 32-bit code then to read 16-bits from port 0x1F0/0x1F1 16 times you would code this in NASM as:

    bits 32
        ; It is assumed that ES is already set and depends on the environment
        mov dx, 0x1f0              ; Read words from port 0x1F0 and 0x1F1
        mov edi, buffer            ; Address of BUFFER
        mov ecx, 16                ; Repeat 16 times
        rep insw
    
    ; Other code and data here
    
    ; 16 word buffer (total 32 bytes)
    buffer: TIMES 16 dw 0x0000
    

    If writing 16-bit code it would look something like:

    bits 16
        ; It is assumed that ES is already set and depends on the environment
        mov dx, 0x1f0              ; Read words from port 0x1F0 and 0x1F1
        mov di, buffer             ; Address of BUFFER
        mov cx, 16                 ; Repeat 16 times
        rep insw
    
    ; Other code and data here
    
    ; 16 word buffer (total 32 bytes)
    buffer: TIMES 16 dw 0x0000
    

    Notes

    • It is assumed that you are running in an environment where you have the privilege level to use port IO instructions and you have permission to read/write specific IO ports. In real-mode there are no restrictions. In protected-mode, long-mode, and v8086 mode you may or may not have the privilege level to use port instructions or permission to access specific ports. You will have to consult your operating system (OS) documentation, or if you are writing your own OS then you must set the privilege and port access restrictions yourself using privilege levels / IOPL / IOPL bitmaps.

    • rep insw can also be used in 64-bit mode using an operand size prefix (NASM will handle the encoding by adding the 0x66 prefix). The count will be in RCX, and the ES segment is not applicable:

      REP INS r/m32, DX     Input RCX default size from port DX into [RDI].