├── .gitattributes ├── .gitignore ├── README.md ├── assignment01 ├── go.sh ├── lib.inc ├── print_string ├── print_string.asm ├── string_copy ├── string_copy.asm ├── string_length ├── string_length.asm ├── string_length.o ├── termcolor.pyc ├── test.py ├── tester └── tester.asm ├── ch01 └── no-code-in-ch01.txt └── ch02 ├── endianness ├── endianness.asm ├── endianness.o ├── false ├── false.asm ├── false.o ├── hello ├── hello.asm ├── hello.o ├── hello_proper_exit ├── hello_proper_exit.asm ├── hello_proper_exit.o ├── lea_vs_mov ├── lea_vs_mov.asm ├── lea_vs_mov.o ├── print_call ├── print_call.asm ├── print_call.o ├── print_rax ├── print_rax.asm ├── print_rax.o ├── strlen ├── strlen.asm └── strlen.o /.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.pyc linguist-vendored 2 | **/*.py linguist-vendored 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Assembly and C 2 | -------------------------------------------------------------------------------- /assignment01/go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | nasm -f elf64 tester.asm -o tester.o 4 | ld -o tester tester.o 5 | chmod 755 tester 6 | 7 | -------------------------------------------------------------------------------- /assignment01/lib.inc: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | section .text 4 | 5 | exit: 6 | mov rax, 60 7 | xor rdi, rdi 8 | syscall 9 | 10 | string_length: 11 | xor rax, rax 12 | ; without ret, it falls through to loop 13 | 14 | .length_loop: 15 | cmp byte[rdi + rax], 0 16 | jz .length_end 17 | inc rax 18 | jmp .length_loop 19 | 20 | .length_end: 21 | ret 22 | 23 | print_string: 24 | push rdi 25 | call string_length ; puts string length into rax 26 | pop rsi 27 | mov rdx, rax ; rax contains length 28 | mov rax, 1 ; write syscall 29 | mov rdi, 1 ; stdout file descriptor 30 | syscall 31 | ret 32 | 33 | print_char: 34 | push rdi 35 | mov rdi, rsp ; rdi must be a memory location of the beginning of the string 36 | call print_string 37 | pop rdi 38 | ret 39 | 40 | print_newline: 41 | mov rdi, 10 ; using a data member here will not work since it does not expect a memory address 42 | call print_char 43 | ret 44 | 45 | print_uint: 46 | ; rdi holds 8-byte unsigned integer 47 | mov rax, rdi ; rax will be dividend 48 | mov rcx, 10 ; divisor 49 | mov rsi, rsp ; save location of the end of the buffer 50 | push 0 ; push null terminator, for end of string, 8 bytes 51 | dec rsi ; this will point to the first char in the buffer 52 | sub rsp, 16 ; add space for 16 more bytes = null + 7 + 16 = 24 53 | ; this gives us enough for 20 digits (unsigned long) and a null, aligned to 8 bytes 54 | 55 | .print_uint_loop: 56 | cmp rax, 0 57 | jz .print_uint_print 58 | xor rdx, rdx ; clear any garbage from last division 59 | div rcx ; DIV divides value in RDX:RAX by operand 60 | ; quotient stored to RAX 61 | ; remainder stored in RDX 62 | add rdx, 48 ; convert to ASCII 63 | dec rsi 64 | mov [rsi], dl ; add lower byte of rdx to buffer 65 | jmp .print_uint_loop 66 | 67 | .print_uint_print: 68 | mov rdi, rsi ; point rdi to beginning of string buffer 69 | call print_string 70 | add rsp, 24 ; pushing rsp at beginning and popping at end to restore will not work 71 | ; since I moved the stack pointer elsewhere 72 | ret 73 | 74 | print_int: 75 | ; rdi holds 8-byte signed integer 76 | 77 | mov rsi, rdi ; determine sign 78 | sar rsi, 63 ; propagate sign bit right 79 | cmp sil, 0xff ; see if lower 8 bits of rsi are all on 80 | jne print_uint 81 | push rdi ; caller-save 82 | mov rdi, '-' ; print negative sign 83 | call print_char 84 | pop rdi ; caller-restore 85 | neg rdi ; turn from negative to positive: flips all bits and adds 1 86 | jmp print_uint 87 | 88 | read_char: 89 | ; returns char in rax (char at beginning) 90 | 91 | xor rax, rax ; 0 is read syscall number: https://web.archive.org/web/20120210203233/http://www.acsu.buffalo.edu/~charngda/linux_syscalls_64bit.html 92 | mov rdi, 0 ; 0 is stdin file descriptor 93 | push 0 ; allocate 8 bytes on stack 94 | mov rsi, rsp ; beginning of buffer 95 | mov rdx, 1 ; read one byte 96 | syscall 97 | 98 | ; to print input char: 99 | ;xor rdi, rdi 100 | ;mov dil, byte[rsp] 101 | ;sal rdi, 64 ; zero-out (null-terminate) all but first 8 bits, move 8 bits to beginning of register 102 | ;call print_char 103 | ;call print_newline 104 | 105 | pop rax 106 | ret 107 | 108 | read_word: 109 | ; rdi - address of buffer 110 | ; rsi - size of buffer 111 | ; return 0 in rax if too large 112 | ; return addr in rax if it fits 113 | ; and word length in rdx 114 | 115 | ; reading a single word, so skip all whitespace until we reach 116 | ; a non-whitespace, then if we hit another whitespace char, 117 | ; add null terminator and return 118 | 119 | mov rbx, rsi ; save size of buffer 120 | xor rcx, rcx ; initialize byte counter 121 | dec rbx ; account for null terminator 122 | 123 | .read_word_find_word: 124 | push rdi 125 | push rbx 126 | push rcx 127 | call read_char 128 | pop rcx 129 | pop rbx 130 | pop rdi 131 | ; char returned in rax (al) 132 | 133 | cmp al, 0x09 ; tab 134 | je .read_word_find_word 135 | cmp al, 0x0A ; line feed 136 | je .read_word_find_word 137 | cmp al, 0x0D ; carriage return 138 | je .read_word_find_word 139 | cmp al, 0x20 ; space 140 | je .read_word_find_word 141 | test al, al ; null terminator 142 | jz .read_word_done 143 | 144 | .read_word_letters_in_word: 145 | mov byte[rdi + rcx], al 146 | inc rcx 147 | 148 | push rdi 149 | push rbx 150 | push rcx 151 | call read_char 152 | pop rcx 153 | pop rbx 154 | pop rdi 155 | 156 | cmp al, 0x09 ; tab 157 | je .read_word_done 158 | cmp al, 0x0A ; line feed 159 | je .read_word_done 160 | cmp al, 0x0D ; carriage return 161 | je .read_word_done 162 | cmp al, 0x20 ; space 163 | je .read_word_done 164 | test al, al ; null terminator 165 | jz .read_word_done 166 | 167 | cmp rcx, rbx ; run out of space? 168 | je .read_word_too_long 169 | 170 | jmp .read_word_letters_in_word 171 | 172 | .read_word_done: 173 | mov byte[rdi + rcx], 0 ; null terminate the string 174 | mov rax, rdi 175 | mov rdx, rcx 176 | ret 177 | 178 | .read_word_too_long: 179 | xor rax, rax 180 | ret 181 | 182 | parse_uint: 183 | ; rdi points to a string 184 | ; returns rax: number as integer 185 | ; rdx: length 186 | 187 | xor rax, rax ; will create integer here 188 | xor rsi, rsi ; byte counter 189 | mov rbx, 10 ; will multiply rax by 10 each time 190 | 191 | .parse_uint_loop: 192 | ; multiply rax by 10 193 | ; take digit from rdi + rsi 194 | ; convert to int 195 | ; add to rax 196 | ; inc rsi 197 | 198 | .parse_uint_done: 199 | mov rdx, rsi 200 | ret 201 | 202 | parse_int: 203 | ; rdi points to a string 204 | ; returns rax: number as integer 205 | ; rdx: length 206 | 207 | ; check for sign 208 | ; call parse_uint on rest of string 209 | ; negate if sign 210 | xor rax, rax 211 | ret 212 | 213 | string_equals: 214 | ret 215 | 216 | string_copy: 217 | ret 218 | 219 | -------------------------------------------------------------------------------- /assignment01/print_string: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/assignment01/print_string -------------------------------------------------------------------------------- /assignment01/print_string.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | str: db '', 0 3 | section .text 4 | %include "lib.inc" 5 | global _start 6 | _start: 7 | 8 | mov rdi, -1 9 | mov rsi, -1 10 | mov rax, -1 11 | mov rcx, -1 12 | mov rdx, -1 13 | mov r8, -1 14 | mov r9, -1 15 | mov r10, -1 16 | mov r11, -1 17 | push rbx 18 | push rbp 19 | push r12 20 | push r13 21 | push r14 22 | push r15 23 | 24 | mov rdi, str 25 | call print_string 26 | 27 | cmp r15, [rsp] 28 | jne .convention_error 29 | pop r15 30 | cmp r14, [rsp] 31 | jne .convention_error 32 | pop r14 33 | cmp r13, [rsp] 34 | jne .convention_error 35 | pop r13 36 | cmp r12, [rsp] 37 | jne .convention_error 38 | pop r12 39 | cmp rbp, [rsp] 40 | jne .convention_error 41 | pop rbp 42 | cmp rbx, [rsp] 43 | jne .convention_error 44 | pop rbx 45 | 46 | jmp continue 47 | 48 | .convention_error: 49 | mov rax, 1 50 | mov rdi, 2 51 | mov rsi, err_calling_convention 52 | mov rdx, err_calling_convention.end - err_calling_convention 53 | syscall 54 | mov rax, 60 55 | mov rdi, -41 56 | syscall 57 | section .data 58 | err_calling_convention: db "You did not respect the calling convention! Check that you handled caller-saved and callee-saved registers correctly", 10 59 | .end: 60 | section .text 61 | continue: 62 | 63 | 64 | mov rax, 60 65 | xor rdi, rdi 66 | syscall -------------------------------------------------------------------------------- /assignment01/string_copy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/assignment01/string_copy -------------------------------------------------------------------------------- /assignment01/string_copy.asm: -------------------------------------------------------------------------------- 1 | 2 | section .data 3 | arg1: db 'ashdb asdhabs dahb', 0 4 | arg2: times 19 db 66 5 | section .text 6 | %include "lib.inc" 7 | global _start 8 | _start: 9 | 10 | mov rdi, -1 11 | mov rsi, -1 12 | mov rax, -1 13 | mov rcx, -1 14 | mov rdx, -1 15 | mov r8, -1 16 | mov r9, -1 17 | mov r10, -1 18 | mov r11, -1 19 | push rbx 20 | push rbp 21 | push r12 22 | push r13 23 | push r14 24 | push r15 25 | 26 | mov rdi, arg1 27 | mov rsi, arg2 28 | mov rdx, 19 29 | call string_copy 30 | 31 | 32 | cmp r15, [rsp] 33 | jne .convention_error 34 | pop r15 35 | cmp r14, [rsp] 36 | jne .convention_error 37 | pop r14 38 | cmp r13, [rsp] 39 | jne .convention_error 40 | pop r13 41 | cmp r12, [rsp] 42 | jne .convention_error 43 | pop r12 44 | cmp rbp, [rsp] 45 | jne .convention_error 46 | pop rbp 47 | cmp rbx, [rsp] 48 | jne .convention_error 49 | pop rbx 50 | 51 | jmp continue 52 | 53 | .convention_error: 54 | mov rax, 1 55 | mov rdi, 2 56 | mov rsi, err_calling_convention 57 | mov rdx, err_calling_convention.end - err_calling_convention 58 | syscall 59 | mov rax, 60 60 | mov rdi, -41 61 | syscall 62 | section .data 63 | err_calling_convention: db "You did not respect the calling convention! Check that you handled caller-saved and callee-saved registers correctly", 10 64 | .end: 65 | section .text 66 | continue: 67 | 68 | mov rdi, arg2 69 | call print_string 70 | mov rax, 60 71 | xor rdi, rdi 72 | syscall -------------------------------------------------------------------------------- /assignment01/string_length: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/assignment01/string_length -------------------------------------------------------------------------------- /assignment01/string_length.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | str: db '', 0 3 | section .text 4 | %include "lib.inc" 5 | global _start 6 | _start: 7 | 8 | mov rdi, -1 9 | mov rsi, -1 10 | mov rax, -1 11 | mov rcx, -1 12 | mov rdx, -1 13 | mov r8, -1 14 | mov r9, -1 15 | mov r10, -1 16 | mov r11, -1 17 | push rbx 18 | push rbp 19 | push r12 20 | push r13 21 | push r14 22 | push r15 23 | 24 | mov rdi, str 25 | call string_length 26 | 27 | cmp r15, [rsp] 28 | jne .convention_error 29 | pop r15 30 | cmp r14, [rsp] 31 | jne .convention_error 32 | pop r14 33 | cmp r13, [rsp] 34 | jne .convention_error 35 | pop r13 36 | cmp r12, [rsp] 37 | jne .convention_error 38 | pop r12 39 | cmp rbp, [rsp] 40 | jne .convention_error 41 | pop rbp 42 | cmp rbx, [rsp] 43 | jne .convention_error 44 | pop rbx 45 | 46 | jmp continue 47 | 48 | .convention_error: 49 | mov rax, 1 50 | mov rdi, 2 51 | mov rsi, err_calling_convention 52 | mov rdx, err_calling_convention.end - err_calling_convention 53 | syscall 54 | mov rax, 60 55 | mov rdi, -41 56 | syscall 57 | section .data 58 | err_calling_convention: db "You did not respect the calling convention! Check that you handled caller-saved and callee-saved registers correctly", 10 59 | .end: 60 | section .text 61 | continue: 62 | 63 | mov rdi, rax 64 | mov rax, 60 65 | syscall -------------------------------------------------------------------------------- /assignment01/string_length.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/assignment01/string_length.o -------------------------------------------------------------------------------- /assignment01/termcolor.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/assignment01/termcolor.pyc -------------------------------------------------------------------------------- /assignment01/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | import re 7 | import sys 8 | from subprocess import CalledProcessError, Popen, PIPE 9 | from termcolor import colored 10 | 11 | #-------helpers--------------- 12 | 13 | def starts_uint( s ): 14 | matches = re.findall('^\d+', s) 15 | if matches: 16 | return (int(matches[0]), len(matches[0])) 17 | else: 18 | return (0, 0) 19 | 20 | def starts_int( s ): 21 | matches = re.findall('^-?\d+', s) 22 | if matches: 23 | return (int(matches[0]), len(matches[0])) 24 | else: 25 | return (0, 0) 26 | 27 | def unsigned_reinterpret(x): 28 | if x < 0: 29 | return x + 2**64 30 | else: 31 | return x 32 | 33 | def first_or_empty( s ): 34 | sp = s.split() 35 | if sp == [] : 36 | return '' 37 | else: 38 | return sp[0] 39 | 40 | #----------------------------- 41 | 42 | def compile( fname, text ): 43 | f = open( fname + '.asm', 'w') 44 | f.write( text ) 45 | f.close() 46 | 47 | if subprocess.call( ['nasm', '-f', 'elf64', fname + '.asm', '-o', fname+'.o'] ) == 0 and subprocess.call( ['ld', '-o' , fname, fname+'.o'] ) == 0: 48 | print ' ', fname, ': compiled' 49 | return True 50 | else: 51 | print ' ', fname, ': failed to compile' 52 | return False 53 | 54 | 55 | def launch( fname, seed = '' ): 56 | output = '' 57 | try: 58 | p = Popen(['./'+fname], shell=None, stdin=PIPE, stdout=PIPE) 59 | (output, err) = p.communicate(input=seed) 60 | return (output, p.returncode) 61 | except CalledProcessError as exc: 62 | return (exc.output, exc.returncode) 63 | else: 64 | return (output, 0) 65 | 66 | 67 | 68 | def test_asm( text, name = 'dummy', seed = '' ): 69 | if compile( name, text ): 70 | r = launch( name, seed ) 71 | #os.remove( name ) 72 | #os.remove( name + '.o' ) 73 | #os.remove( name + '.asm' ) 74 | return r 75 | return None 76 | 77 | class Test: 78 | name = '' 79 | string = lambda x : x 80 | checker = lambda input, output, code : False 81 | 82 | def __init__(self, name, stringctor, checker): 83 | self.checker = checker 84 | self.string = stringctor 85 | self.name = name 86 | def perform(self, arg): 87 | res = test_asm( self.string(arg), self.name, arg) 88 | if res is None: 89 | return False 90 | (output, code) = res 91 | print '"', arg,'" ->', res 92 | return self.checker( arg, output, code ) 93 | 94 | before_call=""" 95 | mov rdi, -1 96 | mov rsi, -1 97 | mov rax, -1 98 | mov rcx, -1 99 | mov rdx, -1 100 | mov r8, -1 101 | mov r9, -1 102 | mov r10, -1 103 | mov r11, -1 104 | push rbx 105 | push rbp 106 | push r12 107 | push r13 108 | push r14 109 | push r15 110 | """ 111 | after_call=""" 112 | cmp r15, [rsp] 113 | jne .convention_error 114 | pop r15 115 | cmp r14, [rsp] 116 | jne .convention_error 117 | pop r14 118 | cmp r13, [rsp] 119 | jne .convention_error 120 | pop r13 121 | cmp r12, [rsp] 122 | jne .convention_error 123 | pop r12 124 | cmp rbp, [rsp] 125 | jne .convention_error 126 | pop rbp 127 | cmp rbx, [rsp] 128 | jne .convention_error 129 | pop rbx 130 | 131 | jmp continue 132 | 133 | .convention_error: 134 | mov rax, 1 135 | mov rdi, 2 136 | mov rsi, err_calling_convention 137 | mov rdx, err_calling_convention.end - err_calling_convention 138 | syscall 139 | mov rax, 60 140 | mov rdi, -41 141 | syscall 142 | section .data 143 | err_calling_convention: db "You did not respect the calling convention! Check that you handled caller-saved and callee-saved registers correctly", 10 144 | .end: 145 | section .text 146 | continue: 147 | """ 148 | tests=[ Test('string_length', 149 | lambda v : """section .data 150 | str: db '""" + v + """', 0 151 | section .text 152 | %include "lib.inc" 153 | global _start 154 | _start: 155 | """ + before_call + """ 156 | mov rdi, str 157 | call string_length 158 | """ + after_call + """ 159 | mov rdi, rax 160 | mov rax, 60 161 | syscall""", 162 | lambda i, o, r: r == len(i) 163 | ), 164 | 165 | Test('print_string', 166 | lambda v : """section .data 167 | str: db '""" + v + """', 0 168 | section .text 169 | %include "lib.inc" 170 | global _start 171 | _start: 172 | """ + before_call + """ 173 | mov rdi, str 174 | call print_string 175 | """ + after_call + """ 176 | 177 | mov rax, 60 178 | xor rdi, rdi 179 | syscall""", 180 | lambda i,o,r: i == o), 181 | 182 | Test('string_copy', 183 | lambda v: """ 184 | section .data 185 | arg1: db '""" + v + """', 0 186 | arg2: times """ + str(len(v) + 1) + """ db 66 187 | section .text 188 | %include "lib.inc" 189 | global _start 190 | _start: 191 | """ + before_call + """ 192 | mov rdi, arg1 193 | mov rsi, arg2 194 | mov rdx, """ + str(len(v) + 1) + """ 195 | call string_copy 196 | 197 | """ + after_call + """ 198 | mov rdi, arg2 199 | call print_string 200 | mov rax, 60 201 | xor rdi, rdi 202 | syscall""", 203 | lambda i,o,r: i == o), 204 | 205 | Test('print_char', 206 | lambda v:""" section .text 207 | %include "lib.inc" 208 | global _start 209 | _start: 210 | """ + before_call + """ 211 | mov rdi, '""" + v + """' 212 | call print_char 213 | """ + after_call + """ 214 | mov rax, 60 215 | xor rdi, rdi 216 | syscall""", 217 | lambda i,o,r: i == o), 218 | 219 | Test('print_uint', 220 | lambda v: """section .text 221 | %include "lib.inc" 222 | global _start 223 | _start: 224 | """ + before_call + """ 225 | mov rdi, """ + v + """ 226 | call print_uint 227 | """ + after_call + """ 228 | mov rax, 60 229 | xor rdi, rdi 230 | syscall""", 231 | lambda i, o, r: o == str(unsigned_reinterpret(int(i)))), 232 | 233 | Test('print_int', 234 | lambda v: """section .text 235 | %include "lib.inc" 236 | global _start 237 | _start: 238 | """ + before_call + """ 239 | mov rdi, """ + v + """ 240 | call print_int 241 | """ + after_call + """ 242 | mov rax, 60 243 | xor rdi, rdi 244 | syscall""", 245 | lambda i, o, r: o == i), 246 | 247 | Test('read_char', 248 | lambda v:"""section .text 249 | %include "lib.inc" 250 | global _start 251 | _start: 252 | """ + before_call + """ 253 | call read_char 254 | """ + after_call + """ 255 | mov rdi, rax 256 | mov rax, 60 257 | syscall""", 258 | lambda i, o, r: (i == "" and r == 0 ) or ord( i[0] ) == r ), 259 | 260 | Test('read_word', 261 | lambda v:""" 262 | section .data 263 | word_buf: times 20 db 0xca 264 | section .text 265 | %include "lib.inc" 266 | global _start 267 | _start: 268 | """ + before_call + """ 269 | mov rdi, word_buf 270 | mov rsi, 20 271 | call read_word 272 | """ + after_call + """ 273 | mov rdi, rax 274 | call print_string 275 | 276 | mov rax, 60 277 | xor rdi, rdi 278 | syscall""", 279 | lambda i, o, r: first_or_empty(i) == o), 280 | 281 | Test('read_word_length', 282 | lambda v:""" 283 | section .data 284 | word_buf: times 20 db 0xca 285 | section .text 286 | %include "lib.inc" 287 | global _start 288 | _start: 289 | """ + before_call + """ 290 | mov rdi, word_buf 291 | mov rsi, 20 292 | call read_word 293 | """ + after_call + """ 294 | 295 | mov rax, 60 296 | mov rdi, rdx 297 | syscall""", 298 | lambda i, o, r: len(first_or_empty(i)) == r or len(first_or_empty(i)) > 19), 299 | 300 | Test('read_word_too_long', 301 | lambda v:""" 302 | section .data 303 | word_buf: times 20 db 0xca 304 | section .text 305 | %include "lib.inc" 306 | global _start 307 | _start: 308 | """ + before_call + """ 309 | mov rdi, word_buf 310 | mov rsi, 20 311 | call read_word 312 | """ + after_call + """ 313 | 314 | mov rdi, rax 315 | mov rax, 60 316 | syscall""", 317 | lambda i, o, r: ( (not len(first_or_empty(i)) > 19) and r != 0 ) or r == 0 ), 318 | 319 | Test('parse_uint', 320 | lambda v: """section .data 321 | input: db '""" + v + """', 0 322 | section .text 323 | %include "lib.inc" 324 | global _start 325 | _start: 326 | """ + before_call + """ 327 | mov rdi, input 328 | call parse_uint 329 | """ + after_call + """ 330 | push rdx 331 | mov rdi, rax 332 | call print_uint 333 | mov rax, 60 334 | pop rdi 335 | syscall""", 336 | lambda i,o,r: starts_uint(i)[0] == int(o) and r == starts_uint( i )[1]), 337 | 338 | Test('parse_int', 339 | lambda v: """section .data 340 | input: db '""" + v + """', 0 341 | section .text 342 | %include "lib.inc" 343 | global _start 344 | _start: 345 | """ + before_call + """ 346 | mov rdi, input 347 | call parse_int 348 | """ + after_call + """ 349 | push rdx 350 | mov rdi, rax 351 | call print_int 352 | pop rdi 353 | mov rax, 60 354 | syscall""", 355 | lambda i,o,r: (starts_int( i )[1] == 0 and int(o) == 0) or (starts_int(i)[0] == int(o) and r == starts_int( i )[1] )), 356 | 357 | Test('string_equals', 358 | lambda v: """section .data 359 | str1: db '""" + v + """',0 360 | str2: db '""" + v + """',0 361 | section .text 362 | %include "lib.inc" 363 | global _start 364 | _start: 365 | """ + before_call + """ 366 | mov rdi, str1 367 | mov rsi, str2 368 | call string_equals 369 | """ + after_call + """ 370 | mov rdi, rax 371 | mov rax, 60 372 | syscall""", 373 | lambda i,o,r: r == 1), 374 | 375 | Test('string_equals not equals', 376 | lambda v: """section .data 377 | str1: db '""" + v + """',0 378 | str2: db '""" + v + """!!',0 379 | section .text 380 | %include "lib.inc" 381 | global _start 382 | _start: 383 | """ + before_call + """ 384 | mov rdi, str1 385 | mov rsi, str2 386 | call string_equals 387 | """ + after_call + """ 388 | mov rdi, rax 389 | mov rax, 60 390 | syscall""", 391 | lambda i,o,r: r == 0), 392 | 393 | Test('string_copy_too_long', 394 | lambda v: """ 395 | section .rodata 396 | err_too_long_msg: db "string is too long", 10, 0 397 | section .data 398 | arg1: db '""" + v + """', 0 399 | arg2: times """ + str(len(v)/2) + """ db 66 400 | section .text 401 | %include "lib.inc" 402 | global _start 403 | _start: 404 | """ + before_call + """ 405 | mov rdi, arg1 406 | mov rsi, arg2 407 | mov rdx, """ + str(len(v)/2 ) + """ 408 | call string_copy 409 | test rax, rax 410 | jnz .good 411 | mov rdi, err_too_long_msg 412 | call print_string 413 | jmp _exit 414 | .good: 415 | """ + after_call + """ 416 | mov rdi, arg2 417 | call print_string 418 | _exit: 419 | mov rax, 60 420 | xor rdi, rdi 421 | syscall""", 422 | lambda i,o,r: o.find("too long") != -1 ) 423 | ] 424 | 425 | 426 | inputs= {'string_length' 427 | : [ 'asdkbasdka', 'qwe qweqe qe', ''], 428 | 'print_string' 429 | : ['ashdb asdhabs dahb', ' ', ''], 430 | 'string_copy' 431 | : ['ashdb asdhabs dahb', ' ', ''], 432 | 'string_copy_too_long' 433 | : ['ashdb asdhabs dahb', ' ', ''], 434 | 'print_char' 435 | : "a c", 436 | 'print_uint' 437 | : ['-1', '12345234121', '0', '12312312', '123123'], 438 | 'print_int' 439 | : ['-1', '-12345234121', '0', '123412312', '123123'], 440 | 'read_char' 441 | : ['-1', '-1234asdasd5234121', '', ' ', '\t ', 'hey ya ye ya', 'hello world' ], 442 | 'read_word' 443 | : ['-1'], # , '-1234asdasd5234121', '', ' ', '\t ', 'hey ya ye ya', 'hello world' ], 444 | 'read_word_length' 445 | : ['-1', '-1234asdasd5234121', '', ' ', '\t ', 'hey ya ye ya', 'hello world' ], 446 | 'read_word_too_long' 447 | : [ 'asdbaskdbaksvbaskvhbashvbasdasdads wewe', 'short' ], 448 | 'parse_uint' 449 | : ["0", "1234567890987654321hehehey", "1" ], 450 | 'parse_int' 451 | : ["0", "1234567890987654321hehehey", "-1dasda", "-eedea", "-123123123", "1" ], 452 | 'string_equals' 453 | : ['ashdb asdhabs dahb', ' ', '', "asd" ], 454 | 'string_equals not equals' 455 | : ['ashdb asdhabs dahb', ' ', '', "asd" ] 456 | } 457 | 458 | if __name__ == "__main__": 459 | found_error = False 460 | for t in tests: 461 | for arg in inputs[t.name]: 462 | if not found_error: 463 | try: 464 | print ' testing', t.name,'on "'+ arg +'"' 465 | res = t.perform(arg) 466 | if res: 467 | print ' [', colored(' ok ', 'green'), ']' 468 | else: 469 | print '* [ ', colored('fail', 'red'), ']' 470 | found_error = True 471 | except: 472 | print '* [ ', colored('fail', 'red'), '] with exception' , sys.exc_info()[0] 473 | found_error = True 474 | if found_error: 475 | print 'Not all tests have been passed' 476 | else: 477 | print colored( "Good work, all tests are passed", 'green') 478 | -------------------------------------------------------------------------------- /assignment01/tester: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/assignment01/tester -------------------------------------------------------------------------------- /assignment01/tester.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | message: db 'hello', 10 3 | 4 | section .text 5 | %include "lib.inc" 6 | global _start 7 | 8 | _start: 9 | mov rdi, message 10 | call print_string 11 | 12 | mov rdi, 123 13 | call print_uint 14 | call print_newline 15 | 16 | mov rdi, 5678 17 | call print_int 18 | call print_newline 19 | 20 | mov rdi, -5678 21 | call print_int 22 | call print_newline 23 | 24 | ;call read_char 25 | ;mov rdi, rax 26 | ;call print_char 27 | ;call print_newline 28 | 29 | push 0 30 | push 0 31 | mov rdi, rsp ; beginning of buffer 32 | mov rsi, 16 ; length to buffer 33 | call read_word 34 | mov rdi, rax 35 | push rdx ; caller saving string length 36 | call print_string 37 | call print_newline 38 | pop rdi ; caller restore string length 39 | call print_int ; print string length 40 | call print_newline 41 | sub rsp, 16 42 | 43 | call exit 44 | 45 | -------------------------------------------------------------------------------- /ch01/no-code-in-ch01.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch01/no-code-in-ch01.txt -------------------------------------------------------------------------------- /ch02/endianness: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/endianness -------------------------------------------------------------------------------- /ch02/endianness.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | newline_char: db 10 4 | codes: db '0123456789abcdef' 5 | 6 | global _start 7 | 8 | print_newline: 9 | mov rax, 1 ; 'write' syscall indentifier 10 | mov rdi, 1 ; stdout file descriptor 11 | mov rsi, newline_char ; where we take data from 12 | mov rdx, 1 ; number of bytes to write 13 | syscall 14 | ret 15 | 16 | print_hex: 17 | mov rax, rdi ; puts function arg (char to print) into rax 18 | mov rdi, 1 ; stdout 19 | mov rdx, 1 ; number of bytes 20 | mov rcx, 64 ; how far we're shifting rax 21 | ; there's no return so we fall through to iterate 22 | 23 | iterate: 24 | push rax ; save initial rax value 25 | sub rcx, 4 26 | sar rax, cl ; shift to 60, 56, 52, ... 4, 0 27 | and rax, 0xf ; zero out all but the last 4 bits 28 | lea rsi, [codes + rax] ; take a hex digit character code 29 | 30 | mov rax, 1 31 | 32 | push rcx ; syscall will break rcx 33 | syscall ; rax = 1 (31) -- the write identifier 34 | ; rdi = 1 for stdout, rsi = character address 35 | pop rcx 36 | 37 | pop rax 38 | test rcx, rcx 39 | jnz iterate 40 | 41 | ret 42 | 43 | section .data 44 | demo1: dq 0x1122334455667788 45 | demo2: db 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 46 | 47 | section .text 48 | 49 | _start: 50 | mov rdi, [demo1] 51 | call print_hex 52 | call print_newline 53 | 54 | mov rdi, [demo2] 55 | call print_hex 56 | call print_newline 57 | 58 | mov rax, 60 59 | xor rdi, rdi 60 | syscall 61 | 62 | -------------------------------------------------------------------------------- /ch02/endianness.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/endianness.o -------------------------------------------------------------------------------- /ch02/false: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/false -------------------------------------------------------------------------------- /ch02/false.asm: -------------------------------------------------------------------------------- 1 | global _start 2 | 3 | section .text 4 | _start: 5 | mov rdi, 1 6 | mov rax, 60 7 | syscall 8 | 9 | -------------------------------------------------------------------------------- /ch02/false.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/false.o -------------------------------------------------------------------------------- /ch02/hello: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/hello -------------------------------------------------------------------------------- /ch02/hello.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | message: db 'hello, world!', 10 3 | 4 | section .text 5 | global _start 6 | 7 | _start: 8 | mov rax, 1 ; system call number (write) should be stored in accumulator rax 9 | mov rdi, 1 ; syscall arg 1: where to write (stdout's file descriptor) 10 | mov rsi, message ; syscall arg 2: what to write 11 | mov rdx, 14 ; syscall arg 3: how many bytes 12 | syscall 13 | -------------------------------------------------------------------------------- /ch02/hello.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/hello.o -------------------------------------------------------------------------------- /ch02/hello_proper_exit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/hello_proper_exit -------------------------------------------------------------------------------- /ch02/hello_proper_exit.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | message: db 'hello, world!', 10 3 | 4 | section .text 5 | global _start 6 | 7 | _start: 8 | mov rax, 1 ; system call number (write) should be stored in accumulator rax 9 | mov rdi, 1 ; syscall arg 1: where to write (stdout's file descriptor) 10 | mov rsi, message ; syscall arg 2: what to write 11 | mov rdx, 14 ; syscall arg 3: how many bytes 12 | syscall 13 | 14 | mov rax, 60 ; exit syscall number 15 | xor rdi, rdi 16 | syscall 17 | 18 | -------------------------------------------------------------------------------- /ch02/hello_proper_exit.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/hello_proper_exit.o -------------------------------------------------------------------------------- /ch02/lea_vs_mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/lea_vs_mov -------------------------------------------------------------------------------- /ch02/lea_vs_mov.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | thing: db '012345' 3 | 4 | section .text 5 | global _start 6 | _start: 7 | ; rsi <- address of label 'thing', a number 8 | mov rsi, thing 9 | 10 | ; rsi <- memory contents starting at thing's address 11 | mov rsi, [thing] 12 | 13 | ; rsi <- address of thing 14 | ; same as mov rsi, thing 15 | lea rsi, [thing] 16 | 17 | mov rax, 3 18 | 19 | ; rsi <- memory contents starting at thing + rax 20 | mov rsi, [thing + rax] 21 | 22 | ; rsi <- thing + rax 23 | ; can't do with single mov: 24 | ; mov rsi, thing 25 | ; add rsi, rax 26 | lea rsi, [thing + rax] 27 | -------------------------------------------------------------------------------- /ch02/lea_vs_mov.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/lea_vs_mov.o -------------------------------------------------------------------------------- /ch02/print_call: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/print_call -------------------------------------------------------------------------------- /ch02/print_call.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | newline_char: db 10 4 | codes: db '0123456789abcdef' 5 | 6 | section .text 7 | global _start 8 | 9 | print_newline: 10 | mov rax, 1 ; 'write' syscall indentifier 11 | mov rdi, 1 ; stdout file descriptor 12 | mov rsi, newline_char ; where we take data from 13 | mov rdx, 1 ; number of bytes to write 14 | syscall 15 | ret 16 | 17 | print_hex: 18 | mov rax, rdi ; puts function arg (char to print) into rax 19 | mov rdi, 1 ; stdout 20 | mov rdx, 1 ; number of bytes 21 | mov rcx, 64 ; how far we're shifting rax 22 | ; there's no return so we fall through to iterate 23 | 24 | iterate: 25 | push rax ; save initial rax value 26 | sub rcx, 4 27 | sar rax, cl ; shift to 60, 56, 52, ... 4, 0 28 | and rax, 0xf ; zero out all but the last 4 bits 29 | lea rsi, [codes + rax] ; take a hex digit character code 30 | 31 | mov rax, 1 32 | 33 | push rcx ; syscall will break rcx 34 | syscall ; rax = 1 (31) -- the write identifier 35 | ; rdi = 1 for stdout, rsi = character address 36 | pop rcx 37 | 38 | pop rax 39 | test rcx, rcx 40 | jnz iterate 41 | 42 | ret 43 | 44 | _start: 45 | mov rdi, 0x1122334455667788 46 | call print_hex 47 | call print_newline 48 | mov rax, 60 49 | xor rdi, rdi 50 | syscall 51 | 52 | -------------------------------------------------------------------------------- /ch02/print_call.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/print_call.o -------------------------------------------------------------------------------- /ch02/print_rax: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/print_rax -------------------------------------------------------------------------------- /ch02/print_rax.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | codes: 3 | db '01234567890ABCDEF' 4 | newline: 5 | db 10 6 | 7 | section .text 8 | global _start 9 | _start: 10 | ; number 1122... in hexadecimal format 11 | mov rax, 0x1122334455667788 12 | 13 | mov rdi, 1 14 | mov rdx, 1 15 | mov rcx, 64 16 | ; Each 4 bits should be output as one hex digit 17 | ; Use shift and bitwise AND to isolate them 18 | ; the result is the offset in 'codes' array 19 | .loop: 20 | push rax 21 | sub rcx, 4 22 | ; cl is a register, smallest part of rcx 23 | ; rax -- eax -- ax -- ah + al 24 | ; rcx -- ecx -- cx -- ch + cl 25 | sar rax, cl 26 | and rax, 0xf 27 | 28 | lea rsi, [codes + rax] 29 | mov rax, 1 30 | 31 | ; syscall leaves rcx and r11 changed 32 | push rcx 33 | syscall 34 | pop rcx 35 | 36 | pop rax 37 | ; test can be used for the fastest 'is it a zero?' check 38 | ; see docs for 'test' command 39 | test rcx, rcx 40 | jnz .loop 41 | 42 | mov rax, 1 43 | mov rsi, newline 44 | syscall 45 | 46 | mov rax, 60 ; invoke exit system call 47 | xor rdi, rdi 48 | syscall 49 | 50 | -------------------------------------------------------------------------------- /ch02/print_rax.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/print_rax.o -------------------------------------------------------------------------------- /ch02/strlen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/strlen -------------------------------------------------------------------------------- /ch02/strlen.asm: -------------------------------------------------------------------------------- 1 | global _start 2 | 3 | section .data 4 | 5 | test_string: db "abcdef", 0 ; the string to check for length, terminated with null 6 | 7 | section .text 8 | 9 | strlen: 10 | xor rax, rax ; zero out rax - we'll increment from 0 11 | 12 | .loop: 13 | cmp byte[rdi + rax], 0 ; look at string's byte, string is indexed starting at rdi 14 | 15 | je .end 16 | 17 | inc rax ; increment rax: increments str len size, and our rdi index 18 | jmp .loop 19 | 20 | .end: 21 | ret 22 | 23 | _start: 24 | mov rdi, test_string 25 | call strlen 26 | mov rdi, rax ; from command line, determine str len by calling echo $? 27 | ; $? returns exit code from last command 28 | mov rax, 60 29 | syscall 30 | 31 | -------------------------------------------------------------------------------- /ch02/strlen.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwasham/assembly-and-c/f704b452664b38041392cb87b7443da56e128686/ch02/strlen.o --------------------------------------------------------------------------------