Added Brainf*ck implementation in ia32 assembly language.

This commit is contained in:
2016-02-14 21:54:58 -04:30
parent 03d426931c
commit 2b26edc47c

617
bf.s Normal file
View File

@@ -0,0 +1,617 @@
.section .data
ptr:
.long 0
loop_ptr:
.long 0
ret_code:
.long 0
error_str:
.asciz "%s: fatal error: no input files\n"
bad_file:
.asciz "File %s could not be opened\n"
mode:
.asciz "r"
bad_loop:
.asciz "%s: fatal error: more than 1024 nested loops\n"
skip:
.long 0 /* Boolean flag to indicate that a badly formatted bf file must be skipped */
debug_str:
.asciz "Tape[%05d]: % 4d\n"
.section .rodata
.align 4
/* Jump table for the exec_bf_inst function */
.L10:
.long .L0 /* # = 35 */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long .L1 /* + = 43 */
.long .L2 /* , = 44 */
.long .L3 /* - = 45 */
.long .L4 /* . = 46 */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long .L5 /* < = 60 */
.long epilogue2 /* Default */
.long .L6 /* > = 62 */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long epilogue2 /* Default */
.long .L7 /* [ = 91 */
.long epilogue2 /* Default */
.long .L8 /* ] = 93 */
.long epilogue2 /* Default */
.section .bss
.comm bf_file, 4 /* Address of the current bf file's FILE structure */
.comm bf_file_name, 4 /* Address of the current bf file name */
.comm tape, 30000 /* 30000 unsigned byte cells for the tape */
.comm jmp_stack, 4100 /* 1024 long words to store bf loop adresses + 1 safeguard byte */
.section .text
.globl _start
_start:
movl (%esp), %ecx /* Get argc */
decl %ecx /* Substract 1 from argc */
movl 4(%esp), %ebx /* Get program name */
movl 8(%esp), %edx /* Get argv[1] */
/* if (argc - 1) == 0 then error */
cmpl $0, %ecx
jne eif1
/* Print error message, set return code and exit */
pushl %ebx
pushl $error_str
call printf
addl $8, %esp
movl $1, ret_code
jmp end
eif1:
/* Loop through all arguments */
movl $0, %edi
loop_start:
/* Process current input file */
pushl %eax
pushl %ecx
pushl %edx
pushl %edx
call interpret
addl $4, %esp
popl %edx
popl %ecx
popl %eax
/* Get current argument length */
pushl %ecx
pushl %edx
pushl %edx
call strlen
add $4, %esp
popl %edx
popl %ecx
/* Skip the characters from current arg + null char at the end */
addl $1, %eax
addl %eax, %edx
/* Increase arg counter and test loop end condition */
incl %edi
cmpl %ecx, %edi
jne loop_start
end:
movl $1, %eax
movl ret_code, %ebx
int $0x80
/* This function processes a Brainfuck file one instruction at a time.
Parameters: The name of the file as a C string. */
interpret:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
movl 8(%ebp), %ebx /* Get the file name*/
/* Open input file */
pushl %ebx /* Save the file name */
pushl $mode
pushl %ebx /* ebx holds the file name*/
call fopen /* eax will hold the file pointer from now on */
addl $8, %esp
popl %ebx /* Restore the file name */
movl %eax, bf_file /* Store the file ptr in memory */
movl %ebx, bf_file_name /* Store the file ptr in memory */
/* Check if file is open */
cmpl $0, %eax
jne echo
/* If file is NULL then print an error msg and exit */
pushl %ebx /* Save the file name */
pushl %ebx
pushl $bad_file
call printf
addl $8, %esp
popl %ebx /* Restore the file name */
jmp epilogue1
/* Process the file */
echo:
/* Check if this file must be skipped */
/* Only happens if max loops is reached */
movl skip, %esi
cmpl $0, %esi
movl $0, skip
jne close
/* Read 1 byte from the file */
pushl %ebx /* Save the file name */
pushl %eax /* Save the file pointer */
pushl %eax
call fgetc
addl $4, %esp
movl %eax, %ecx /* ecx will hold the char read */
popl %eax /* Restore the file pointer */
popl %ebx /* Restore the file name */
/* Test for EOF */
pushl %ebx /* Save the file name */
pushl %eax /* Save the file pointer */
pushl %ecx /* Save the char */
pushl %eax
call feof
addl $4, %esp
movl %eax, %edx
popl %ecx /* Restore the char */
popl %eax /* Restore the file pointer */
popl %ebx /* Restore the file name */
cmpl $0, %edx
jne close
/* Execute the corresponding instruction */
pushl %ebx /* Save the file name */
pushl %eax /* Save the file pointer */
pushl %ecx
call exec_bf_inst
addl $4, %esp
popl %eax /* Restore the file pointer */
popl %ebx /* Restore the file name */
jmp echo
/* Close input file */
close:
pushl %eax
call fclose
addl $4, %esp
epilogue1:
popl %esi
popl %edi
popl %ebx
leave
ret
/* Function that executes a BF instruction by calling the appropiate subroutine
with a switch construct.
Parameters: The instruction as a char.*/
exec_bf_inst:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
movl 8(%ebp), %ebx /* Get the instruction */
/* Determine wich case the instruction falls into */
subl $35, %ebx
cmpl $58, %ebx
ja epilogue2
jmp *.L10(,%ebx, 4)
.L0: /* # = 35 */
call debug_tape
jmp epilogue2
.L1: /* + = 43 */
call add_cell
jmp epilogue2
.L2: /* , = 44 */
call input_cell
jmp epilogue2
.L3: /* - = 45 */
call sub_cell
jmp epilogue2
.L4: /* . = 46 */
call output_cell
jmp epilogue2
.L5: /* < = 60 */
call move_left
jmp epilogue2
.L6: /* > = 62 */
call move_right
jmp epilogue2
.L7: /* [ = 91 */
call bf_loop_start
jmp epilogue2
.L8: /* ] = 93 */
call bf_loop_end
/* The function's epilogue doubles as defaut */
epilogue2:
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: > */
move_right:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Increase the tape pointer by 1 looping around on cell 29999 */
movl ptr, %eax
incl %eax
cmpl $30000, %eax
jl end_mr
movl $0, %eax
end_mr:
movl %eax, ptr /* Update the tape pointer */
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: < */
move_left:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Decrease the tape pointer by 1 looping around on cell 0 */
movl ptr, %eax
decl %eax
cmpl $0, %eax
jge end_ml
movl $29999, %eax
end_ml:
movl %eax, ptr /* Update the tape pointer */
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: + */
add_cell:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Just add 1 on the tape */
movl ptr, %eax
incb tape(, %eax, 1)
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: - */
sub_cell:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Just substract 1 on the tape */
movl ptr, %eax
decb tape(, %eax, 1)
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: . */
output_cell:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Print wathever is on the tape as ascii */
movl ptr, %eax
xorl %ebx, %ebx
movb tape(, %eax, 1), %bl
pushl %ebx
call putchar
addl $4, %esp
/* Since we are printing just one char we must flush stoud to make sure it actually prints
instead of just being buffered by the OS */
pushl stdout
call fflush
addl $4, %esp
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: , */
input_cell:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Read one char on stdin */
xorl %eax, %eax
call getchar
movl ptr, %ebx
movb %al, tape(, %ebx, 1)
input_end:
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: # */
debug_tape:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* First move the tape pointer 5 positions to the left*/
movl $0, %edi
.W1:
call move_left
incl %edi
cmpl $5, %edi
jl .W1
/* Then loop through the next 11 tape positions and print them */
movl $0, %edi
print_loop:
movl ptr, %eax
xorl %ebx, %ebx
movb tape(, %eax, 1), %bl
pushl %ebx
pushl %eax
pushl $debug_str
call printf
addl $12, %esp
call move_right
incl %edi
cmpl $11, %edi
jl print_loop
/* Flush stdout for the same reasons as in . */
pushl stdout
call fflush
addl $4, %esp
/* Finally restore the tape pointer to it's original position */
movl $0, %edi
.W2:
call move_left
incl %edi
cmpl $6, %edi
jl .W2
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: [ */
bf_loop_start:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
movl loop_ptr, %ebx
cmpl $1024, %ebx
jl check_loop
pushl bf_file_name
pushl $bad_loop
call printf
addl $8, %esp
movl $1, skip
jmp epilogue_ls
check_loop:
call cell_is_zero
cmpl $0, %eax
je push_loop
call find_matching_le
jmp epilogue_ls
push_loop:
pushl %ebx
pushl bf_file
call ftell
addl $4, %esp
popl %ebx
movl %eax, jmp_stack(, %ebx, 4)
incl %ebx
movl %ebx, loop_ptr
epilogue_ls:
popl %esi
popl %edi
popl %ebx
leave
ret
/* Instruction: ] */
bf_loop_end:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Check if the current cell is 0 and loop if it isn't */
call cell_is_zero
cmpl $0, %eax
je loop
/* If the current cell is 0 then pop the last loop address */
movl loop_ptr, %ebx
decl %ebx
movl $0, jmp_stack(, %ebx, 4)
movl %ebx, loop_ptr
jmp epilogue_le
loop:
movl bf_file, %eax
movl loop_ptr, %ebx
decl %ebx
movl jmp_stack(, %ebx, 4), %ecx
pushl $0 /* SEEK_SET */
pushl %ecx
pushl %eax
call fseek
addl $12, %esp
epilogue_le:
popl %esi
popl %edi
popl %ebx
leave
ret
/* This function returns 1 if the current tape cell is 0, returns 0 otherwise */
cell_is_zero:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* Check if the current cell is 0 */
xorl %ebx, %ebx
movl ptr, %eax
movb tape(, %eax, 1), %bl
cmpb $0, %bl
jne .FALSE1
.TRUE1:
movl $1, %eax
jmp epilogue_cz
.FALSE1:
movl $0, %eax
epilogue_cz:
popl %esi
popl %edi
popl %ebx
leave
ret
/* Function that searches through the BF file in search of the matching ] for a given [.
NOTE: Doesn't handle mismatched loops */
find_matching_le:
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
movl $1, %ebx
find_loop:
pushl %ebx
pushl bf_file
call fgetc
addl $4, %esp
popl %ebx
/* TODO: Check for EOF and skip file if a premature EOF is found */
cmpl $93, %eax
jne new_loop
decl %ebx
cmpl $0, %ebx
jne find_loop
jmp epilogue_fm
new_loop:
cmpl $91, %eax
jne find_loop
addl $1, %ebx
jmp find_loop
epilogue_fm:
popl %esi
popl %edi
popl %ebx
leave
ret