summaryrefslogtreecommitdiff
path: root/PGU/CHAP6_7
diff options
context:
space:
mode:
Diffstat (limited to 'PGU/CHAP6_7')
-rw-r--r--PGU/CHAP6_7/count-chars.s47
-rw-r--r--PGU/CHAP6_7/error.s46
-rw-r--r--PGU/CHAP6_7/linux.s16
-rw-r--r--PGU/CHAP6_7/modify-records.s86
-rw-r--r--PGU/CHAP6_7/read-records.s74
-rw-r--r--PGU/CHAP6_7/read_write.s62
-rw-r--r--PGU/CHAP6_7/record-def.s7
-rw-r--r--PGU/CHAP6_7/write-newline.s29
-rw-r--r--PGU/CHAP6_7/write_records.s135
9 files changed, 502 insertions, 0 deletions
diff --git a/PGU/CHAP6_7/count-chars.s b/PGU/CHAP6_7/count-chars.s
new file mode 100644
index 0000000..7e12791
--- /dev/null
+++ b/PGU/CHAP6_7/count-chars.s
@@ -0,0 +1,47 @@
+# Count the characters in a string (until a null byte is reached)
+# It's supposed to behave similarly with strlen()
+#
+# Returns the count in %rax
+#
+# - %rcx: Char count
+# - %rdx: current Char address
+# - %al: current char
+#
+
+.type count_chars, @function
+.globl count_chars
+
+# We receive the string addr in the stack.
+# Remember %rsp + 8 contains the return value
+.equ ST_STRING_ADDRESS, 16
+
+count_chars:
+ pushq %rbp
+ movq %rsp, %rbp
+
+ #initialize counter
+ movq $0, %rcx
+
+ # Start address of string:
+ movq ST_STRING_ADDRESS(%rbp), %rdx
+
+count_loop_begin:
+
+ # Grab char
+ movb (%rdx), %al
+
+ cmpb $0, %al
+ je count_loop_end
+
+ # We are not done yet...
+ incq %rcx
+ incq %rdx
+ jmp count_loop_begin
+
+count_loop_end:
+
+ movq %rcx, %rax
+
+ movq %rbp, %rsp
+ popq %rbp
+ ret
diff --git a/PGU/CHAP6_7/error.s b/PGU/CHAP6_7/error.s
new file mode 100644
index 0000000..6e933ed
--- /dev/null
+++ b/PGU/CHAP6_7/error.s
@@ -0,0 +1,46 @@
+# Error function
+#
+# Print error messages and exit program.
+
+.include "linux.s"
+.equ ST_ERROR_CODE, 16
+.equ ST_ERROR_MSG, 24
+
+.globl error_exit
+.type error_exit, @function
+
+error_exit:
+ pushq %rbp
+ movq %rsp, %rbp
+
+ # Write error Code
+ movq ST_ERROR_CODE(%rbp), %rcx
+ pushq %rcx
+ call count_chars
+ popq %rcx
+
+ movq %rax, %rdx
+ movq %rcx, %rsi
+ movq $STDERR, %rdi
+ movq $SYS_WRITE, %rax
+ syscall
+
+ # Write error message
+ movq ST_ERROR_MSG(%rbp), %rcx
+ pushq %rcx
+ call count_chars
+ popq %rcx
+
+ movq %rax, %rdx # How many bytes to write
+ movq %rcx, %rsi # Buffer to write from
+ movq $STDERR, %rdi # FD to write to
+ movq $SYS_WRITE, %rax # self explanatory
+ syscall
+
+ pushq $STDERR
+ call write_newline
+
+ # Exit program
+ movq $SYS_EXIT, %rax
+ movq $1, %rdi
+ syscall
diff --git a/PGU/CHAP6_7/linux.s b/PGU/CHAP6_7/linux.s
new file mode 100644
index 0000000..9ab8243
--- /dev/null
+++ b/PGU/CHAP6_7/linux.s
@@ -0,0 +1,16 @@
+# Syscall numbers (x86_64)
+
+.equ SYS_EXIT, 60
+.equ SYS_READ, 0
+.equ SYS_WRITE, 1
+.equ SYS_OPEN, 2
+.equ SYS_CLOSE, 3
+.equ SYS_BRK, 12
+
+# Default File Descriptors
+.equ STDIN, 0
+.equ STDOUT, 1
+.equ STDERR, 2
+
+# Common Status Codes
+.equ END_OF_FILE, 0
diff --git a/PGU/CHAP6_7/modify-records.s b/PGU/CHAP6_7/modify-records.s
new file mode 100644
index 0000000..4cf2d92
--- /dev/null
+++ b/PGU/CHAP6_7/modify-records.s
@@ -0,0 +1,86 @@
+# Modify records in a file and write them to the output file
+.include "linux.s"
+.include "record-def.s"
+
+.section .data
+ input_filename:
+ .ascii "test.dat\0"
+ output_filename:
+ .ascii "modified.dat\0"
+
+.section .bss
+ .lcomm record_buffer, RECORD_SIZE
+
+
+.section .text
+.globl _start
+
+_start:
+ # Stack local vars
+ .equ ST_INPUT_DESCRIPTOR, -8
+ .equ ST_OUTPUT_DESCRIPTOR, -16
+
+ # Copy stack pointer and make room for local variables
+ movq %rsp, %rbp
+ subq $16, %rsp
+
+ # Open file for reading
+ movq $SYS_OPEN, %rax
+ movq $input_filename, %rdi
+ movq $0, %rsi
+ movq $0666, %rdx
+ syscall
+
+ cmpq $0, %rax
+ jl continue_processing
+
+ # We've got an error...
+continue_processing:
+ movq %rax, ST_INPUT_DESCRIPTOR(%rbp)
+
+ .section .data
+ no_open_file_code:
+ .ascii "0001: \0"
+ no_open_file_msg:
+ .ascii "Can't open input file\0"
+ .section .text
+ pushq $no_open_file_msg
+ pushq $no_open_file_code
+ call error_exit
+
+ # Open file for writing
+ movq $SYS_OPEN, %rax
+ movq $output_filename, %rdi
+ movq $0101, %rsi
+ movq $0666, %rdx
+ syscall
+
+ movq %rax, ST_OUTPUT_DESCRIPTOR(%rbp)
+
+loop_begin:
+ pushq ST_INPUT_DESCRIPTOR(%rbp)
+ pushq $record_buffer
+ call read_record
+ addq $16, %rsp
+
+ # Return number of bytes read. If it's not the same as
+ # $RECORD_SIZE, then we are either at EOF or we hit an error.
+ cmpq $RECORD_SIZE, %rax
+ jne loop_end
+
+ # Increment age...
+ incq record_buffer + RECORD_AGE
+
+ # Write record to the output file
+ pushq ST_OUTPUT_DESCRIPTOR(%rbp)
+ pushq $record_buffer
+ call write_record
+ addq $16, %rsp
+
+ jmp loop_begin
+
+loop_end:
+
+ movq $SYS_EXIT, %rax
+ movq $0, %rbx
+ syscall
diff --git a/PGU/CHAP6_7/read-records.s b/PGU/CHAP6_7/read-records.s
new file mode 100644
index 0000000..89b1e91
--- /dev/null
+++ b/PGU/CHAP6_7/read-records.s
@@ -0,0 +1,74 @@
+# Read records previously written in file.dat, by write-records software
+
+.include "linux.s"
+.include "record-def.s"
+
+.section .data
+ filename:
+ .ascii "test.dat\0"
+
+.section .bss
+ .lcomm record_buffer, RECORD_SIZE
+
+.section .text
+
+.globl _start
+
+_start:
+ # Stack locations for INPUT and OUTPUT FDs
+ .equ ST_INPUT_DESCRIPTOR, -8
+ .equ ST_OUTPUT_DESCRIPTOR, -16
+
+ movq %rsp, %rbp
+ subq $16, %rsp # Save space in the stack for FDs
+
+ # Open data file
+ movq $SYS_OPEN, %rax
+ movq $filename, %rdi
+ movq $0, %rsi # Open for read only
+ movq $0666, %rdx
+ syscall
+
+ # Save FD
+ movq %rax, ST_INPUT_DESCRIPTOR(%rbp)
+
+ # Yes, STDOUT is always 1, but if I want to change the output location
+ # later, I don't need to change everywhere...
+ movq $STDOUT, ST_OUTPUT_DESCRIPTOR(%rbp)
+
+record_read_loop:
+ pushq ST_INPUT_DESCRIPTOR(%rbp)
+ pushq $record_buffer
+ call read_record
+ addq $16, %rsp # Cleanup stack
+
+ # All records are RECORD_SIZE size, if we didn't get this amount of
+ # bytes from read function, we either are at EOF or we hit an error.
+ cmpq $RECORD_SIZE, %rax
+ jne finished_reading
+
+ # We are ok, so print the first name in the record
+ pushq $RECORD_FIRSTNAME + record_buffer # Location of firstname
+ # record in the buffer
+ # we just read
+ call count_chars
+ addq $8, %rsp # Cleanup stack
+
+ # Write name to OUTPUT
+ movq %rax, %rdx # Returned record size, used as argument to
+ # write()
+ movq $RECORD_FIRSTNAME + record_buffer, %rsi
+ movq ST_OUTPUT_DESCRIPTOR(%rbp), %rdi
+ movq $SYS_WRITE, %rax
+ syscall
+
+ pushq ST_OUTPUT_DESCRIPTOR(%rbp)
+ call write_newline
+ addq $8, %rsp
+
+ jmp record_read_loop
+
+finished_reading:
+ movq $SYS_EXIT, %rax
+ movq $0, %rdi
+ syscall
diff --git a/PGU/CHAP6_7/read_write.s b/PGU/CHAP6_7/read_write.s
new file mode 100644
index 0000000..d574013
--- /dev/null
+++ b/PGU/CHAP6_7/read_write.s
@@ -0,0 +1,62 @@
+.include "record-def.s"
+.include "linux.s"
+
+# Read function
+# Reads a record from the file descriptor
+# and writes it into the buffer passed
+
+# STACK VARS - Used for both read and write functions. They don't share the
+# location, but the arguments are passed in the same position
+# for both, so, no need to create different location vars.
+
+.equ ST_BUFFER, 16 # Ret address is at %rsp+8
+.equ ST_FILEDES, 24
+
+.section .text
+
+.globl read_record
+.type read_record, @function
+
+read_record:
+ pushq %rbp
+ movq %rsp, %rbp
+
+ # READ A RECORD
+ pushq %rdi
+ movq ST_FILEDES(%rbp), %rdi
+ movq ST_BUFFER(%rbp), %rsi
+ movq $RECORD_SIZE, %rdx
+ movq $SYS_READ, %rax
+ syscall
+
+ # NOTE: %rax has the return value, which we will give back
+ # to our caller
+
+ popq %rdi
+
+ movq %rbp, %rsp
+ popq %rbp
+ ret
+
+.globl write_record
+.type write_record, @function
+
+write_record:
+ pushq %rbp
+ movq %rsp, %rbp
+
+ # WRITE A RECORD
+ pushq %rdi
+ movq ST_FILEDES(%rbp), %rdi
+ movq ST_BUFFER(%rbp), %rsi
+ movq $RECORD_SIZE, %rdx
+ movq $SYS_WRITE, %rax
+ syscall
+
+ # NOTE: %rax has the return value, which we will give back
+ # to our caller
+ popq %rdi
+
+ movq %rbp, %rsp
+ popq %rbp
+ ret
diff --git a/PGU/CHAP6_7/record-def.s b/PGU/CHAP6_7/record-def.s
new file mode 100644
index 0000000..9e5274d
--- /dev/null
+++ b/PGU/CHAP6_7/record-def.s
@@ -0,0 +1,7 @@
+# Define offsets within a record
+
+.equ RECORD_FIRSTNAME, 0
+.equ RECORD_LASTNAME, 40
+.equ RECORD_ADDRESS, 80
+.equ RECORD_AGE, 320 # Use 8 bytes for age, because it's
+.equ RECORD_SIZE, 328 # simpler to deal with whole words
diff --git a/PGU/CHAP6_7/write-newline.s b/PGU/CHAP6_7/write-newline.s
new file mode 100644
index 0000000..e916379
--- /dev/null
+++ b/PGU/CHAP6_7/write-newline.s
@@ -0,0 +1,29 @@
+# Just write a newline (\n) to STDOUT
+
+.include "linux.s"
+.type write_newline, @function
+.globl write_newline
+
+.section .data
+
+newline:
+ .ascii "\n"
+
+.section .text
+ .equ ST_FILEDES, 16
+
+write_newline:
+ pushq %rbp
+ movq %rsp, %rbp
+
+ movq $SYS_WRITE, %rax
+ movq ST_FILEDES(%rbp), %rdi
+ movq $newline, %rsi
+ movq $1, %rdx
+ syscall
+
+ movq %rbp, %rsp
+ popq %rbp
+ ret
+
+
diff --git a/PGU/CHAP6_7/write_records.s b/PGU/CHAP6_7/write_records.s
new file mode 100644
index 0000000..e52d7e0
--- /dev/null
+++ b/PGU/CHAP6_7/write_records.s
@@ -0,0 +1,135 @@
+.include "linux.s"
+.include "record-def.s"
+
+.section .data
+
+# Constant data of the records we want to write
+# Each data item is padded to the proper length
+# With null (i.e 0) bytes
+
+# .rept is used to pad each item. .rept tells the assembler to repeat the
+# section between .rept and .endr the number of times specified.
+# This is used in this program to add extra null chars at the end of each field
+# to fill the record up to the record size.
+
+# START OF RECORDS
+record1:
+ # First name
+ .ascii "Fredrick\0"
+ .rept 31 # Padding to 40 bytes
+ .byte 0
+ .endr
+
+ # Last name
+ .ascii "Bardlet\0"
+ .rept 31 # Padding to 40 bytes
+ .byte 0
+ .endr
+
+ # Address
+ .ascii "4242 S Prairie\nTulsa, OK 55555\0"
+ .rept 209 # Padding to 240 bytes
+ .byte 0
+ .endr
+
+ # Age
+ .long 45 # Field is 8 bytes long (1 word)
+
+record2:
+ # First name
+ .ascii "Marilyn\0"
+ .rept 32 # Padding to 40 bytes
+ .byte 0
+ .endr
+
+ # Last name
+ .ascii "Taylor\0"
+ .rept 33 # Padding to 40 bytes
+ .byte 0
+ .endr
+
+ # Address
+ .ascii "2224 S Johannan St\nChicago, IL 12345\0"
+ .rept 203 # Padding to 240 bytes
+ .byte 0
+ .endr
+
+ # Age
+ .long 29 # Field is 8 bytes long (1 word)
+
+record3:
+ # First name
+ .ascii "Derrick\0"
+ .rept 32 # Padding to 40 bytes
+ .byte 0
+ .endr
+
+ # Last name
+ .ascii "McIntire\0"
+ .rept 31 # Padding to 40 bytes
+ .byte 0
+ .endr
+
+ # Address
+ .ascii "500 W Oakland\nSan Diego, CA 54321\0"
+ .rept 206 # Padding to 240 bytes
+ .byte 0
+ .endr
+
+ # Age
+ .long 36 # Field is 8 bytes long (1 word)
+
+# END OF RECORDS
+
+
+.section .text
+# File we'll be writing to
+file_name:
+ .ascii "test.dat\0"
+
+.equ ST_FILE_DESCRIPTOR, -8
+
+.globl _start
+.global write_record
+
+_start:
+ movq %rsp, %rbp
+ subq $8, %rsp # Allocate space in stack for FD
+
+ # Open file
+ movq $SYS_OPEN, %rax
+ movq $file_name, %rdi
+ movq $0101, %rsi # Open for writing, create if it doesn't exist
+ movq $0666, %rdx
+ syscall
+
+ # Store FD:
+ movq %rax, ST_FILE_DESCRIPTOR(%rbp)
+
+ # Write first record
+ pushq ST_FILE_DESCRIPTOR(%rbp)
+ pushq $record1
+ call write_record
+ addq $16, %rsp #cleanup stack
+
+ # Write second record
+ pushq ST_FILE_DESCRIPTOR(%rbp)
+ pushq $record2
+ call write_record
+ addq $16, %rsp
+
+ # Write third record
+ pushq ST_FILE_DESCRIPTOR(%rbp)
+ pushq $record3
+ call write_record
+ addq $16, %rsp
+
+ # Close FD
+ movq $SYS_CLOSE, %rax
+ movq ST_FILE_DESCRIPTOR(%rbp), %rdi
+ syscall
+
+ # We are done, just exit
+ movq $SYS_EXIT, %rax
+ movq $0, %rdi
+ syscall