From d98f46ce647846b0aa30b2e16a30fd4e152a1bf5 Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Thu, 10 Jul 2025 22:55:07 +0200 Subject: Add new code Signed-off-by: Carlos Maiolino --- PGU/CHAP6_7/count-chars.s | 47 +++++++++++++++ PGU/CHAP6_7/error.s | 46 +++++++++++++++ PGU/CHAP6_7/linux.s | 16 +++++ PGU/CHAP6_7/modify-records.s | 86 +++++++++++++++++++++++++++ PGU/CHAP6_7/read-records.s | 74 ++++++++++++++++++++++++ PGU/CHAP6_7/read_write.s | 62 ++++++++++++++++++++ PGU/CHAP6_7/record-def.s | 7 +++ PGU/CHAP6_7/write-newline.s | 29 ++++++++++ PGU/CHAP6_7/write_records.s | 135 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 502 insertions(+) create mode 100644 PGU/CHAP6_7/count-chars.s create mode 100644 PGU/CHAP6_7/error.s create mode 100644 PGU/CHAP6_7/linux.s create mode 100644 PGU/CHAP6_7/modify-records.s create mode 100644 PGU/CHAP6_7/read-records.s create mode 100644 PGU/CHAP6_7/read_write.s create mode 100644 PGU/CHAP6_7/record-def.s create mode 100644 PGU/CHAP6_7/write-newline.s create mode 100644 PGU/CHAP6_7/write_records.s (limited to 'PGU/CHAP6_7') 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 -- cgit v1.2.3