logoISU  

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