Add comments to x86 assembly language program

Add Comments to x86 Assembly Language Program
 

Assembly Programming

Example 1:

Do not say that a function “computes the result of a1*b1 and then adds to it the result of
a2*b2”. Instead say that the function “computes the vector product of A =[a1 a2] and B=[b1 b2]T”.

Example 2:

Do not say that “EAX is XORed with itself”. Instead say that “EAX is zeroed efficiently”.
With that said, you are encouraged to try to restore the [REDACTED] comments on the code. Not allof them were particularly informative initially, but obviously some of them shed light into non-obviousmanipulations. If you can understand said manipulations, restoring the comment will speed up yourunderstanding of the full code.
With the exception of lol, top, kek, and magic, all labels are “meaningful”, well, to me, at least!
Nothing in the source code is false nor deliberately misleading.

Jorge_TTT_sanitized.asm

%define cmpb      cmp byte

%define decb      dec byte

%define incb      inc byte

%define xorb      xor byte

%define movb      mov byte

%define movq      mov qword

section .bss                              ; Uninitialized data

buffer            resb  4

pfunc       resq  1

section .data                             ; Initialized data

mdbg        db          “Do you want to enable debug information?”, 0xA

mdbgl       equ         $ – mdbg

mPXO        db          “Player X”

mPXOl       equ         $ – mPXO

mplay2            db          “, choose your location (0-8):”, 0xA, “Current board:”, 0xA

mplay2l           equ         $ – mplay2

mwin2       db          ” wins!”, 0xA

mwin2l            equ         $ – mwin2

spot        equ         7           ; [REDACTED]

magic       equ         0x17  ; Magic! =D

mtie        db          “It’s a draw (tie)!”, 0xA

mtiel       equ         $ – mtie

mfinal            db          “Final board:”, 0xA

mfinall           equ         $ – mfinal

merr        db          “That location is out of range or already taken.”, 0xA

merrl       equ         $ – merr

pbd1        db          ” | | “, 0xA

pbd1l       equ         $ – pbd1

pbd2        db          “—–“, 0xA

pbd2l       equ         $ – pbd2

dbd1        db          ” 012345678 “, 0xA, “[”

dbd1l       equ         $ – dbd1

board       db          ”         ” ; 3×3 linearized game board

bdl               equ         $ – board

dbd2        db          “]”, 0xA

dbd2l       equ         $ – dbd2

 

ebd1        db          “Current board (hex):”, 0xA, ”  0  1  2  3  4  5  6  7  8 “, 0xA, “[”

ebd1l       equ         $ – ebd1

eboard            db          “20 58 20 4F 20 58 20 20 4F”  ; [REDACTED]

el                equ         $ – eboard

ebd2        db          “]”, 0xA

ebd2l       equ         $ – ebd2

hexdigits   db          ‘0123456789ABCDEF’

mbd1        db          “Current board (mem):”, 0xA, “&board = 0x”

mbd1l       equ         $ – mbd1

mboard            db          “FFFFFFFFFFFFFFFF”

ml                equ         $ – mboard

mbd2        db          0xA, “+offset / hex / ASCII”, 0xA

mbd2l       equ         $ – mbd2

mbd3        db          “0x0/: 58h X”, 0xA      ; [REDACTED]

mbd3l       equ         $ – mbd3

lol               db          2,1,6,3,8,4,9,9, \

2,0,7,4,9,9,9,9, \

1,0,8,5,6,4,9,9, \

5,4,6,0,9,9,9,9, \

5,3,7,5,8,0,6,2, \

4,3,8,2,9,9,9,9, \

8,7,3,0,4,2,9,9, \

8,6,5,4,9,9,9,9, \

7,6,5,2,4,0,9,9

top               equ         lol+7

kek               db          5,3,5,3,7,3,5,3,5

section .text                             ; Code

global      _start                              ; Export entry point

print_int:                                ;ecx: const char* msg, edx: size_tmsgl

mov         eax,4                   ; System call number (sys_write)

mov         ebx,1                   ; First argument: file descriptor (stdout == 1)

int         0x80                    ; Call kernel

ret

read_int:                                 ;ecx: char* msg, ; edx: size_tmsgl

mov         eax,3                   ; System call number (sys_read)

xor         ebx,ebx                       ; First argument: file descriptor (stdin == 0)

int         0x80                    ; Call kernel

ret

check_line:                               ; [REDACTED]

mov         bl,[mPXO+spot]          ; [REDACTED]

add         bl,bl                   ; [REDACTED]

sub         bl,[board+esi]          ; [REDACTED]

sub         bl,[board+edi]          ; [REDACTED]

jz          win

ret

tie:                                      ; No return, (it’s a tie)

mov         ecx,mtie                ; Second argument: pointer to message to write

mov         edx,mtiel+mfinall ; Third argument: message length

call  print_int

jmp               pfinalb

win:                                      ; No return, (someone won)

mov         ecx,mPXO                ; Second argument: pointer to message to write

mov         edx,mPXOl               ; Third argument: message length

call  print_int

mov         ecx,mwin2               ; Second argument: pointer to message to write

mov         edx,mwin2l              ; Third argument: message length

call  print_int

mov         ecx,mfinal              ; Second argument: pointer to message to write

mov         edx,mfinall             ; Third argument: message length

call  print_int

; Fallthrough

pfinalb:                                  ; No return, (print final board and exit)

call  [pfunc]

mov         eax,1                   ; System call number (sys_exit)

xor         ebx,ebx                       ; First syscall argument: exit code

int         0x80                    ; Call kernel

; No ret

debug_board:

mov         ecx,dbd1                ; Second argument: pointer to message to write

mov         edx,dbd1l+bdl+dbd2l     ; Third argument: message length

call  print_int

mov         ecx,8                   ; Locations

hexboard:

mov         bl,[board+ecx]

mov         dx,’20’

cmp         bl,’ ‘

cmove ax,dx

mov         dx,’58’

cmp         bl,’X’

cmove ax,dx

mov         dx,’4F’

cmp         bl,’O’

cmove ax,dx

mov         [eboard+2*ecx+ecx],al

mov         [eboard+2*ecx+ecx+1],ah

dec         ecx

jns         hexboard

mov         ecx,ebd1                ; Second argument: pointer to message to write

mov         edx,ebd1l+el+ebd2l      ; Third argument: message length

call  print_int

mov         ecx,mbd1                ; Second argument: pointer to message to write

mov         edx,mbd1l+ml+mbd2l      ; Third argument: message length

call  print_int

mov         rsi,board

mov         rdi,mbd3

memboard:

incb  [rdi+3]                       ; [REDACTED]

mov         bl,[rsi]                ; [REDACTED]

mov         [rdi+10],bl

mov         dx,’20’

cmp         bl,’ ‘

cmove ax,dx

mov         dx,’58’

cmp         bl,’X’

cmove ax,dx

mov         dx,’4F’

cmp         bl,’O’

cmove ax,dx

mov         [rdi+6],ax              ; [REDACTED]

mov         ecx,mbd3                ; Second argument: pointer to message to write

mov         edx,mbd3l               ; Third argument: message length

call  print_int

inc         rsi

cmp         rsi,board+9

jne         memboard

movb  [rdi+3],’/’             ; [REDACTED]

ret

print_board:

xor         esi,esi                       ; [REDACTED]

nrow:

mov         edi,2                   ; [REDACTED]

ncol:

mov         dl,[board+esi+edi]      ; [REDACTED]

mov         [pbd1+edi*2],dl         ; [REDACTED]

dec         edi

jns         ncol                    ; [REDACTED]

mov         ecx,pbd1                ; Second argument: pointer to message to write

add         esi,3                   ; [REDACTED]

cmp         esi,9                   ; [REDACTED]

je          pdone

mov         edx,pbd1l+pbd2l         ; Third argument: message length

call  print_int

jmp         nrow

pdone:

mov         edx,pbd1l               ; Third argument: message length

call  print_int

ret

_start:

; Enable debug?

mov         ecx,mdbg                ; Second argument: pointer to message to write

mov         edx,mdbgl               ; Third argument: message length

call  print_int

; Read answer

mov         ecx,buffer              ; Store input at location ‘buffer’

mov         edx,2                         ; Read these many bytes

call  read_int

; [REDACTED]

cmpb  [buffer],’Y’

je          do_debug

cmpb  [buffer],’y’

je          do_debug

cmpb  [buffer],’D’

je          do_debug

cmpb  [buffer],’d’

je          do_debug

; [REDACTED]

movq  [pfunc],print_board

jmp         play

do_debug:

movq  [pfunc],debug_board

mov         ecx,15                        ; [REDACTED]

mov         rdx, board              ; [REDACTED]

mov         rbx, hexdigits          ; [REDACTED]

memheader:

mov         rax,rdx                       ; [REDACTED]

and         rax,0x000000000000000f

xlatb                               ; [REDACTED]

mov byte          [mboard+ecx],al

dec         ecx

mov         rax,rdx                       ; [REDACTED]

and         rax,0x00000000000000f0

shr         rax,4                   ; [REDACTED]

xlatb                               ; [REDACTED]

mov byte          [mboard+ecx],al

shr         rdx,8                   ; [REDACTED]

dec         ecx

jns         memheader

jmp         play

invalid:

mov         ecx,merr                ; Second argument: pointer to message to write

mov         edx,merrl               ; Third argument: message length

call  print_int

; Fallthrough

play:

; Print messages and board

mov         ecx,mPXO                ; Second argument: pointer to message to write

mov         edx,mPXOl+mplay2l ; Third argument: message length

call  print_int

call  [pfunc]

; Read input

mov         ecx,buffer              ; Store input at location ‘buffer’

mov         edx,2;                        ; Read these many bytes

call  read_int

; Validate

movzx eax, byte [buffer]

sub         al,’0′                        ; [REDACTED]

cmp         al,8

ja          invalid

; Range is valid

cmpb  [board+eax],’ ‘         ; Is empty?

jne         invalid

; Move is fully valid

mov         bl,[mPXO + spot]

mov         [board+eax],bl          ; [REDACTED]

; [REDACTED]

movzx ecx, byte [kek+eax]     ; [REDACTED]

pair:

movzx esi, byte [lol+eax*8+ecx]

dec         ecx

movzx edi, byte [lol+eax*8+ecx]

call  check_line

dec         ecx

jns         pair                    ; [REDACTED]

decb  [top]                   ; [REDACTED]

jz          tie

xorb  [mPXO+spot],magic ; [REDACTED]

jmp               play

Solution

 Jorge_TTT_sanitized.asm

%define cmpb      cmp byte

%define decb      dec byte

%define incb      inc byte

%define xorb      xor byte

%define movb      mov byte

%define movq      mov qword

section .bss                              ; Uninitialized data

buffer            resb  4

pfunc       resq  1

section .data                             ; Initialized data

mdbg        db          “Do you want to enable debug information?”, 0xA

mdbgl       equ         $ – mdbg

mPXO        db          “Player X”

mPXOl       equ         $ – mPXO

mplay2            db          “, choose your location (0-8):”, 0xA, “Current board:”, 0xA

mplay2l           equ         $ – mplay2

mwin2       db          ” wins!”, 0xA

mwin2l            equ         $ – mwin2

spot        equ         7           ; position of the player mark in the mPXO message

magic       equ         0x17  ; Magic! =D

mtie        db          “It’s a draw (tie)!”, 0xA

mtiel       equ         $ – mtie

mfinal            db          “Final board:”, 0xA

mfinall           equ         $ – mfinal

merr        db          “That location is out of range or already taken.”, 0xA

merrl       equ         $ – merr

pbd1        db          ” | | “, 0xA

pbd1l       equ         $ – pbd1

pbd2        db          “—–“, 0xA

pbd2l       equ         $ – pbd2

dbd1        db          ” 012345678 “, 0xA, “[”

dbd1l       equ         $ – dbd1

board       db          ”         ” ; 3×3 linearized game board

bdl               equ         $ – board

dbd2        db          “]”, 0xA

dbd2l       equ         $ – dbd2

ebd1        db          “Current board (hex):”, 0xA, ”  0  1  2  3  4  5  6  7  8 “, 0xA, “[”

ebd1l       equ         $ – ebd1

eboard            db          “20 58 20 4F 20 58 20 20 4F”  ; template used to display the linear board in hexadecimal

el                equ         $ – eboard

ebd2        db          “]”, 0xA

ebd2l       equ         $ – ebd2

hexdigits   db          ‘0123456789ABCDEF’

mbd1        db          “Current board (mem):”, 0xA, “&board = 0x”

mbd1l       equ         $ – mbd1

mboard            db          “FFFFFFFFFFFFFFFF”

ml                equ         $ – mboard

mbd2        db          0xA, “+offset / hex / ASCII”, 0xA

mbd2l       equ         $ – mbd2

mbd3        db          “0x0/: 58h X”, 0xA      ; template used for printing board positions and hex contents one line at a time

; position initialized to ‘/’ so an increment will bring it to ‘0’

mbd3l       equ         $ – mbd3

lol               db          2,1,6,3,8,4,9,9, \

2,0,7,4,9,9,9,9, \

1,0,8,5,6,4,9,9, \

5,4,6,0,9,9,9,9, \

5,3,7,5,8,0,6,2, \

4,3,8,2,9,9,9,9, \

8,7,3,0,4,2,9,9, \

8,6,5,4,9,9,9,9, \

7,6,5,2,4,0,9,9

top               equ         lol+7

kek               db          5,3,5,3,7,3,5,3,5

section .text                             ; Code

global      _start                              ; Export entry point

print_int:                                ;ecx: const char* msg, edx: size_tmsgl

mov         eax,4                   ; System call number (sys_write)

mov         ebx,1                   ; First argument: file descriptor (stdout == 1)

int         0x80                    ; Call kernel

ret

read_int:                                 ;ecx: char* msg, ; edx: size_tmsgl

mov         eax,3                   ; System call number (sys_read)

xor         ebx,ebx                       ; First argument: file descriptor (stdin == 0)

int         0x80                    ; Call kernel

ret

check_line:                               ; check if the two positions edi and esi in the board contain the current player mark

mov         bl,[mPXO+spot]          ; load the current player’s mark in bl

add         bl,bl                   ; put 2 times the current mark in bl

sub         bl,[board+esi]          ; subtract the character in position esi in the board from bl

sub         bl,[board+edi]          ; subtract the character in position edi in the board from bl

jz          win

ret

tie:                                      ; No return, (it’s a tie)

mov         ecx,mtie                ; Second argument: pointer to message to write

mov         edx,mtiel+mfinall ; Third argument: message length

call  print_int

jmp               pfinalb

win:                                      ; No return, (someone won)

mov         ecx,mPXO                ; Second argument: pointer to message to write

mov         edx,mPXOl               ; Third argument: message length

call  print_int

mov         ecx,mwin2               ; Second argument: pointer to message to write

mov         edx,mwin2l              ; Third argument: message length

call  print_int

mov         ecx,mfinal              ; Second argument: pointer to message to write

mov         edx,mfinall             ; Third argument: message length

call  print_int

; Fallthrough

pfinalb:                                  ; No return, (print final board and exit)

call  [pfunc]

mov         eax,1                   ; System call number (sys_exit)

xor         ebx,ebx                       ; First syscall argument: exit code

int         0x80                    ; Call kernel

; No ret

debug_board:

mov         ecx,dbd1                ; Second argument: pointer to message to write

mov         edx,dbd1l+bdl+dbd2l     ; Third argument: message length

call  print_int

mov         ecx,8                   ; Locations

hexboard:

mov         bl,[board+ecx]

mov         dx,’20’

cmp         bl,’ ‘

cmove ax,dx

mov         dx,’58’

cmp         bl,’X’

cmove ax,dx

mov         dx,’4F’

cmp         bl,’O’

cmove ax,dx

mov         [eboard+2*ecx+ecx],al

mov         [eboard+2*ecx+ecx+1],ah

dec         ecx

jns         hexboard

mov         ecx,ebd1                ; Second argument: pointer to message to write

mov         edx,ebd1l+el+ebd2l      ; Third argument: message length

call  print_int

mov         ecx,mbd1                ; Second argument: pointer to message to write

mov         edx,mbd1l+ml+mbd2l      ; Third argument: message length

call  print_int

mov         rsi,board

mov         rdi,mbd3

memboard:

incb  [rdi+3]                       ; increment the character representing the position

mov         bl,[rsi]                ; load the current char from the board and update template with it

mov         [rdi+10],bl

mov         dx,’20’

cmp         bl,’ ‘

cmove ax,dx

mov         dx,’58’

cmp         bl,’X’

cmove ax,dx

mov         dx,’4F’

cmp         bl,’O’

cmove ax,dx

mov         [rdi+6],ax              ; save board character translation to hex in the template

mov         ecx,mbd3                ; Second argument: pointer to message to write

mov         edx,mbd3l               ; Third argument: message length

call  print_int

inc         rsi

cmp         rsi,board+9

jne         memboard

movb  [rdi+3],’/’             ; restore the position in the template to ‘/’

ret

print_board:

xor         esi,esi                       ; initialize esi to zero

nrow:

mov         edi,2                   ; we will fill the 3 chars in a single row on the board template (0 to 2)

ncol:

mov         dl,[board+esi+edi]      ; load character from the board at the current position

mov         [pbd1+edi*2],dl         ; save the character in the board line template

dec         edi

jns         ncol                    ; repeat while edi is not negative

mov         ecx,pbd1                ; Second argument: pointer to message to write

add         esi,3                   ; advance to next row in the board by adding 3

cmp         esi,9                   ; if we get to position 9 we have printed all the board

je          pdone

mov         edx,pbd1l+pbd2l         ; Third argument: message length

call  print_int

jmp         nrow

pdone:

mov         edx,pbd1l               ; Third argument: message length

call  print_int

ret

_start:

; Enable debug?

mov         ecx,mdbg                ; Second argument: pointer to message to write

mov         edx,mdbgl               ; Third argument: message length

call  print_int

; Read answer

mov         ecx,buffer              ; Store input at location ‘buffer’

mov         edx,2                         ; Read these many bytes

call  read_int

; determine if the user entered Y, y, D or d

cmpb  [buffer],’Y’

je          do_debug

cmpb  [buffer],’y’

je          do_debug

cmpb  [buffer],’D’

je          do_debug

cmpb  [buffer],’d’

je          do_debug

; by default, use the print board function

movq  [pfunc],print_board

jmp         play

do_debug:

movq  [pfunc],debug_board

mov         ecx,15                        ; we will use it to loop 16 times

mov         rdx, board              ; load the board address in rdx

mov         rbx, hexdigits          ; load the hexadecimal table for translating digits

memheader:

mov         rax,rdx                       ; load the current address into rax

and         rax,0x000000000000000f

xlatb                               ; translate the lowest nibble to ascii using the hex table

mov byte          [mboard+ecx],al

dec         ecx

mov         rax,rdx                       ; restore the board address in rax

and         rax,0x00000000000000f0

shr         rax,4                   ; move the upper nibble of the first byte to the bottom

xlatb                               ; translate the second nibble to ascii using the hex table

mov byte          [mboard+ecx],al

shr         rdx,8                   ; update the address to print the next byte

dec         ecx

jns         memheader

jmp         play

invalid:

mov         ecx,merr                ; Second argument: pointer to message to write

mov         edx,merrl               ; Third argument: message length

call  print_int

; Fallthrough

play:

; Print messages and board

mov         ecx,mPXO                ; Second argument: pointer to message to write

mov         edx,mPXOl+mplay2l ; Third argument: message length

call  print_int

call  [pfunc]

; Read input

mov         ecx,buffer              ; Store input at location ‘buffer’

mov         edx,2;                        ; Read these many bytes

call  read_int

; Validate

movzx eax, byte [buffer]

sub         al,’0′                        ; convert the read ascii char to an integer

cmp         al,8

ja          invalid

; Range is valid

cmpb  [board+eax],’ ‘         ; Is empty?

jne         invalid

; Move is fully valid

mov         bl,[mPXO + spot]

mov         [board+eax],bl          ; save the current player mark to the selected position in the board

; check the board for a possible winner or tie

movzx ecx, byte [kek+eax]     ; get the number of positions to compare into ecx

pair:

movzx esi, byte [lol+eax*8+ecx]

dec         ecx

movzx edi, byte [lol+eax*8+ecx]

call  check_line

dec         ecx

jns         pair                    ; repeat while ecx>=0

decb  [top]                   ; decrement number of free positions

jz          tie

xorb  [mPXO+spot],magic ; change the player by xoring with ‘X’^’O’ = 0x17

jmp               play 

Report

The code Jorge_TTT has several clever tricks to avoid using memory and innecessary loops. However, the readability of it is decreased since there are not much procedures and the code relies on updating the messages every time they are used.

The program proceeds as one would expect. The first step is reading the user selection for printing the boards. In my case, the code simply updates a flag that is used afterwards to decide the subroutine to use. In the Jorge_TTT however, a more convenient way to do it is presented. The actual print function to use is saved in a variable, thus we don’t need to do any comparison afterwards, but simply call the saved function pointer. Another trick is the use of templates for printing. In my code, all print_board and debug_board messages are generated by the code printing each part at a time (message, char, other message, etc.). In the Jorge_TTT code templates are used and the code in both print routines only updates certain positions in it and prints the entire template as a single string. Another difference between our codes is the way to display hexadecimal numbers. In my code, a set of subroutines to print a digit in hex, print a byte in hex and print a 32 bit number in hex are used for displaying the debug_board output. In the Jorge_TTT code however, this process is quite simplified. The translation of a 4 bit digit to ascii is simply done using a table, a byte translation is done by looking up the upper and lower nibble in the table. The board address is displayed by going through each byte in the qword and saving it in a template that is reused everytime the debug_board is displayed. In my case the address is translated everytime, which is a time consuming process. Another clever trick is the way in which the board position is displayed when the debug_board is printed one position per line. In my case, the position was first translated to hex and then displayed, and then it was incremented in a loop. In Jorge_TTT code this process is completely simplified by modifying a simple template with the format ‘0x0/’, this can be done because the position is always between 0 and 8 so only one digit must be updated and, since the update implies only an increment, it can be initialized to ‘/’ so the first time it’s incremented, the char changes to ‘0’ since the ascii value for ‘/’ is 47 and for ‘0’ is 48.

Another simplification I found is avoiding the translation of the board characters to hexadecimal. It’s now obvious that the board characters don’t need to be translated to hexadecimal using the ordinary method. Since they are only 3 possible chars (‘X’, ‘O’ and ‘ ‘), we only need to display ‘58’, ‘4f’ or ‘20’, which is the way that Jorge_TTT uses, it only makes a series of comparison and conditional moves to update the template with the hex representation of the board characters.

The game loop is done in the usual way, asking for input, displaying the board, validating, checking for win or tie and then changing the player turn. The input is handled with the sys_readsyscall as expected and the display process was described earlier. Another optimization is found in the win and tie check part. The win check is based on a table and two pieces of code with the labels pair and check_line. The steps are as follows, the current move is first saved on the board using the current player mark. Then, the table is used to get the positions that are affected by the change, that is, which positions could result in a win if the current position is filled. Since the current position is known to have the current player mark we only need to check two positions at a time against the current player, that is what the “pair” code does in a loop, checking every two consecutive positions in the lol table until “kek” positions are checked. The check_line simply compares the two positions with one another and if they have the same char as the current player mark it goes to the win part and ends the game. The tie check is done by a surprisingly simple update of a variable; it simply counts the number of times a char has been saved on the board. The variable is initialized to 9 so if it reaches zero, then the whole board is full and the game results in a tie.

The player turn changed by yet another clever trick which consists in xoring the current player mark with 0x17. The value 0x17 result of xoring the characters ‘X’ and ‘O’. That way, if any of the chars is xored with 0x17, it will result in the other since A^(A^B) = B.