nasm汇编

鉴于操作系统该门课程的放纵教学,企图用一两个小时使学生掌握繁多的汇编语言,也就浅浅记录一下汇编语言的学习吧

nasm基础

入门的一些小案例我觉得这里就很好,不知是往年哪位学长/学姐整理的,感谢万分。

SECTION

.data——有初始值的量,使用dX来申请空间大小

.bss——无初始值的量,使用resX来申请空间大小

.text——方法区,_start:相当于main(),没有call/ret/跳转语句默认向下执行

X Meaning size/bytes
b byte 1
w word 2
d double word 4
q quad word 8
t ten word 20

鉴于int是32位,即4bytes,因此我们可以用dd和resd为其申请空间

寄存器

寄存器.png

汇编语言中最为常见的怕不就是寄存器之间的操作了,根据寄存器的大小合理存放数据即可,当然如果寄存器中还存在有用数据时,记住push和pop也就行。

系统调用

系统调用对于指定寄存器以及其对应的值是有要求的,不过也因此固定且使用方便。

函数

call    quit

quit:
mov ebx, 0 ; exit时返回状态0 - 'No Errors'
mov eax, 1 ; 调用SYS_EXIT(OPCODE是1)
int 80h
ret

调用时call,一定要在调用结束时ret,段错误大多数是因为函数没有能够正确ret导致的

循环

结合判断的条件语句进行跳转,每次跳转至loop开始初,从而实现循环,大多数如i++的,可以用ecx等作为计数器
例:

    mov     ecx, 0  ; to count
xor eax, eax
.loop0:
mov al, byte[input+ecx]
cmp al, 32
je getlen1
mov byte[input1+ecx], al
inc ecx
jmp .loop0

解引用

store:
mov ecx, 0 ; use ecx to count

.loop1:
cmp ecx, dword[len1]
je .L1
mov ebx, dword[len1]
sub ebx, ecx
sub ebx, 1
movzx eax, byte[input1+ebx]
sub eax, '0'
xor ebx, ebx
mov dword[dividend+ecx*4], eax
inc ecx
jmp .loop1

使用[]进行解引用,前面加上dword等标明大小,[]内为需要解引用的地址,注意根据所占位数的不同,地址偏移量是不同的,如上面的byte[input1+ebx]和dword[dividend+ecx*4]

nasm实现大数除法

; file name:main.asm
; nasm -f elf main.asm
; ld -m elf_i386 main.o -o main
; ./main
; BigIntDiv

%include 'function.asm'

SECTION .data
msg1: db 'Please input two numbers', 0Ah, 0h ;inform the user to input two numbers
msg2: db 'The quotient is:', 0Ah, 0h ;tell the user next output is quotient
msg3: db 'The remainder is:', 0Ah, 0h
ZERO: db '0', 0Ah, 0h
error_msg: db 'Error!', 0Ah, 0h
dividend: times 102 dd 0
divisor: times 102 dd 0
quotient: times 102 dd 0

SECTION .bss
input resb 210
input1 resb 102 ;dividend
input2 resb 102 ;divisor
len1 resd 1
len2 resd 1
judge resd 1
index resd 1
printout resb 1


SECTION .text
global _start

; main()
_start:
; print msg1
mov eax, msg1
call sprint
; input the two numbers
; mov eax, 3
; mov ebx, 0
; mov ecx, input1
; mov edx, 102
; int 80h
; mov eax, 3
; mov ebx, 0
; mov ecx, input2
; mov edx, 102
; int 80h
mov eax, 3
mov ebx, 0
mov ecx, input
mov edx, 210
int 80h
mov ecx, 0 ; to count
xor eax, eax
.loop0:
mov al, byte[input+ecx]
cmp al, 32
je getlen1
mov byte[input1+ecx], al
inc ecx
jmp .loop0

; get the length of the first num
getlen1:
mov dword[len1], ecx
mov byte[input1+ecx], 0Ah
inc ecx
mov byte[input1+ecx], 0
mov esi, 0

.loop01:
mov al, byte[input+ecx]
cmp al, 0
je getlen2
mov byte[input2+esi], al
inc esi
inc ecx
jmp .loop01

; get the length of the second num
getlen2:
sub esi, 1
mov dword[len2], esi
add esi, 1
mov byte[input2+esi], 0

; use list to store the two numbers,inverted order
call store
; two simple case
; first:divisor == 0
xor eax, eax
mov al, byte[input2]
cmp al, '0'
je error
; second:len1 < len2
xor eax, eax
mov eax, dword[len1]
mov ebx, dword[len2]
cmp eax, ebx
jb case1
; the most complex part
; do BigIntDiv
jmp case2
; make sure exit
call quit





; ------------------------------
; functions and loop
; ------------------------------


; dividend[i] = input1[len1-1-i] - '0';
; divisor[i] = input2[len2-1-i] - '0';
store:
mov ecx, 0 ; use ecx to count

.loop1:
cmp ecx, dword[len1]
je .L1
mov ebx, dword[len1]
sub ebx, ecx
sub ebx, 1
movzx eax, byte[input1+ebx]
sub eax, '0'
xor ebx, ebx
mov dword[dividend+ecx*4], eax
inc ecx
jmp .loop1

.L1:
mov ecx, 0

.loop2:
cmp ecx, dword[len2]
je .L2
mov ebx, dword[len2]
sub ebx, ecx
sub ebx, 1
movzx eax, byte[input2+ebx]
sub eax, '0'
xor ebx, ebx
mov dword[divisor+ecx*4], eax
inc ecx
jmp .loop2

.L2:
ret


; -------------------------------
; error
error:
mov eax, error_msg
call sprint
call quit


; --------------------------------
; a simple case
case1:
mov eax, msg2
call sprint
mov eax, ZERO
call sprint
mov eax, msg3
call sprint
mov eax, input1
call sprint
call quit

; --------------------------------
; BigIntDiv
case2:
mov eax, dword[len1]
sub eax, dword[len2]
mov ecx, eax ;use ecx to represent i

.loop3:
cmp ecx, 0
jl output
push esi ;use esi as j


; while(1)
.loop4:
mov dword[judge], 1
mov esi, dword[len2]
sub esi, 1

; judge wheather enough
.loop5:
cmp esi, 0
jl .L4
mov eax, dword[len2]
add eax, ecx
cmp dword[dividend+4*eax], 0
jg .L4
xor eax, eax
mov eax, ecx
add eax, esi
mov ebx, dword[divisor+4*esi]
cmp dword[dividend+4*eax], ebx
jg .L4
jl .L3
dec esi
jmp .loop5

.L3:
mov dword[judge], 0

.L4:
cmp dword[judge], 0
je .L6
mov esi, 0

; do sub
.loop6:
cmp esi, dword[len2]
je .L5
mov eax, ecx
add eax, esi
mov ebx, dword[divisor+4*esi]
sub dword[dividend+4*eax], ebx
cmp dword[dividend+4*eax], 0
jl .L7
inc esi
jmp .loop6


.L5:
add dword[quotient+4*ecx], 1
jmp .loop4

.L6:
dec ecx
jmp .loop3

.L7:
add dword[dividend+4*eax], 10
add eax, 1
sub dword[dividend+4*eax], 1
inc esi
jmp .loop6



; output the two result
output:
pop esi
mov dword[index], 0
mov ecx, dword[len1]
sub ecx, dword[len2]

; the first number != 0 and get its index
.loop7:
cmp ecx, 0
jl .L8
cmp dword[quotient+4*ecx], 0
jne .L8
dec ecx
jmp .loop7

.L8:
mov dword[index], ecx
mov ecx, dword[index]
mov eax, msg2
call sprint

.loop8:
cmp ecx, 0
jl .L9
mov eax, dword[quotient+4*ecx]
and eax, 0fh
add eax, '0'
mov byte[printout], al
push ecx
mov edx, 1
mov ecx, printout
mov ebx, 1
mov eax, 4
int 80h
pop ecx
dec ecx
jmp .loop8


.L9:
cmp dword[index], -1
je .small_solve0
call sprintLn
.L11:
mov dword[index], 0
mov ecx, dword[len2]
sub ecx, 1

.loop9:
cmp ecx, 0
jl .L10
cmp dword[dividend+4*ecx], 0
jne .L10
dec ecx
jmp .loop9


.L10:
mov dword[index], ecx
mov ecx, dword[index]
mov eax, msg3
call sprint

.loop10:
cmp ecx, 0
jl .end
mov eax, dword[dividend+4*ecx]
and eax, 0fh
add eax, '0'
mov byte[printout], al
push ecx
mov edx, 1
mov ecx, printout
mov ebx, 1
mov eax, 4
int 80h
pop ecx
dec ecx
jmp .loop10

.end:
cmp dword[index], -1
je .small_solve
call sprintLn
call quit

; quotient = 0
.small_solve0:
mov eax, ZERO
call sprint
jmp .L11

; remainder = 0
.small_solve:
mov eax, ZERO
call sprint
call quit
; file name:function.asm
; define some useful functions
; 1. strlen(String msg):caluate the length of string
; 2. sprint(String msg): print the string
; 3. sprintLn():print \n
; 4. quit(): exit the process

;------------------------------------------
; int strlen(String message)
strlen: ; 返回值保存在EAX中
push ebx ; 将EBX中的值保存于栈上,因为strlen会使用该寄存器
mov ebx, eax ; 将EAX中msg的地址移EBX(现在二者指向内存中同一处)

nextchar:
cmp byte [eax], 0 ; 比较当前EAX地址处的内容是否为字符串结尾'\0'
jz finished ; ZF为1,跳出循环到finished
inc eax ; ZF不为1,EAX中的地址递增
jmp nextchar ; 继续循环

finished:
sub eax, ebx ; EBX - EAX,长度保存在EAX中
pop ebx ; 将栈上之前保存的值pop回EBX
ret ; 返回函数调用处


;------------------------------------------
; void sprint(String message)
; 打印字符串
sprint:
push edx ; 将EDX中的值保存于栈上
push ecx ; 将ECX中的值保存于栈上
push ebx ; 将EBX中的值保存于栈上
push eax ; 将EAX中的值保存于栈上,即参数string
call strlen ; 计算EAX中字符串长度,保存在EAX中

mov edx, eax ; 将长度移入到EDX
pop eax ; 恢复EAX值,即参数string

mov ecx, eax ; 将待打印string移入ECX
mov ebx, 1 ; 表示写入到标准输出STDOUT
mov eax, 4 ; 调用SYS_WRITE(操作码是4)
int 80h

pop ebx ; 恢复原来EBX中的值
pop ecx ; 恢复原来ECX中的值
pop edx ; 恢复原来EDX中的值
ret


;------------------------------------------
; void sprintLn
; 打印换行符
sprintLn:
push eax ; 将EAX中的值保存于栈上
mov eax, 0Ah ; 将换行符0Ah移入EAX
push eax ; 将换行符0Ah入栈,这样可以获取其地址
mov eax, esp ; 将当前栈指针ESP中的地址(指向0Ah)移入EAX
call sprint ; 调用sprint打印换行符
pop eax ; 换行符退栈
pop eax ; 恢复调用该函数前EAX中的值
ret ; 返回调用处


;------------------------------------------
; void quit()
; 退出程序
quit:
mov ebx, 0 ; exit时返回状态0 - 'No Errors'
mov eax, 1 ; 调用SYS_EXIT(OPCODE是1)
int 80h
ret