Github copilot: Assembly VGA XOR pattern

what can you do?

I can assist you with a variety of programming tasks, including providing code examples, explaining algorithms, and helping with GitHub-related queries such as setting up CI/CD pipelines, managing repositories, and more. If you have a specific question or need help with a coding issue, feel free to ask!


write code for a COM executable (flat binary) in real mode x86 assembly, that switches to VGA mode 320x200 256 colors, and draws a full screen XOR pattern. When done drawing, the program should wait for ESC to be pressed, to exit.

Sure! Below is an example of a real mode x86 assembly program that accomplishes this. It switches to VGA mode 0x13 (320x200 with 256 colors), draws an XOR pattern on the screen, and waits for the ESC key to be pressed before exiting. This program is written for the NASM assembler.

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Draw XOR pattern
    xor di, di             ; DI = 0 (offset in video memory)
    mov dx, 320            ; Screen width in pixels
    mov cx, 200            ; Screen height in pixels

draw_pattern:
    push cx                ; Save CX (vertical counter)
    mov cx, dx             ; Reset CX (horizontal counter)
draw_row:
    mov al, di             ; AL = (XOR pattern)
    mov [0xA000:di], al    ; Write AL to video memory
    inc di                 ; Move to next pixel
    loop draw_row          ; Repeat for full row

    pop cx                 ; Restore CX (vertical counter)
    loop draw_pattern      ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01           ; Check for key press
    int 0x16
    jz wait_for_esc        ; If no key pressed, keep checking

    mov ah, 0x00           ; Get key press
    int 0x16
    cmp al, 0x1B           ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc       ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03           ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00         ; Terminate program
    int 0x21

times 510-($-$$) db 0      ; Pad to 510 bytes
dw 0xAA55                  ; Boot signature

Explanation:

Set Video Mode:

Draw XOR Pattern:

Wait for ESC:

Exit:

Assembling and Running:

To assemble and run this program, use NASM and an emulator like DOSBox:

Save the code to a file named xor_pattern.asm. Assemble the code with NASM: nasm -f bin xor_pattern.asm -o xor_pattern.com

Run the program in DOSBox:

dosbox xor_pattern.com

This will display the XOR pattern and wait for you to press ESC to exit.


doesn't build. nasm complains about an invalid segment override in line 21. A segment register should be used.

You are correct. The segment override in line 21 is not valid. We need to use a segment register for accessing the VGA memory. The correct segment for VGA memory is 0xA000. Here is the corrected code:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    mov dx, 320          ; Screen width in pixels

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, dx           ; Reset CX (horizontal counter)
draw_row:
    mov al, di           ; AL = (XOR pattern)
    stosb                ; Write AL to [ES:DI] and increment DI
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

times 510-($-$$) db 0    ; Pad to 510 bytes
dw 0xAA55                ; Boot signature

Explanation of the Changes:

Setting the VGA Segment:

Writing to Video Memory:

This will display the XOR pattern and wait for you to press ESC to exit.


still wrong, on line 22, you're trying to move di (a 16bit register) to al which is 8bits wide. Assuming the rest of the logic is correct, It should be moved to ax instead, taking care to preserve ah if it has something useful at that point.

You are correct, and I apologize for the oversight. We need to ensure that we handle the 16-bit to 8-bit register correctly. We should move the value from di to ax and then copy the lower byte of ax to al. Here is the corrected code:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    mov dx, 320          ; Screen width in pixels

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, dx           ; Reset CX (horizontal counter)
draw_row:
    mov ax, di           ; Move DI to AX
    mov al, ah           ; Move the higher byte of AX to AL (XOR pattern)
    stosb                ; Write AL to [ES:DI] and increment DI
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

times 510-($-$$) db 0    ; Pad to 510 bytes
dw 0xAA55                ; Boot signature

Explanation of the Changes:

Handling 16-bit to 8-bit Register:

This will display the XOR pattern and wait for you to press ESC to exit.


this version builds, but fails to produce a xor pattern.

Thank you for the feedback. Let's correct the logic to ensure the XOR pattern is displayed correctly. The issue might be with how the XOR pattern is being calculated and displayed. We can simplify the logic by properly calculating the XOR pattern based on the x and y coordinates. Here is the updated code:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    xor bx, bx           ; BX will hold the y coordinate

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, 320          ; Screen width in pixels
    xor dx, dx           ; DX will hold the x coordinate
draw_row:
    mov al, bl           ; AL = y coordinate (BL)
    xor al, dl           ; AL = AL XOR x coordinate (DL)
    stosb                ; Write AL to [ES:DI] and increment DI
    inc dx               ; Increment x coordinate
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    inc bx               ; Increment y coordinate
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

times 510-($-$$) db 0    ; Pad to 510 bytes
dw 0xAA55                ; Boot signature

Explanation of the Changes:

Calculate XOR Pattern:

Coordinate Management:

This should correctly display the XOR pattern and wait for you to press ESC to exit.


The logic seems correct, but it doesn't look right because the default VGA palette is not a single linear color ramp, but rather broken into incoherent sections. Add a piece of code at the start, to initialize the palette to a single smooth gradient from black to white.

Sure, I can add a piece of code to initialize the VGA palette to create a smooth gradient from black to white. The VGA palette consists of 256 entries, each entry containing a color defined by its red, green, and blue components.

Here is the updated assembly program that sets the VGA palette to a smooth gradient from black to white and then draws the XOR pattern:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Set up VGA palette to a smooth gradient from black to white
    mov dx, 0x03C8       ; VGA DAC write index register
    xor al, al           ; Start from color index 0
    out dx, al
    inc dx               ; VGA DAC data register
    mov cx, 256          ; 256 colors
set_palette:
    mov ax, cx           ; Copy CX to AX
    shr ax, 2            ; Divide by 4 (VGA DAC uses 6-bit values)
    mov al, ah           ; Set AL to the value of AH (gradient value)
    out dx, al           ; Output red component
    out dx, al           ; Output green component
    out dx, al           ; Output blue component
    loop set_palette

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    xor bx, bx           ; BX will hold the y coordinate

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, 320          ; Screen width in pixels
    xor dx, dx           ; DX will hold the x coordinate
draw_row:
    mov al, bl           ; AL = y coordinate (BL)
    xor al, dl           ; AL = AL XOR x coordinate (DL)
    stosb                ; Write AL to [ES:DI] and increment DI
    inc dx               ; Increment x coordinate
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    inc bx               ; Increment y coordinate
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

times 510-($-$$) db 0    ; Pad to 510 bytes
dw 0xAA55                ; Boot signature

Explanation of the Changes:

Set Up VGA Palette:

Drawing the XOR Pattern:

The logic for drawing the XOR pattern remains the same as previously described.


There are two problems with this palette initialization. You're overwriting the color value in al with ah on line 20, which is guaranteed to set it to 0. The useful part is already in al. The other issue is that you're decrementing the color value as you go. I asked for a gradient from black to white, not from white to black.

Thank you for pointing out the issues. Let's correct the code to ensure that the palette initialization creates a gradient from black to white and preserves the correct color values. Here is the corrected version:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Set up VGA palette to a smooth gradient from black to white
    mov dx, 0x03C8       ; VGA DAC write index register
    xor al, al           ; Start from color index 0
    out dx, al
    inc dx               ; VGA DAC data register
    mov cx, 256          ; 256 colors
    xor bx, bx           ; BX will hold the gradient value
set_palette:
    mov al, bl           ; Set AL to the gradient value
    shr al, 2            ; Divide by 4 (VGA DAC uses 6-bit values)
    out dx, al           ; Output red component
    out dx, al           ; Output green component
    out dx, al           ; Output blue component
    inc bx               ; Increment gradient value
    loop set_palette

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    xor bx, bx           ; BX will hold the y coordinate

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, 320          ; Screen width in pixels
    xor dx, dx           ; DX will hold the x coordinate
draw_row:
    mov al, bl           ; AL = y coordinate (BL)
    xor al, dl           ; AL = AL XOR x coordinate (DL)
    stosb                ; Write AL to [ES:DI] and increment DI
    inc dx               ; Increment x coordinate
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    inc bx               ; Increment y coordinate
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

times 510-($-$$) db 0    ; Pad to 510 bytes
dw 0xAA55                ; Boot signature

Explanation of the Changes:

Correct Gradient Calculation:

Increment Gradient Value:


this version works nicely, but why do you keep adding a 0xaa55 word at the end of the code?

The 0xAA55 word at the end of the code is typically used as a boot signature for bootable disk images. It indicates to the BIOS that the disk is bootable. However, for a COM executable, this is not required and can be safely removed.

Here's the cleaned-up version of the assembly code without the boot signature:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Set up VGA palette to a smooth gradient from black to white
    mov dx, 0x03C8       ; VGA DAC write index register
    xor al, al           ; Start from color index 0
    out dx, al
    inc dx               ; VGA DAC data register
    mov cx, 256          ; 256 colors
    xor bx, bx           ; BX will hold the gradient value
set_palette:
    mov al, bl           ; Set AL to the gradient value
    shr al, 2            ; Divide by 4 (VGA DAC uses 6-bit values)
    out dx, al           ; Output red component
    out dx, al           ; Output green component
    out dx, al           ; Output blue component
    inc bx               ; Increment gradient value
    loop set_palette

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    xor bx, bx           ; BX will hold the y coordinate

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, 320          ; Screen width in pixels
    xor dx, dx           ; DX will hold the x coordinate
draw_row:
    mov al, bl           ; AL = y coordinate (BL)
    xor al, dl           ; AL = AL XOR x coordinate (DL)
    stosb                ; Write AL to [ES:DI] and increment DI
    inc dx               ; Increment x coordinate
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    inc bx               ; Increment y coordinate
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

times 510-($-$$) db 0    ; Pad to 510 bytes

In this version:


isn't the 510 byte padding also unnecessary then?

Yes, you are correct. The 510-byte padding is also unnecessary for a COM executable. The padding was originally included because it is typically used for bootable disk images to ensure the code fits within a sector, but it is not required for a COM file.

Here's the updated version of the code without the unnecessary padding:

; XOR pattern in VGA mode 0x13 (320x200 256 colors)
section .text
    org 0x100

start:
    ; Set video mode to 0x13 (320x200 256 colors)
    mov ah, 0x00
    mov al, 0x13
    int 0x10

    ; Set up VGA palette to a smooth gradient from black to white
    mov dx, 0x03C8       ; VGA DAC write index register
    xor al, al           ; Start from color index 0
    out dx, al
    inc dx               ; VGA DAC data register
    mov cx, 256          ; 256 colors
    xor bx, bx           ; BX will hold the gradient value
set_palette:
    mov al, bl           ; Set AL to the gradient value
    shr al, 2            ; Divide by 4 (VGA DAC uses 6-bit values)
    out dx, al           ; Output red component
    out dx, al           ; Output green component
    out dx, al           ; Output blue component
    inc bx               ; Increment gradient value
    loop set_palette

    ; Draw XOR pattern
    mov ax, 0xA000       ; VGA segment
    mov es, ax
    xor di, di           ; DI = 0 (offset in video memory)
    mov cx, 200          ; Screen height in pixels
    xor bx, bx           ; BX will hold the y coordinate

draw_pattern:
    push cx              ; Save CX (vertical counter)
    mov cx, 320          ; Screen width in pixels
    xor dx, dx           ; DX will hold the x coordinate
draw_row:
    mov al, bl           ; AL = y coordinate (BL)
    xor al, dl           ; AL = AL XOR x coordinate (DL)
    stosb                ; Write AL to [ES:DI] and increment DI
    inc dx               ; Increment x coordinate
    loop draw_row        ; Repeat for full row

    pop cx               ; Restore CX (vertical counter)
    inc bx               ; Increment y coordinate
    loop draw_pattern    ; Repeat for full screen

    ; Wait for ESC key to be pressed
wait_for_esc:
    mov ah, 0x01         ; Check for key press
    int 0x16
    jz wait_for_esc      ; If no key pressed, keep checking

    mov ah, 0x00         ; Get key press
    int 0x16
    cmp al, 0x1B         ; Check if ESC key (ASCII 0x1B)
    jne wait_for_esc     ; If not ESC, keep checking

    ; Restore text mode and exit
    mov ah, 0x00
    mov al, 0x03         ; Text mode 80x25 16 colors
    int 0x10

    mov ax, 0x4C00       ; Terminate program
    int 0x21

Explanation:

Removed Padding: The padding to 510 bytes and the boot signature (0xAA55) at the end of the file have been removed, as they are not necessary for a COM executable.

This will display the XOR pattern with a smooth gradient from black to white and wait for you to press ESC to exit.