The assembly code has read and write functions. The differences of Linux and macOS are using the %ifdef MACHO64 which is the code for macOS %else is for Linux. For example the system call numbers are different and macho64 cannot use absolute addressing.
- 01_05-solution1.asm Select all
; solution - answer to Chapter 1 challenge
; Read decimal input, add 10, generate output
section .data
prompt db "How old are you? ",0
result db "In 10 years, you will be ",0
newline db 10 ; newline, \n
ten dq 10 ; used for multiplication
size equ 4 ; buffer size
azero equ 0x30 ; ASCII zero, '0'
anine equ 0x39 ; ASCII nine, '9'
nullchar equ 0x0 ; null terminator, \0
STDIN equ 0 ; standard input device
STDOUT equ 1 ; standard output device
%ifdef MACHO64
SYS_read equ 0x2000003 ; system call to read input macOS
SYS_write equ 0x2000004 ; system call to write message macOS
SYS_exit equ 0x2000001 ; system call to terminate program macOS
%else
SYS_read equ 0 ; system call to read input
SYS_write equ 1 ; system call to write message
SYS_exit equ 60 ; system call to terminate program
%endif
EXIT_OK equ 0 ; OK exit status
TENS dq 10000 ; tens table
dq 1000
dq 100
dq 10
dq 1
; uninitialized data
section .bss
input_buf resb size ; age input buffer
age resq 1 ; binary age value
output_buf resb size ; modified age string
; code
section .text
global _start
_start:
; output the prompt
mov rsi, prompt ; prompt string output
call print_string
; read input
mov rbx, input_buf ; age string storage
call input
; translate decimal string to binary
mov rsi, input_buf ; where string value is stored
call decimal2binary
cmp al, 0 ; check for bogus input
jz start_exit ; bail if so
add rax, 10 ; add 10 to the value
%ifdef MACHO64
mov [rel age], rax ; store result
%else
mov [age], rax ; store result
%endif
; output the final prompt
mov rsi, result ; First part of the string
call print_string
; convert the value in 'age' to a string
%ifdef MACHO64
mov rbx, [rel age] ; value
%else
mov rbx, [age] ; value
%endif
mov rsi, output_buf ; modified string
call binary2decimal
; output the age-value string
mov rsi, output_buf
call print_value ; use this function to strip leading
; zeros
; exit program
start_exit:
mov rax, SYS_exit ; system exit call
mov rdi, EXIT_OK
syscall
; end of _start
; functions
;--------;
; output a null terminated string in rsi
print_string:
cmp byte [rsi], nullchar ; end-of-string test
je print_string_exit ; bail on null char
mov rdx, 1 ; number of characters to write
mov rdi, STDOUT ; to standard output
mov rax, SYS_write ; Write characters(s)
syscall
inc rsi ; next character
jmp print_string ; keep looping
print_string_exit:
ret
;--------;
; grab standard input in the buffer in rbx
input:
mov r12, 1 ; buffer position
read_char:
mov rsi, rbx ; input char storage
mov rdx, 1 ; character count
mov rdi, STDIN ; from standard input
mov rax, SYS_read ; read into rsi (rbx)
syscall
cmp byte [rbx], 10 ; is character read newline?
je input_exit ; finish, don't store newline
inc rbx ; next byte in the buffer
inc r12 ; up the character count
cmp r12, size ; check buffer size
jl read_char ; keep looping if room
; otherwise, fall through:
input_exit:
mov byte [rbx], 0 ; cap the string
ret
;--------;
; translate string input at rsi into binary value in rax
; if garbage input, returned value is zero
decimal2binary:
mov rax, 0 ; initial value
d2b0:
mov rbx, 0 ; initialize bax
mov bl, byte [rsi] ; character, digit
; filter out non-digit values
cmp bl, azero
jl d2b_exit ; exit on character < '0'
cmp bl, anine
jg d2b_exit ; exit on character > '9'
sub bl, azero ; convert from ASCII to binary
%ifdef MACHO64
mul qword [rel ten] ; multiply rax by 10
%else
mul qword [ten] ; multiply rax by 10
%endif
add rax, rbx ; add new value
inc rsi ; next char
cmp byte [rsi], nullchar ; end of string?
jnz d2b0 ; if not, keep looping
d2b_exit:
ret
;--------;
; generate a string at rsi representing the value in rbx
binary2decimal:
mov rdi, TENS ; reference comparision table
; values here are subtracted from
; rbx to calculate base 10 digits
b2d0:
xor al, al ; zero out al; al stores the character
mov rcx, [rdi] ; get power of ten
b2d1:
or al, al ; clear carry bit (for jb)
sub rbx, rcx ; subtract power of ten
jb b2d2 ; if <0, the count in al is the value
inc al ; decimal value++
jmp b2d1 ; keep subtracting
b2d2:
add al, azero ; make al ASCII
add rbx, rcx ; recover from last subtraction
mov byte [rsi], al ; add character to the string
inc rsi ; next string position
add rdi, 8 ; next value in TENS table
cmp rcx, 1 ; end of table?
jnz b2d0 ; loop again if not
mov byte [rsi], nullchar ; terminate string
ret
;--------;
; output a null-terminated string in rsi
; strip any leading zero characters
print_value:
cmp byte [rsi], nullchar ; always check for null char
je print_value_exit ; and exit; string empty
cmp byte [rsi], azero ; ASCII zero
jne pv1 ; if the char isnt zero, continue
inc rsi ; otherwise, check next character
jmp print_value ; keep looping
pv1: ; process the remaining non-zero digits
cmp byte [rsi], nullchar ; null char terminator
je print_value_exit ; if true, exit
mov rdx, 1 ; chars to write
mov rdi, STDOUT ; standard output
mov rax, SYS_write ; write characters
syscall
inc rsi ; next char
jmp pv1 ; loop until null char
print_value_exit:
mov rsi, newline ; newline defined as \n
mov rdx, 1 ; write 1 char
mov rax, SYS_write ; write character
mov rdi, STDOUT ; to standard output
syscall
ret
; end
makefile for Linux
- makefile Select all
#makefile for Linux
filename_prefix := 01_05-
src_files := $(wildcard *.asm)
obj_files := $(src_files:.asm=.o)
prog_files := $(subst $(filename_prefix), ,$(basename $(src_files)))
all: $(prog_files)
$(prog_files): % : $(filename_prefix)%.o
ld -o $@ $< -arch x86_64
$(filter %.o,$(obj_files)): %.o : %.asm
nasm -g -f elf64 $< -o $@
.PHONY: clean
clean:
rm -f $(obj_files) $(prog_files)
makefile for macOS
- makefile Select all
#makefile for macOS
filename_prefix := 01_05-
src_files := $(wildcard *.asm)
obj_files := $(src_files:.asm=.o)
prog_files := $(subst $(filename_prefix), ,$(basename $(src_files)))
all: $(prog_files)
$(prog_files): % : $(filename_prefix)%.o
ld -no_pie -macosx_version_min 11.0.0 -o $@ $< -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch x86_64
codesign --entitlement entitlements --force -s - $@
$(filter %.o,$(obj_files)): %.o : %.asm
nasm -g -f macho64 -dMACHO64 $< -o $@
.PHONY: clean
clean:
rm -f $(obj_files) $(prog_files)
In order to debug in macOS, codesign entitlement file is needed.
- entitlements Select all
<?xml version="1.0" encoding="UTF-8">
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
For linux, it is required to install these packages "sudo apt install build-essentials nasm gdb lldb"
For macOS, it is required to install Xcode and Command Line Tools and Homebrew package "brew install nasm"
It is possible to build, run and debug x86_64 program on M1 Macs, if Rosetta is installed.