//---------------------------------------------------------------- // showdots.s // // This is an example of an 'interactive' program: it prints // a message asking the user to type in a number, then reads // the user's response from the standard input device (i.e., // the keyboard). Next it performs a computation to convert // the user's string of numerals (ascii codes) into a number // that can serve as a loop-counter. Finally, it repeatedly // writes one character to the standard output device (i.e., // the display monitor) until the loop-counter is exhausted. // To simplify this program's design and debugging, the main // function calls subroutines to perform the separate phases // of this work (i.e., the 'Input-Process-Output' paradigm). // // assemble-and-link using: gcc showdots.s -o showdots // // programmer: ALLAN CRUSE // written on: 01 FEB 2005 //---------------------------------------------------------------- # our symbolic constants .equ sys_write, 4 # service ID-number .equ sys_read, 3 # service ID-number .equ stdout_ID, 1 # device ID-number .equ stdin_ID, 0 # device ID-number .section .data msg: .ascii "How many dots do you want to see? " len: .int . - msg # number of msg characters inbuf: .space 8 # storage for user's input maxin: .int . - inbuf # amount of storage space dot: .byte '.' # ascii-code for a period count: .int 0 # dot repetitions counter newln: .byte '\n' # ascii-code for linefeed .section .text main: call obtain_input # our 'Input' subroutine call process_data # our 'Process' subroutine call print_output # our 'Output' subroutine ret # return to command shell .globl main # make entry-point visible obtain_input: # print message asking for user's input movl $sys_write, %eax # specify system service movl $stdout_ID, %ebx # specify desired device movl $msg, %ecx # output-string's address movl len, %edx # length of output-string int $0x80 # enter the Linux kernel # input the user's response movl $sys_read, %eax # specify system service movl $stdin_ID, %ebx # specify desired device movl $inbuf, %ecx # address of input buffer movl maxin, %edx # maximum bytes to accept int $0x80 # enter the Linux kernel movl %eax, maxin # actual length of input # TODO: we ought to terminate with an error-message if our # user types in more characters than can fit in our buffer ret process_data: # We will loop though the characters in 'inbuf' until we # encounter an ascii-code that is NOT a decimal numeral, # or until we come to the last byte in our input-buffer. # We shall begin with a 'count' value equal to zero, but # each time we encounter another valid numeral (i.e., an # ascii-code from '0' through '9') we will convert it to # its corresponding integer value, and that integer will # be added to ten-times-count. (We have not yet studied # the Pentium's multiplication instruction, so right now # we can get the effect of multiplying by ten by using a # series of add-instructions, since 10*x = 2*(x + 4*x). movl $0, count # initial count is zero cmpl $0, maxin # test: error in input? jle fini # yes, no further work xorl %esi, %esi # initial index is zero again: cmpl %esi, maxin # input exhausted yet? jle fini # yes, we are finished movb inbuf(%esi), %al # fetch next character incl %esi # advance source index cmpb $'0', %al # char preceeds '0'? jb fini # yes, invalid numeral cmpb $'9', %al # char follows '9'? ja fini # yes, invalid numeral subb $'0', %al # convert to a number movzx %al, %eax # extended to 32-bits movl count, %edx # prior 'count' value addl count, %edx # is doubled in EDX addl %edx, %edx # and doubled again addl count, %edx # now five times count addl %edx, %edx # is added to itself addl %eax, %edx # add the new number movl %edx, count # store this new total jmp again # go back for another fini: ret # return to 'main' print_output: # check: are any more dots to be printed? cmp $0, count # is 'count' positive? jle done # no, printing is done # print one dot character movl $sys_write, %eax # specify system service movl $stdout_ID, %ebx # specify desired device movl $dot, %ecx # address of output buffer movl $1, %edx # number of buffer bytes int $0x80 # enter the Linux kernel # adjust the loop counter decl count # decrement our counter jmp print_output # then go back for more done: # output the ascii 'newline' code movl $sys_write, %eax # specify system service movl $stdout_ID, %ebx # specify desired device movl $newln, %ecx # address of output buffer movl $1, %edx # number of buffer bytes int $0x80 # enter the Linux kernel ret # return to 'main' function .end # assembler can stop here