mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-21 07:46:47 +08:00
194 lines
5.9 KiB
ArmAsm
194 lines
5.9 KiB
ArmAsm
/* Perry Kivolowitz
|
|
Example of file operations.
|
|
*/
|
|
.text
|
|
.global main
|
|
.align 2
|
|
|
|
/* This program will
|
|
* open() a file in the current directory,
|
|
* write() some text to it,
|
|
* seek back to the beginning of the file,
|
|
* read() each line, printing it
|
|
* close() the file
|
|
*/
|
|
|
|
retval .req w27
|
|
fd .req w28
|
|
|
|
main: stp x29, x30, [sp, -16]!
|
|
stp x27, x28, [sp, -16]!
|
|
bl open_file
|
|
|
|
// w0 will contain either the file descriptor of the new
|
|
// file or -1 for a failure. Note that the value in w0
|
|
// has also been copied to "fd" - a register alias.
|
|
cmp w0, wzr
|
|
bge 1f
|
|
|
|
// If we get here, the open has failed. Use perror() to
|
|
// print a meaningful error and branch to exit. The return
|
|
// code of the program will be set to non-zero inside fail.
|
|
ldr x0, =fname
|
|
bl fail
|
|
b 99f
|
|
|
|
1: // When we get here, the file is open. Write some data to it.
|
|
// If write_file returns non-zero, it signifies an error. If
|
|
// so, branch to the file closing code since the file is open
|
|
// after printing an error message.
|
|
bl write_data
|
|
cbz w0, 10f
|
|
|
|
// If we get here, there was an error in write_data. Print
|
|
// a reasonable error message then branch to the clean usleep
|
|
// code.
|
|
ldr x0, =wf // load legend
|
|
bl fail // print error
|
|
b 50f // branch to clean up.
|
|
|
|
// Seek back to position zero preparing to read the file back.
|
|
// The return value in x0 (off_t) is the return value of
|
|
// lseek().
|
|
10: bl seek_zero
|
|
cbz x0, 20f
|
|
|
|
// If we get here, the seek failed. Cause a reasonable
|
|
// message to be printed then branch to the clean up code.
|
|
ldr x0, =sf
|
|
bl fail
|
|
b 50f
|
|
|
|
20: // When we get here, we have to read from the file and print
|
|
// the results. To ignore the complexity of memory allocation
|
|
// and buffer overrun potential, we'll read one character at a
|
|
// time looking the end-of-file.
|
|
|
|
// ssize_t read(int fildes, void *buf, size_t nbyte);
|
|
mov w0, fd
|
|
ldr x1, =buffer
|
|
mov x2, 1
|
|
bl read
|
|
// Check the return value - should be 1.
|
|
cbz x0,50f // zero means EOF - that's OK.
|
|
// If x0 is negative, that IS a problem.
|
|
cmp x0, xzr
|
|
bge 25f
|
|
// The return value is negative - this is an error.
|
|
ldr x0, =rf
|
|
bl fail
|
|
b 99f
|
|
|
|
25: // Write the character sitting in buffer to the console.
|
|
mov w0, 1
|
|
ldr x1, =buffer
|
|
mov x2, 1
|
|
bl write
|
|
// We will ignore the return value for the sake of brevity.
|
|
// There are plenty of examples of handling a potential error
|
|
// elsewhere in this code.
|
|
// --
|
|
b 20b
|
|
|
|
// When we get here, we are done. Close the file.
|
|
50: mov w0, fd
|
|
bl close
|
|
mov retval, wzr
|
|
|
|
99: ldp x27, x28, [sp], 16
|
|
ldp x29, x30, [sp], 16
|
|
mov w0, retval
|
|
ret
|
|
|
|
/* open_file()
|
|
This function attempts to open a file for both reading and
|
|
writing. Return values will be checked to ensure the file is
|
|
opened. If successful, the fd is returned (and is squirreled
|
|
away in register "fd"). If unsuccessful, the -1 returned by
|
|
open() is passed back to the caller.
|
|
|
|
Explanation of the magic numbers:
|
|
|
|
int open(const char *pathname, int flags, mode_t mode);
|
|
|
|
octal 102 for flags is O_RDRW | O_CREAT
|
|
octal 600 for mode is rw------- i.e. read and write for
|
|
the owner but no permissions for anyone else.
|
|
|
|
There is a version of open() that takes two parameters. However,
|
|
if O_CREAT is specified, the three parameter version is required.
|
|
*/
|
|
|
|
.equ O_FLAGS, 0102
|
|
.equ O_MODE, 0600
|
|
|
|
open_file:
|
|
stp x29, x30, [sp, -16]!
|
|
ldr x0, =fname
|
|
mov w1, O_FLAGS
|
|
mov w2, O_MODE
|
|
bl open
|
|
mov fd, w0
|
|
ldp x29, x30, [sp], 16
|
|
ret
|
|
|
|
|
|
/* This function uses perror() to print a meaningful error
|
|
message in the event of a failure. The string value
|
|
passed to perror() arrives to us as a pointer in x0.
|
|
*/
|
|
|
|
fail:
|
|
stp x29, x30, [sp, -16]!
|
|
bl perror
|
|
mov retval, 1
|
|
ldp x29, x30, [sp], 16
|
|
ret
|
|
|
|
/* ssize_t write(int fd, const void *buf, size_t count);
|
|
|
|
This function will write a string to the file descriptor contained
|
|
in "fd" (a register alias).
|
|
*/
|
|
|
|
write_data:
|
|
stp x29, x30, [sp, -16]!
|
|
str x20, [sp, -16]!
|
|
mov w0, fd // file descriptor
|
|
ldr x1, =txt // address to print from
|
|
ldr x2, =txt_s // load pointer to size
|
|
ldr x2, [x2] // dereference the pointer
|
|
mov w20, w2 // need this value for error check.
|
|
bl write
|
|
cmp x0, x20 // Did we write the expected amount?
|
|
bne 90f
|
|
// successful write - return 0
|
|
mov x0, xzr
|
|
b 99f
|
|
90: // failure - ensure we return non-zero!
|
|
mov x0, 1
|
|
99: ldr x20, [sp], 16
|
|
ldp x29, x30, [sp], 16
|
|
ret
|
|
|
|
/* off_t lseek(int fd, off_t offset, int whence);
|
|
*/
|
|
seek_zero:
|
|
stp x29, x30, [sp, -16]!
|
|
mov w0, fd // file descriptor
|
|
mov x1, xzr // beginning of file
|
|
mov w2, wzr // SEEK_SET - absolute offset
|
|
bl lseek
|
|
ldp x29, x30, [sp], 16
|
|
ret
|
|
|
|
.data
|
|
prog: .asciz "file_ops"
|
|
wf: .asciz "write failed"
|
|
rf: .asciz "read failed"
|
|
sf: .asciz "lseek failed"
|
|
fname: .asciz "test.txt"
|
|
txt: .asciz "some data\n"
|
|
txt_s: .word txt_s - txt - 1 // strlen(txt)
|
|
buffer: .word 0
|
|
.end
|