Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 71 additions & 97 deletions chapter10/integer2string.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,118 +2,92 @@
# for display
#
#INPUT: A buffer large enough to hold the largest
# possible number
# An integer to convert
# possible number (passed as a pointer)
# An integer to convert (passed as a value)
#
#OUTPUT: The buffer will be overwritten with the
# decimal string
#
#Variables:
#
# %ecx will hold the count of characters processed
# %eax will hold the current value
# %edi will hold the base (10)
#
.code32
.equ ST_VALUE, 8
.equ ST_BUFFER, 12
# %rcx will hold the count of characters processed
# %rax will hold the current value / quotient
# %rdi will hold the base (10) for division
# %rdx will hold the remainder / character value / buffer pointer

# Stack argument offsets from %rbp
.equ ST_VALUE_ARG, 16 # Integer value to convert
.equ ST_BUFFER_ARG, 24 # Pointer to output buffer

.globl integer2string
.type integer2string, @function
.globl integer2string
.type integer2string, @function
integer2string:
#Normal function beginning
pushl %ebp
movl %esp, %ebp
# Standard function prologue
pushq %rbp
movq %rsp, %rbp

#Current character count
movl $0, %ecx
# Current character count
movq $0, %rcx

#Move the value into position
movl ST_VALUE(%ebp), %eax
# Move the value to convert into %rax
movq ST_VALUE_ARG(%rbp), %rax

#When we divide by 10, the 10
#must be in a register or memory location
movl $10, %edi
# Divisor 10 must be in a register or memory location for divq
movq $10, %rdi

conversion_loop:
#Division is actually performed on the
#combined %edx:%eax register, so first
#clear out %edx
movl $0, %edx

#Divide %edx:%eax (which are implied) by 10.
#Store the quotient in %eax and the remainder
#in %edx (both of which are implied).
divl %edi

#Quotient is in the right place. %edx has
#the remainder, which now needs to be converted
#into a number. So, %edx has a number that is
#0 through 9. You could also interpret this as
#an index on the ASCII table starting from the
#character '0'. The ascii code for '0' plus zero
#is still the ascii code for '0'. The ascii code
#for '0' plus 1 is the ascii code for the
#character '1'. Therefore, the following
#instruction will give us the character for the
#number stored in %edx
addl $'0', %edx

#Now we will take this value and push it on the
#stack. This way, when we are done, we can just
#pop off the characters one-by-one and they will
#be in the right order. Note that we are pushing
#the whole register, but we only need the byte
#in %dl (the last byte of the %edx register) for
#the character.
pushl %edx

#Increment the digit count
incl %ecx

#Check to see if %eax is zero yet, go to next
#step if so.
cmpl $0, %eax
je end_conversion_loop

#%eax already has its new value.

jmp conversion_loop
# divq uses %rdx:%rax. Zero %rdx before each division.
movq $0, %rdx

# Divide %rdx:%rax by %rdi (10).
# Quotient stored in %rax, remainder in %rdx.
divq %rdi

# Remainder in %rdx (0-9) needs to be converted to ASCII '0'-'9'.
addq $'0', %rdx

# Push the character (in %dl, but push whole %rdx) onto the stack.
# This reverses the order of digits.
pushq %rdx

# Increment the digit count
incq %rcx

# Check if quotient %rax is zero. If so, conversion is done.
cmpq $0, %rax
je end_conversion_loop

# %rax (quotient) becomes the new number to convert for the next iteration.
jmp conversion_loop

end_conversion_loop:
#The string is now on the stack, if we pop it
#off a character at a time we can copy it into
#the buffer and be done.
# Digits are on the stack in reverse order. Pop them one by one
# into the buffer.

#Get the pointer to the buffer in %edx
movl ST_BUFFER(%ebp), %edx
# Get the pointer to the output buffer into %rdx
movq ST_BUFFER_ARG(%rbp), %rdx
copy_reversing_loop:
#We pushed a whole register, but we only need
#the last byte. So we are going to pop off to
#the entire %eax register, but then only move the
#small part (%al) into the character string.
popl %eax
movb %al, (%edx)

#Decreasing %ecx so we know when we are finished
decl %ecx
#Increasing %edx so that it will be pointing to
#the next byte
incl %edx

#Check to see if we are finished
cmpl $0, %ecx
#If so, jump to the end of the function
je end_copy_reversing_loop
#Otherwise, repeat the loop
jmp copy_reversing_loop
# Pop character from stack into %rax. We only need the byte in %al.
popq %rax
movb %al, (%rdx) # Move the character byte into the buffer

# Decrement digit count (%rcx)
decq %rcx
# Increment buffer pointer (%rdx)
incq %rdx

# Check if all digits have been copied
cmpq $0, %rcx
# If so, jump to the end of the function
je end_copy_reversing_loop
# Otherwise, repeat the loop
jmp copy_reversing_loop

end_copy_reversing_loop:
#Done copying. Now write a null byte and return
movb $0, (%edx)
movl %ebp, %esp
popl %ebp
ret
# Done copying. Write a null terminator.
movb $0, (%rdx)
# Standard function epilogue
movq %rbp, %rsp
popq %rbp
ret
1 change: 1 addition & 0 deletions chapter2/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/bin/bash
as exit.s -o exit.o
ld exit.o -o exit
./exit
Expand Down
6 changes: 3 additions & 3 deletions chapter2/exit.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
.globl _start

_start:
movl $1, %eax
movl $0, %ebx # 返回值
int $0x80
movq $60, %rax # SYS_EXIT syscall number for amd64
movq $0, %rdi # Exit status 0
syscall
27 changes: 14 additions & 13 deletions chapter3/maximum.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,30 @@
.section .data

data_items:
.long 3,67,12,45,21,24,63,21,11,6,4,0
.long 3,67,12,45,21,24,63,21,11,6,4,0 # Array of 4-byte integers

.section .text

.globl _start

_start:
movl $0, %edi
movl data_items(,%edi,4), %eax
movl %eax, %ebx
movq $0, %rdi # Initialize index to 0 (use %rdi as index)
movl data_items(,%rdi,4), %eax # Load first data item into %eax (as it's a .long)
movl %eax, %ebx # Current maximum in %ebx (still 32-bit as data is .long)

start_loop:
cmpl $0, %eax # 用0作为数据尾部标识
je loop_exit
cmpl $0, %eax # Check if current element is 0 (end of data)
je loop_exit # Jump if equal to 0

incl %edi
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax
jle start_loop # 不大于则跳转(jump if less or equal),相当于<=
incq %rdi # Increment index (%rdi is 64-bit)
movl data_items(,%rdi,4), %eax # Load next data item into %eax
cmpl %ebx, %eax # Compare current item with current maximum
jle start_loop # Jump if less than or equal (keep current max)

movl %eax, %ebx # 更新最大值
movl %eax, %ebx # Update current maximum
jmp start_loop

loop_exit:
movl $1, %eax # 1是exit()系统调用
int $0x80
movq $60, %rax # SYS_EXIT syscall number for amd64
movslq %ebx, %rdi # Move the 32-bit max value from %ebx to %rdi, sign-extended to 64-bit for exit status
syscall
7 changes: 3 additions & 4 deletions chapter4/build_factorial.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
as --32 factorial.s -o factorial.o

ld -m elf_i386 factorial.o -o factorial

#!/bin/bash
as factorial.s -o factorial.o
ld factorial.o -o factorial
./factorial
echo $?
40 changes: 22 additions & 18 deletions chapter4/factorial.s
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
# 计算阶乘
.code32

.section .data

.section .text

.globl _start
_start:
pushl $4
pushq $4 # Push argument for factorial (will be 64-bit value on stack)
call factorial
movl %eax, %ebx
addq $8, %rsp # Clean up stack after call (1 argument * 8 bytes)

movq %rax, %rdi # Factorial result (in %rax) is the exit status for %rdi

movl $1, %eax
int $0x80
movq $60, %rax # SYS_EXIT syscall number
syscall

.type factorial, @function
factorial:
pushl %ebp
movl %esp, %ebp
pushq %rbp # Save old base pointer
movq %rsp, %rbp # Set new base pointer

movl 8(%ebp), %eax
movq 16(%rbp), %rax # Get argument n (16 bytes offset from %rbp: 8 for ret addr, 8 for saved %rbp)

cmpl $1, %eax
cmpq $1, %rax # Compare n with 1
je end_factorial

decl %eax
pushl %eax
decq %rax # n-1
pushq %rax # Push n-1 for recursive call

call factorial
movl 8(%ebp), %ebx
imull %ebx, %eax
call factorial # Recursive call: factorial(n-1)
addq $8, %rsp # Clean up stack after recursive call

end_factorial:
movl %ebp, %esp
popl %ebp
ret
# Result of factorial(n-1) is in %rax
# n (original argument) needs to be retrieved again
movq 16(%rbp), %rbx # Get original n into %rbx
imulq %rbx, %rax # rax = rax * rbx ( (n-1)! * n )

end_factorial:
movq %rbp, %rsp # Restore stack pointer
popq %rbp # Restore old base pointer
ret # Return, result in %rax
12 changes: 7 additions & 5 deletions chapter5/build.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
as --32 toupper.s -o toupper.o

ld -m elf_i386 toupper.o -o toupper

./toupper toupper.s toupper.txt
#!/bin/bash
as toupper.s -o toupper.o
ld toupper.o -o toupper
# Test with itself and output to toupper.txt
# Make sure this test is valid and input file exists (e.g. toupper.s)
./toupper toupper.s toupper.txt
echo "toupper_exit_status=$?"
Loading