Search code examples
linuxassemblyx86-64yasm

How can I change the terminal color in 64-bit Linux when using YASM assembly?


Im trying to change the colour of the terminal based on a number of 1 to 5 inputed by the user but it just doesnt work.

Sorry i dont really understand assembly but need to do this for a project and cant understand what is wrong with it.

Its only supposed to change to this colors that i use in the code. It doesnt even reset the terminal color so i dont know how to make it work.

section .data
    reset_color db "\033[0m"  ; ANSI escape code to reset colors
    bg_red db "\033[41m"     ; ANSI escape code for red background color
    bg_green db "\033[42m"   ; ANSI escape code for green background color
    bg_yellow db "\033[43m"  ; ANSI escape code for yellow background color
    bg_blue db "\033[44m"    ; ANSI escape code for blue background color
    bg_magenta db "\033[45m" ; ANSI escape code for magenta background color

section .bss
    number resb 1            ; Variable to store the user-selected number

section .text
    global _start

_start:
    ; Reset terminal attributes
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, reset_color
    mov rdx, 4       ; length of the string
    syscall

    ; Display a prompt to enter a number
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, prompt
    mov rdx, prompt_len
    syscall

    ; Read the user input
    mov rax, 0       ; sys_read
    mov rdi, 0       ; stdin
    mov rsi, number
    mov rdx, 1       ; read one byte
    syscall

    ; Convert the input to a number
    movzx eax, byte [number]
    sub eax, '0'

    ; Change the background color based on the selected number
    cmp eax, 1
    je change_to_red
    cmp eax, 2
    je change_to_green
    cmp eax, 3
    je change_to_yellow
    cmp eax, 4
    je change_to_blue
    cmp eax, 5
    je change_to_magenta

    ; Invalid input, display an error message
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, error_message
    mov rdx, error_message_len
    syscall
    jmp exit_program

change_to_red:
    ; Change the background color to red
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, bg_red
    mov rdx, 5       ; length of the string
    syscall
    jmp exit_program

change_to_green:
    ; Change the background color to green
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, bg_green
    mov rdx, 5       ; length of the string
    syscall
    jmp exit_program

change_to_yellow:
    ; Change the background color to yellow
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, bg_yellow
    mov rdx, 5       ; length of the string
    syscall
    jmp exit_program

change_to_blue:
    ; Change the background color to blue
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, bg_blue
    mov rdx, 5       ; length of the string
    syscall
    jmp exit_program

change_to_magenta:
    ; Change the background color to magenta
    mov rax, 1       ; sys_write
    mov rdi, 1       ; stdout
    mov rsi, bg_magenta
    mov rdx, 5       ; length of the string
    syscall

exit_program:
    ; Exit the program
    mov rax, 60      ; sys_exit
    xor rdi, rdi     ; exit code 0
    syscall

section .data
    prompt db "Enter a number (1-5): ", 0
    prompt_len equ $-prompt

    error_message db "Invalid input!", 0
    error_message_len equ $-error_message

Solution

  • Like Jester showed in a comment, you can write that \033 as the literal number 27 in the db lines:

    section .data
        reset_color db 27, "[0m"  ; ANSI escape code to reset colors
        bg_color    db 27, "[4?m" ; ANSI escape code for ??? background color
                              ^
                               \ ? is a placeholder
    

    Your program will work then, but it still suffers from repeating itself!
    You can easily simplify this code enormously by using a placeholder in the escape sequence (I used the question mark). And once the user supplies a valid digit '1' through '5', you just copy it over the placeholder and output the lot. No need to first convert the digit character into its number value.

        ; Read the user input
        mov   eax, 0        ; sys_read
        mov   edi, 0        ; stdin
        mov   rsi, number
        mov   edx, 1        ; read one byte
        syscall
    
        ; Patch escape sequence and write it
        mov   rsi, error_message
        mov   edx, error_message_len
        movzx eax, byte [number]
        cmp   al, '1'
        jb    WriteMsg
        cmp   al, '5'
        ja    WriteMsg
        mov   rsi, bg_color
        mov   edx, 5        ; length of the string
        mov   [rsi + 3], al ; This overwrites the placeholder
    
        ; Changes the background color OR displays error message
    WriteMsg:
        mov   eax, 1        ; sys_write
        mov   edi, 1        ; stdout
        syscall
    
    exit_program:
        ; Exit the program
        mov   eax, 60       ; sys_exit
        xor   edi, edi      ; exit code 0
        syscall