Search code examples
linuxassemblystdoutstdinatt

Continuous reading & writing using stdin and stdout in AT&T assembly


I have a task to write a program in AT&T Assembly which can read and write part by part as long as you want sequence of bytes using Linux’s stdin and stdout.

It should be a Caesar cipher scrambler that changes only big Latin characters (A,B,C,D,…) one step forward (so like A to B, B to C, C to D, …, Z to A).

So like the program reads first part of the sequence, do operations to change letters leaving other characters untouched, writes the changed part to stdout, and reads next parts, and do everything again and again.

In the end it should work with files set to stdin and stdout by running the program by command

>./executable  >output.txt < input.txt

What I was able to write by now is the program that reads some amount of characters, do operations, prints output and ask for more. However if you put more characters in the input than the buf size it crashes.

Here’s the code:

SYSCALL32 = 0x80 

EXIT = 1 
ERR_CODE = 0

STDIN = 0 
READ = 3 

STDOUT = 1 
WRITE = 4  

BUF_SIZE = 80 

.data
textin: .space BUF_SIZE
textout: .space BUF_SIZE

.global _start
_start:
mov $READ, %eax
mov $STDIN, %ebx
mov $textin, %ecx
mov $BUF_SIZE, %edx

int $SYSCALL32 # reading 80 characters (buf_size) of std input

cmp $0, %eax 
je exit #if 0 characters read exit

xorl %esi, %esi #clering iterator for loop

loop:
mov textin(%esi), %al #copying 1 character from textin buf to work with

cmp $'\n', %al
je out 
cmp $'A', %al
jl skip
cmp $'Z', %al
jg skip
je takeCareOfZ

add $1, %al
jmp skip

takeCareOfZ:
mov $'A', %al

skip:
movb %al, textout(%esi)
incl %esi
jmp petla

out:
mov $WRITE, %eax
mov $STDOUT, %ebx
mov $textout, %ecx
mov $BUF_SIZE, %edx

int $SYSCALL32 #writing 80 characters to std output

jmp _start

exit:
mov $EXIT, %eax
mov $ERR_CODE, %ebx

int $SYSCALL32

Solution

  • Here is a version of the program that removes the complexity associated with using a buffer of fixed size.

            .text
            .global _start
    _start:
            # read(STDIN_FILENO, buf, 1)
            movl    $1, %edx    # size_t nbyte
            movl    $buf, %ecx  # void *buf
            movl    $0, %ebx    # int filedes
            movl    $3, %eax    # sys_read
            int     $0x80
    
            cmp     $1, %eax    
            jne     bye         # EOF or read() error
    
            cmp     $'A', (%ecx)
            jl      output      # *buf < 'A'
    
            cmp     $'Z', (%ecx)
            jg      output      # *buf > 'Z'
            je      z           # *buf == 'Z'
    
            incl    (%ecx)      # *buf >= 'A' && *buf < 'Z'
            jmp     output
    
    z:
            movl    $'A', (%ecx)
    
    output:
            # write(STDOUT_FILENO, buf, 1)
            movl    $1, %ebx    # int filedes
            movl    $4, %eax    # sys_write
            int     $0x80
            jmp     _start
    
    bye:
            # exit(0)
            movl    $0, %ebx    # int status
            movl    $1, %eax    # sys_exit
            int     $0x80
    
    .data
    buf:
            .byte 0
    

    Sample run

    $ gcc -nostdlib -m32 caesar.S -o caesar
    $ ./caesar 
    1234
    1234
    ABC1
    BCD1
    Z12BB
    A12CC
    

    Sample run using gdb, one instruction at a time.

    gdb caesar