|
CS456 - Systems Programming
Spring 2025
|
Displaying ./code/nasm/mycat.asm
; mycat (the cat command in unix-based systems)
; we are emulating the "cat" command in unix based systems
; similar to earlier this semester, but in assembly
; Compile with: nasm -f elf mycat.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 mycat.o -o mycat
; or (if Makefile exists) type make
; Run with: ./mycat
; You will be implementing the "cat" command in NASM assembly. You've seen
; it done in C earlier this semester.
;
; There is an external file 'functions.asm' that has some pre-written functions you can use
; to do this program.
;
;You can compile by running these two commands
; > nasm -f elf mycat.asm
; > ld -m elf_i386 mycat.o -o mycat
%include 'functions.asm' ; include the external file 'functions.asm'
;section for define statements
SECTION .data
usage db 'Usage: ./mycat file', 0Ah ; storing a usage statement
; note that 0Ah is 10 in decimal
; 10 is the ASCII number for newline character
; we define our variables in this section only
; resb means reserve this number of bytes
SECTION .bss
fd resb 8 ; reserving 8 bytes of memory to store a descriptor
sz resb 16 ; reserving 16 bytes to store integer for file size
buffer resb 10000 ; reserving 10000 bytes to store the contents of a file
SECTION .text
global _start
_start: ;
; dealing with command line arguments
; argc - stored in ecx register
; argv - all args are stored in a stack in eax register
args:
pop ecx ; getting the value of argc by popping it off the stack
pop eax ; getting argv[0]
dec ecx ; decreasing value inside ecx by 1
cmp ecx, 0h ; checking if argc is now 0
jz printUsage ; if above is true, we jump to section labeled "print usage"
; otherwise we continue below
; open the file for reading by invoking the "open" systemcall
; use the 32-bit opcode table to help you put this together
; Hint: what value/argument goes in what register?
; what is open's opcode?
openfile:
pop eax ; we still have argv[1] in eax stack, we need to pop it off first
; before we can use it
; move data out of eax into ebx first, as we will be using eax in a minute
mov ebx, eax ; what will go in ebx? (filename) (opcode for open system call)
mov ecx, 0 ; what will go in ecx? (flags, 0 means read only)
mov eax, 5 ; what will go in eax? (opcode for 'open' system call)
int 80h ; now call the kernel to run the function
; open returns a file descriptor and stores it in eax
; create a variable "fd", move data from eax into it
mov [fd], eax ; open will return a file descriptor that is stored in eax
; we need to store this somewhere so we create a variable fd
; we define what fd is above in SECTION .bss, and put square
; brackets around it.
; now invoke the read system call
readfile:
mov eax, 3 ; what goes in eax? (opcode for read)
mov ebx, [fd] ; what goes in ebx? (file descriptor)
mov ecx, buffer ; what goes in ecx? (the buffer we are reading into)
mov edx, 10000 ; what goes in edx? (max number of bytes read)
int 80h ;now call the kernel
; read returns number of bytes read,
mov [sz], eax ; store this value in sz, so we can use this later
; now we will output the text we read in to the terminal
; Hint: what is standard output's file descriptor?
; we will use the "write" system call
; write is set up similar to read
output:
mov eax, 4 ; what goes in eax?
mov ebx, 1 ; ebx?
mov ecx, buffer ; ecx?
mov edx, [sz] ; edx?
int 80h ; then call the kernel
jmp close
; lastly, we will do the close system call
; close takes only one argument, so we're only using 2 registers
close:
mov eax, 6 ; eax?
mov ebx, [fd] ; ebx?
int 80h ; now call the kernel
jmp done ; then jump to done
;if no argument was provided, we print a usage statement then go to done
printUsage:
mov eax, usage ; move usage statement to eax
call sprint ; call sprint (external function in functions.asm)
jmp done ; go to section labeled "done"
;exit the program
done:
call quit ; external function defined in functions.asm that cleanly exits the program
|