Updated with Lesson 5: Include files for subroutines.
This commit is contained in:
parent
f0f1892c8c
commit
f3603d7835
|
@ -2,9 +2,7 @@
|
||||||
*~
|
*~
|
||||||
\#*
|
\#*
|
||||||
.\#*
|
.\#*
|
||||||
hello32
|
hello
|
||||||
hello64
|
strlen
|
||||||
counted-hello32
|
subroutines
|
||||||
counted-hello64
|
includes
|
||||||
subroutine-hello32
|
|
||||||
subroutine-hello64
|
|
||||||
|
|
|
@ -23,6 +23,9 @@ strlen: strlen.o ## Build Lesson 2: Determine length programmatically
|
||||||
subroutines: subroutines.o ## Build Lesson 3: Separate strlen() and puts() into subroutine.
|
subroutines: subroutines.o ## Build Lesson 3: Separate strlen() and puts() into subroutine.
|
||||||
$(LD) -m $(LINK_32) -o $@ $<
|
$(LD) -m $(LINK_32) -o $@ $<
|
||||||
|
|
||||||
|
includes: includes.o ## Build Lesson 4: Separate strlen() and puts() into their own files.
|
||||||
|
$(LD) -m $(LINK_32) -o $@ $<
|
||||||
|
|
||||||
help: run-help ## Print this helpful message (default)
|
help: run-help ## Print this helpful message (default)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
;; This is an includes file. It doesn't have its own compilation
|
||||||
|
;; capabilities. There is no namespacing in assembly language;
|
||||||
|
;; try not to use these names in your own code.
|
||||||
|
|
||||||
|
;; sys/unistd_32.h
|
||||||
|
%define SYS_write 4
|
||||||
|
%define SYS_exit 1
|
||||||
|
|
||||||
|
;; unistd.h
|
||||||
|
%define STDOUT 1
|
||||||
|
|
||||||
|
;; strlen() function. Takes eax as an argument - the pointer to
|
||||||
|
;; the initial string. Returns eax as the result - the length of
|
||||||
|
;; the string.
|
||||||
|
|
||||||
|
strlen:
|
||||||
|
push ebx ; We'll be borrowing this register, so we put its
|
||||||
|
; current value on the stack.
|
||||||
|
mov ebx, eax
|
||||||
|
|
||||||
|
;; Note that even though these have underscores to indicate that
|
||||||
|
;; outside users should not use them, they're still globally
|
||||||
|
;; accesible in this program's namespace. A user could
|
||||||
|
;; theoretically call _strlen_done from anywhere. Assembly gives
|
||||||
|
;; you ALL the opportunities to shoot yourself in the foot!
|
||||||
|
_strlen_next:
|
||||||
|
cmp byte [eax], 0
|
||||||
|
jz _strlen_done
|
||||||
|
inc eax
|
||||||
|
jmp _strlen_next
|
||||||
|
|
||||||
|
_strlen_done:
|
||||||
|
sub eax, ebx
|
||||||
|
pop ebx ; Restore the register
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; Puts() function - puts a string to the console. Takes EAX as
|
||||||
|
;; its only argument - the pointer to the beginning of the string.
|
||||||
|
|
||||||
|
puts:
|
||||||
|
push edx
|
||||||
|
push ecx
|
||||||
|
push ebx
|
||||||
|
push eax
|
||||||
|
call strlen ; Uses EAX as the pointer to the beginning of the string.
|
||||||
|
; Returns EAX as the length of the string
|
||||||
|
|
||||||
|
mov edx, eax ; Move the length of the string into EDX, where WRITE expects
|
||||||
|
pop eax ; Restore EAX from the stack
|
||||||
|
|
||||||
|
push eax ; Put the value BACK on the stack.
|
||||||
|
mov ecx, eax ; Put the pointer to the message into ECX, where WRITE expects
|
||||||
|
mov ebx, STDOUT
|
||||||
|
mov eax, SYS_write
|
||||||
|
int 80h
|
||||||
|
pop eax ; Restore registers in reverse order.
|
||||||
|
pop ebx
|
||||||
|
pop ecx
|
||||||
|
pop edx
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; exit(). Straight from the original.
|
||||||
|
|
||||||
|
exit_program:
|
||||||
|
mov ebx, 0
|
||||||
|
mov eax, SYS_exit
|
||||||
|
int 80h
|
||||||
|
|
37
x86/hello.s
37
x86/hello.s
|
@ -1,31 +1,46 @@
|
||||||
;; Hello World Program #1
|
;; Hello World Program #2
|
||||||
;; Compile with: nasm -f elf hello.s
|
;; Compile with: nasm -f elf hello.s
|
||||||
;; Link with: ld -m elf_i386 -o hello hello.o
|
;; Link with: ld -m elf_i386 -o hello hello.o
|
||||||
;; Run with: ./hello
|
;; Run with: ./hello
|
||||||
|
|
||||||
|
;; The following includes are derived from:
|
||||||
;; sys/unistd_32.h
|
;; sys/unistd_32.h
|
||||||
%define SYS_write 4
|
%define SYS_write 4
|
||||||
%define SYS_exit 1
|
%define SYS_exit 1
|
||||||
|
|
||||||
|
;; The following includes are derived from:
|
||||||
;; unistd.h
|
;; unistd.h
|
||||||
%define STDOUT 1
|
%define STDOUT 1
|
||||||
|
|
||||||
|
;;; 'global' is the directive that tells the NASM the address of the
|
||||||
|
;;; first instruction to run.
|
||||||
|
|
||||||
|
global _start
|
||||||
|
|
||||||
section .data
|
section .data
|
||||||
msg db "Hello You Beautiful Human", 0Ah
|
msg db "Hello You Beautiful Human", 0Ah
|
||||||
len equ $-msg ; NASM-supplied macro
|
len equ $-msg ; The $ is a NASM helper that means
|
||||||
|
; "The address of the current
|
||||||
|
; instruction. 'equ' is a NASM
|
||||||
|
; macro that performs the math and
|
||||||
|
; places in the 'len' constant the
|
||||||
|
; difference between the start of the
|
||||||
|
; current instruction and the 'msg'.
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
global _start
|
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
mov edx, len
|
mov edx, len ; Mov the address of 'len' to register EDX
|
||||||
mov ecx, msg ; Address of the message (not the content)
|
mov ecx, msg ; Mov the address of the message (not the content)
|
||||||
mov ebx, STDOUT ; using STDOUT (see definition above)
|
mov ebx, STDOUT ; using STDOUT (see definition above)
|
||||||
mov eax, SYS_write ; Using WRITE in 32-bit mode?
|
mov eax, SYS_write ; Acesss WRITE in 32-bit Linux
|
||||||
int 80h ; Interrupt target. The 'h' means 'hexidecimal'
|
int 80h ; Call the kernel to run the WRITE command.
|
||||||
|
|
||||||
mov ebx, 0
|
;; Note that it's always register AX that we use to tell the kernel
|
||||||
mov eax, SYS_exit
|
;; what we want it to do. Depending on the kernel instruction, other
|
||||||
int 80h
|
;; registers may be used to fill out the command.
|
||||||
|
|
||||||
|
mov ebx, 0 ; Mov '0' to be our exit code
|
||||||
|
mov eax, SYS_exit ; Access EXIT
|
||||||
|
int 80h ; Call the kernel to run the EXIT command.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
;; Hello World Program #5: Includes
|
||||||
|
|
||||||
|
%include "functions.asm"
|
||||||
|
|
||||||
|
section .data
|
||||||
|
msg db "Cool, we can now do something like structured programming.", 0Ah, 00h
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov eax, msg ; Put the address of our message into eax.
|
||||||
|
call puts ; Calls strlen internally!
|
||||||
|
call exit_program
|
||||||
|
|
16
x86/strlen.s
16
x86/strlen.s
|
@ -10,11 +10,12 @@
|
||||||
;; unistd.h
|
;; unistd.h
|
||||||
%define STDOUT 1
|
%define STDOUT 1
|
||||||
|
|
||||||
|
global _start
|
||||||
|
|
||||||
section .data
|
section .data
|
||||||
msg db "Hello You Beautiful Human, You're Looking Fine Today!", 0Ah, 00h
|
msg db "Hello You Beautiful Human, You're Looking Fine Today!", 0Ah, 00h
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
global _start
|
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
mov ebx, msg ; Move the address of the message into ebx
|
mov ebx, msg ; Move the address of the message into ebx
|
||||||
|
@ -28,16 +29,19 @@ nextchar:
|
||||||
;; sub does. cmp sets flags; does sub? This is why 'jz' works,
|
;; sub does. cmp sets flags; does sub? This is why 'jz' works,
|
||||||
;; because if they're equal the result of subtraction is zero.
|
;; because if they're equal the result of subtraction is zero.
|
||||||
jz counted ; Jump if the zero flag set
|
jz counted ; Jump if the zero flag set
|
||||||
inc eax
|
inc eax ; Increment the counter
|
||||||
jmp nextchar
|
jmp nextchar ; Jump to the beginning of the loop
|
||||||
|
|
||||||
counted:
|
counted:
|
||||||
sub eax, ebx ; Subtract the end from the start, and the result goes into the start
|
sub eax, ebx ; Subtract the end from the start, and
|
||||||
|
; the result goes into the start
|
||||||
|
|
||||||
|
mov edx, eax ; syswrite needs that register for something
|
||||||
|
; else! Man, picking registers is hard.
|
||||||
|
|
||||||
mov edx, eax ; syswrite needs that register for something else! Man, picking registers is hard.
|
|
||||||
mov ecx, msg ; Address of the message (not the content)
|
mov ecx, msg ; Address of the message (not the content)
|
||||||
mov ebx, STDOUT ; using STDOUT (see definition above)
|
mov ebx, STDOUT ; using STDOUT (see definition above)
|
||||||
mov eax, SYS_write ; Using WRITE in 32-bit mode?
|
mov eax, SYS_write ; Using WRITE in 32-bit mode.
|
||||||
int 80h ; Interrupt target. The 'h' means 'hexidecimal'
|
int 80h ; Interrupt target. The 'h' means 'hexidecimal'
|
||||||
|
|
||||||
mov ebx, 0
|
mov ebx, 0
|
||||||
|
|
|
@ -23,7 +23,6 @@ _start:
|
||||||
call exit
|
call exit
|
||||||
|
|
||||||
strlen:
|
strlen:
|
||||||
; will probably want its state restored correctly, right?
|
|
||||||
mov edx, ecx
|
mov edx, ecx
|
||||||
|
|
||||||
strlen_next:
|
strlen_next:
|
||||||
|
@ -33,7 +32,7 @@ strlen_next:
|
||||||
jmp strlen_next
|
jmp strlen_next
|
||||||
|
|
||||||
strlen_done:
|
strlen_done:
|
||||||
sub edx, ecx ; Straight from the counted-hello file
|
sub edx, ecx ; Straight from the strlen file
|
||||||
ret
|
ret
|
||||||
|
|
||||||
;; Takes EAX as the address of the message and EDX as the
|
;; Takes EAX as the address of the message and EDX as the
|
||||||
|
|
|
@ -23,6 +23,9 @@ strlen: strlen.o ## Build Lesson 2: Determine length programmatically
|
||||||
subroutines: subroutines.o ## Build Lesson 3: Separate strlen() and puts() into subroutine.
|
subroutines: subroutines.o ## Build Lesson 3: Separate strlen() and puts() into subroutine.
|
||||||
$(LD) -m $(LINK_64) -o $@ $<
|
$(LD) -m $(LINK_64) -o $@ $<
|
||||||
|
|
||||||
|
includes: includes.o ## Build Lesson 4: Separate strlen() and puts() into their own files.
|
||||||
|
$(LD) -m $(LINK_64) -o $@ $<
|
||||||
|
|
||||||
help: run-help ## Print this helpful message (default)
|
help: run-help ## Print this helpful message (default)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
;; This is an includes file. It doesn't have its own compilation
|
||||||
|
;; capabilities. There is no namespacing in assembly language;
|
||||||
|
;; try not to use these names in your own code.
|
||||||
|
|
||||||
|
;; sys/unistd_32.h
|
||||||
|
%define SYS_write 1
|
||||||
|
%define SYS_exit 60
|
||||||
|
|
||||||
|
;; unistd.h
|
||||||
|
%define STDOUT 1
|
||||||
|
|
||||||
|
;; strlen() function. Takes rdx as an argument - the pointer to
|
||||||
|
;; the initial string. Returns rdx as the result - the length of
|
||||||
|
;; the string.
|
||||||
|
|
||||||
|
strlen:
|
||||||
|
push rsi
|
||||||
|
mov rsi, rdx
|
||||||
|
|
||||||
|
strlen_next:
|
||||||
|
cmp byte [rdx], 0
|
||||||
|
jz strlen_done
|
||||||
|
inc rdx
|
||||||
|
jmp strlen_next
|
||||||
|
|
||||||
|
strlen_done:
|
||||||
|
sub rdx, rsi
|
||||||
|
pop rsi
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; puts() function - puts a string to the console. Takes RSI as
|
||||||
|
;; its only argument - the pointer to the beginning of the string.
|
||||||
|
|
||||||
|
puts:
|
||||||
|
push rdx ; RDX (data) is used as the length of the message
|
||||||
|
push rax ; RAX is the instruction to WRITE
|
||||||
|
push rdi ; RDI (destination) is used as an instruction to WRITE
|
||||||
|
|
||||||
|
mov rdx, rsi
|
||||||
|
call strlen
|
||||||
|
|
||||||
|
;; Because I chose to use RDX as the target for strlen, I don't need to
|
||||||
|
;; do the pop/push dance as I did in the 32-bit version.
|
||||||
|
|
||||||
|
mov rdi, STDOUT ; using STDOUT (see definition above)
|
||||||
|
mov rax, SYS_write ; Using WRITE in 32-bit mode?
|
||||||
|
syscall
|
||||||
|
|
||||||
|
;; Likewise, because I chose RSI as the Source (pointer) to my
|
||||||
|
;; message, it remains unchanged during the lifetime of this
|
||||||
|
;; routine (unless there are any bugs in strlen) and I don't
|
||||||
|
;; have to restore it either.
|
||||||
|
|
||||||
|
pop rdi
|
||||||
|
pop rax
|
||||||
|
pop rdx
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; exit(). Straight from the original.
|
||||||
|
|
||||||
|
exit_program:
|
||||||
|
mov rdi, 0
|
||||||
|
mov rax, SYS_exit
|
||||||
|
syscall
|
|
@ -1,30 +1,45 @@
|
||||||
;; Hello World Program #1
|
;; Hello World Program #2
|
||||||
;; Compile with: nasm -f elf64 hello.s
|
;; Compile with: nasm -f elf64 hello.s
|
||||||
;; Link with: ld -o hello hello.o
|
;; Link with: ld -m elf_x86_64 -o hello hello.o
|
||||||
;; Run with: ./hello
|
;; Run with: ./hello
|
||||||
|
|
||||||
|
;; The following includes are derived from:
|
||||||
;; sys/unistd_64.h
|
;; sys/unistd_64.h
|
||||||
%define SYS_write 1
|
%define SYS_write 1
|
||||||
%define SYS_exit 60
|
%define SYS_exit 60
|
||||||
|
|
||||||
|
;; The following includes are derived from:
|
||||||
;; unistd.h
|
;; unistd.h
|
||||||
%define STDOUT 1
|
%define STDOUT 1
|
||||||
|
|
||||||
|
;;; 'global' is the directive that tells the NASM the address of the
|
||||||
|
;;; first instruction to run.
|
||||||
|
|
||||||
|
global _start
|
||||||
|
|
||||||
section .data
|
section .data
|
||||||
msg db "Hello You Beautiful Human", 0Ah
|
msg db "Hello You Beautiful Human", 0Ah
|
||||||
len equ $-msg ; NASM-supplied macro
|
len equ $-msg ; The $ is a NASM helper that means
|
||||||
|
; "The address of the current
|
||||||
|
; instruction. 'equ' is a NASM
|
||||||
|
; macro that performs the math and
|
||||||
|
; places in the 'len' constant the
|
||||||
|
; difference between the start of the
|
||||||
|
; current instruction and the 'msg'.
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
global _start
|
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
mov rdx, len ; Length of the message
|
mov rdx, len ; Mov the address of 'len' to register RDX
|
||||||
mov rsi, msg ; Address of the message
|
mov rsi, msg ; Mov the address of the message to RSI (Source Index)
|
||||||
mov rdi, STDOUT ; using STDOUT (see definition above)
|
mov rdi, STDOUT ; using STDOUT (see definition above)
|
||||||
mov rax, SYS_write ; Using WRITE in 32-bit mode?
|
mov rax, SYS_write ; Access WRITE in 64-bit linux
|
||||||
syscall
|
syscall ; Call the kernel to run the WRITE command.
|
||||||
|
|
||||||
mov rdi, 0
|
;; Note that it's always register AX that we use to tell the kernel
|
||||||
mov rax, SYS_exit
|
;; what we want it to do. Depending on the kernel instruction, other
|
||||||
syscall
|
;; registers may be used to fill out the command.
|
||||||
|
|
||||||
|
mov rdi, 0 ; Mov '0' to be our exit code
|
||||||
|
mov rax, SYS_exit ; Access the exit command
|
||||||
|
syscall ; Call the kernel to run the EXIT command.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
;; Hello World Program #5: Includes
|
||||||
|
|
||||||
|
%include "functions.asm"
|
||||||
|
|
||||||
|
section .data
|
||||||
|
msg db "Cool, we can now do something like structured programming - 64 bit version.", 0Ah, 00h
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov rsi, msg ; Put the address of our message into eax.
|
||||||
|
call puts ; Calls strlen internally!
|
||||||
|
call exit_program
|
|
@ -1,7 +1,7 @@
|
||||||
;; Hello World Program #1
|
;; Hello World Program #3
|
||||||
;; Compile with: nasm -f elf64 hello.s
|
;; Compile with: nasm -f elf64 strlen.s
|
||||||
;; Link with: ld -o hello hello.o
|
;; Link with: ld -o strlen strlen.o
|
||||||
;; Run with: ./hello
|
;; Run with: ./strlen
|
||||||
|
|
||||||
;; sys/unistd_64.h
|
;; sys/unistd_64.h
|
||||||
%define SYS_write 1
|
%define SYS_write 1
|
||||||
|
@ -10,19 +10,20 @@
|
||||||
;; unistd.h
|
;; unistd.h
|
||||||
%define STDOUT 1
|
%define STDOUT 1
|
||||||
|
|
||||||
|
global _start
|
||||||
|
|
||||||
section .data
|
section .data
|
||||||
msg db "Hello You Beautiful Human, You're Looking Mighty Fine!", 0Ah, 00h
|
msg db "Hello You Beautiful Human, You're Looking Mighty Fine!", 0Ah, 00h
|
||||||
|
|
||||||
section .text
|
|
||||||
global _start
|
|
||||||
|
|
||||||
|
section .text
|
||||||
_start:
|
_start:
|
||||||
mov rsi, msg ; Move the address of the message into rsi
|
mov rsi, msg ; Move the address of the message into rsi
|
||||||
mov rax, rsi ; Move the address of the message into rax
|
mov rax, rsi ; Move the address of the message into rax
|
||||||
; (Register-to-register copying is faster that a constant!)
|
; (Register-to-register copying is faster that a constant!)
|
||||||
|
|
||||||
nextchar:
|
nextchar:
|
||||||
cmp byte [rax], 0 ; Compare the byte pointed to by eax with zero
|
cmp byte [rax], 0 ; Compare the byte pointed to by rax with zero
|
||||||
|
|
||||||
|
|
||||||
;; Small detail: cmp and sub use the same internal architecture,
|
;; Small detail: cmp and sub use the same internal architecture,
|
||||||
|
@ -30,16 +31,16 @@ nextchar:
|
||||||
;; sub does. cmp sets flags; does sub? This is why 'jz' works,
|
;; sub does. cmp sets flags; does sub? This is why 'jz' works,
|
||||||
;; because if they're equal the result of subtraction is zero.
|
;; because if they're equal the result of subtraction is zero.
|
||||||
jz counted ; Jump if the zero flag set
|
jz counted ; Jump if the zero flag set
|
||||||
inc rax
|
inc rax ; Increment rax by 1
|
||||||
jmp nextchar
|
jmp nextchar ; Jump to the beginning of the loop
|
||||||
|
|
||||||
counted:
|
counted:
|
||||||
sub rax, rsi ; Substract source from endpointer, leaving counter
|
sub rax, rsi ; Substract source from endpointer, leaving counter
|
||||||
mov rdx, rax ; Length of the message
|
mov rdx, rax ; Length of the message
|
||||||
mov rsi, msg ; Address of the message
|
mov rsi, msg ; Address of the message
|
||||||
mov rdi, STDOUT ; using STDOUT (see definition above)
|
mov rdi, STDOUT ; using STDOUT (see definition above)
|
||||||
mov rax, SYS_write ; Using WRITE in 32-bit mode?
|
mov rax, SYS_write ; Using WRITE in 64-bin mode.
|
||||||
syscall
|
syscall ; Call the kernel
|
||||||
|
|
||||||
mov rdi, 0
|
mov rdi, 0
|
||||||
mov rax, SYS_exit
|
mov rax, SYS_exit
|
||||||
|
|
Loading…
Reference in New Issue