chastelib test suite for 32-bit Linux GNU Assembly Language

Ever since I figured out the technique for translating NASM source files into GNU Assembler source files, I have been quite excited at the possibilities. I translated the whole chastelib library into this syntax. This means that I could even learn to use these as inline assembly in C programs on Linux. I am not sure what the benefit is unless of course I enter some kind of obfuscated code contest!

main.s

# Using Linux System calls for 32-bit
# Tested with GNU Assembler on Debian 12 (bookworm)
# It uses Chastity's putstring function for output

.global _start

_start:


mov $main_string,%eax # move address of string into eax register
call   putstring      # call the putstring function Chastity wrote

movl $0x10,radix # set the radix to sixteen or hexadecimal
movl $1,int_width # set the minimum integer width

mov $input_string,%eax
call strint

mov %eax,%ebx # copy eax to ebx before we use it in a loop1

movl $0,%eax # zero eax

loop1:

movl $2,radix # set radix to binary
movl $8,int_width # width of 8 bits
call putint
call putspace

movl $0x10,radix # set radix to hexadecimal
movl $1,int_width # width of 2 hex digits
call putint
call putspace

movl $10,radix # set radix to decimal (what humans read)
movl $3,int_width # width of 3 decimal digits
call putint
call putspace

cmp    $0x20,%al
jb     not_char
cmp    $0x7e,%al
ja     not_char

call putspace
call putchar  # print the character if it is in the range 0x20 to 0x7E

not_char:

call putline
inc    %eax
cmp    %ebx,%eax
jne    loop1

mov    $0x1,%eax      # system call 1 is exit
mov    $0x0,%ebx      # we want to return code 0
int    $0x80          # end program with system call

main_string:
.string	"This program is the official test suite for the Linux Assembly version of chastelib.\n"

input_string:
.string	"100"

putstring:            # the start of the putstring function
push   %eax
push   %ebx
push   %ecx
push   %edx
mov    %eax,%ebx

putstring_strlen_start:
cmpb   $0x0,(%ebx)
je     putstring_strlen_end
inc    %ebx
jmp    putstring_strlen_start

putstring_strlen_end:
sub    %eax,%ebx # subtract eax from ebx for number of bytes to write
mov    %ebx,%edx # copy number of bytes from ebx to edx
mov    %eax,%ecx # address of string to output
mov    $0x1,%ebx # file handler 1 is stdout
mov    $0x4,%eax # system call 4 is write
int    $0x80

pop    %edx
pop    %ecx
pop    %ebx
pop    %eax
ret

.data        # must declare data section for mutable memory

int_string:  # storage for the bytes of an integer
.skip 32,'?' # unknown bytes represented with question marks
.byte 0      # terminating zero of this string

int_newline: # optional newline to print after a number
.byte 0xA,0

radix: .long 2
int_width: .long 8

intstr: # start of the intstr function
mov    $int_string+31,%ebx
mov    $0x1,%ecx

digits_start:
mov    $0x0,%edx
divl   radix            # divide by number at address radix
cmp    $0xa,%edx
jb     decimal_digit
jae    hexadecimal_digit

decimal_digit:
add    $0x30,%edx
jmp    save_digit

hexadecimal_digit:
sub    $0xa,%edx
add    $0x41,%edx

save_digit:
mov    %dl,(%ebx)
cmp    $0x0,%eax
je     intstr_end
dec    %ebx
inc    %ecx
jmp    digits_start

intstr_end:
cmp    int_width,%ecx # see if ecx is above or equal to the integer width we want
jae    end_zeros
dec    %ebx
movb   $0x30,(%ebx)
inc    %ecx
jmp    intstr_end

end_zeros:
mov    %ebx,%eax
ret

putint: # the putint function calls intstr and then putstring to display any number

push   %eax
push   %ebx
push   %ecx
push   %edx
call   intstr
call   putstring
pop    %edx
pop    %ecx
pop    %ebx
pop    %eax
ret

# the strint function is arguably the most complicated assembly function I have ever written
# it can convert any string into a number based on the current radix
# as soon as it finds a zero byte or a charact that is not a valid digit in that radix
# it will return the value in the eax register so it can be printed or used elsewhere

strint:
mov    %eax,%ebx # copy string address from eax to ebx because eax will be replaced soon!
mov    $0x0,%eax # eax set to zero before digits multiplied in


read_strint:
mov    $0x0,%ecx
mov    (%ebx),%cl
inc    %ebx
cmp    $0x0,%cl
je     strint_end
cmp    $0x30,%cl
jb     not_digit
cmp    $0x39,%cl
ja     not_digit

is_digit:
sub    $0x30,%cl
jmp    process_char

not_digit:
cmp    $0x41,%cl
jb     not_upper
cmp    $0x5a,%cl
ja     not_upper

is_upper:
sub    $0x41,%cl
add    $0xa,%cl
jmp    process_char

not_upper:
cmp    $0x61,%cl
jb     not_lower
cmp    $0x7a,%cl
ja     not_lower

is_lower:
sub    $0x61,%cl
add    $0xa,%cl
jmp    process_char

not_lower:
jmp    strint_end

process_char:
cmp    radix,%ecx
jae    strint_end
mov    $0x0,%edx
mull   radix
add    %ecx,%eax
jmp    read_strint

strint_end:
ret

space: .byte ' ',0
line: .byte 0x0A,0

putspace:
push   %eax
mov    $space,%eax
call   putstring
pop    %eax
ret

putline:
push   %eax
mov    $line,%eax
call   putstring
pop    %eax
ret

char: .byte 0,0 # where char data is temporarily stored by the putchar function

putchar:
push   %eax
mov    %al,char
mov    $char,%eax
call   putstring
pop    %eax
ret

# This Assembly source file has been formatted for the GNU assembler.
# The following makefile rule has commands to assemble, link, and run the program
#
#main-gas:
#	gcc -nostdlib -nostartfiles -nodefaultlibs -static main.s -o main -m32
#	strip main
#	./main

Comments

Leave a comment