Tutorial: Moodle VPL -- Test Assembly Functions and checks that function is recursive

From DftWiki

Jump to: navigation, search

--D. Thiebaut (talk) 07:07, 5 November 2014 (EST)


Contents

MoodleVPLLogo.png



Moodle VPL Tutorials



This VPL activity tests an assembly language program containing 2 functions. The student program does not have a main (_start:) entry point, and the student is recommended to have his/her own test program to develop the program.
The VPL module provides a test4.asm program that tests each function by passing known parameter values and testing the output. The test program also checks the invariance of the stack and of the registers across the call (they should be the same before and after the function is called). In addition, the test script attempts to verify that the student function is recursive.


vpl_run.sh


#! /bin/bash
   
cat > vpl_execution <<EOF
#! /bin/bash
prog=Hw6_4


nasm -f elf test4.asm
nasm -f elf  \$prog.asm
nasm -f elf 231Lib.asm
ld -melf_i386 -o \$prog \$prog.o 231Lib.o test4.o
./\$prog | cat -v
EOF

 
chmod +x vpl_execution


vpl_evaluate.sh


#! /bin/bash
# D. Thiebaut
#
cat > vpl_execution <<EOF
#! /bin/bash
prog=Hw6_4

cat \${prog}.asm | sed 's:;.*$::g'  > _\${prog}.asm

grep f5  _\${prog}.asm | grep call > grepLines.out
grep f6  _\${prog}.asm | grep call >> grepLines.out

if [ -s grepLines.out ] ; then
  rm grepLines.out
else
  echo "Comment :=>> One of your functions is not recursive"
  echo "Grade :=>> 10"
  exit
fi


nasm -f elf test4.asm
nasm -f elf  \$prog.asm
nasm -f elf 231Lib.asm
ld -melf_i386 -o \$prog \$prog.o 231Lib.o test4.o

echo "<|--"
./\$prog | cat -v
echo "--|>"



noOK=\`./\$prog | grep -i OK | wc -l\`
if [[ noOK -ne 1 ]] ; then
   echo "Comment :=>> One or more of your functions has an invalid output"
fi

grade=\$(( 40 + 60 * \$noOK ))
if (( grade > 100 )); then
    grade=100
fi

echo "Grade :=>> \$grade"
EOF

 
chmod +x vpl_execution


vpl_evaluate.cases


 


Support Files


231Lib.asm


;;; A simple I/O library for CSC231.
;;; will be expanded as needed
;;; D. Thiebaut
;;; Taken from Swarnali Ahmed's 2002 program: mytools.inc
;;; http://cs.smith.edu/dftwiki
;;;
;;; Contains several functions for performing simple I/O of
;;; data.
;;; _printDec: function that prints an integer on the screen
;;; _printString: function that prints a string on the screen
;;; _println: moves the cursor to the next line on the screen
       
%assign SYS_EXIT        1
%assign SYS_WRITE       4
%assign STDOUT          1
       
global  _printDec
global  _printString
global  _println
global  _getInput
       
        section         .text

;;; ;------------------------------------------------------
;;; ;------------------------------------------------------
;;; ; flushSTDIn: flush the stdin buffer.
;;; ; from http://stackoverflow.com/questions/23036509/
;;; ;                    assembly-x86-nasm-avoid-read-return-key
;;; ; REGISTERS MODIFIED: eax, ebx, ecx, edx (and more)
;;; ;------------------------------------------------------
;;; ;------------------------------------------------------
_flushSTDIn:    
                mov eax,54            ; kernel function SYS_IOCTL
                mov ebx,0           ; EBX=0: STDIN
                mov ecx,0x540B      ; ECX=0x540B: TCFLSH
                xor edx, edx        ; EDX=0: TCIFLUSH
                int 0x80            ; sys_call
                ret
       
     
;;; ;------------------------------------------------------
;;; ;------------------------------------------------------
;;; ; getInput: gets a numerical input from the keyboard.
;;; ;           returns the resulting number in eax.
;;; ;           recognize - as the first character of
;;; ;           negative numbers.  Does not skip whitespace
;;; ;           at the begining.  Stops on first not decimal
;;; ;           digit encountered.
;;; ;
;;; ; NO REGISTERS MODIFIED, except eax
;;; ; Example:
;;; ;
;;; ;          call  getInput
;;; ;          mov   dword[x], eax ; put integer in x
;;; ;
;;; ;------------------------------------------------------
;;; ;------------------------------------------------------
_getInput:
                section .bss
buffer          resb    120
intg            resd    1
isneg           resb    1
       
                section .text
                pushad                  ; save all registers

                mov     esi, buffer     ; eci --> buffer
                mov     edi, 0          ; edi = counter of chars
.loop1:        
                mov     eax, 03         ; input
                mov     ebx, 0          ; stdin
                mov     ecx, esi        ; where to put the next char
                mov     edx, 1          ; one char at a time
                int     0x80            ; get the input into buffer

                cmp     byte[esi], 0    ; EOF?
                je      .parse
                cmp     byte[esi], 10   ; line feed?
                je      .parse
                inc     esi             ; point to next cell
                inc     edi             ; increment char counter
                jmp     .loop1

.parse:
                mov     esi, buffer     ; esi --> buffer
                mov     ecx, edi        ; loop for all chars received
                mov     dword[intg], 0
                mov     byte[isneg], 0

.negativ:      
                cmp     byte[esi], '-'
                jne     .loop
                inc     byte[isneg]
       
.loop:          mov     ebx, 0
                mov     bl, byte[esi]

                ;; stop on line feed
                cmp     bl, 10          ; line feed?
                je      .done

                ;; stop on non-digit characters
                cmp     bl, '0'
                jb      .done
                cmp     bl, '9'
                ja      .done
       
                ;; bl is a digit...  multiply .int by 10 first
                mov     edx, 10
                mov     eax, dword[intg]
                mul     edx             ; edx:eax <-- 10 * .int
               
                ;; add int version of char
                sub     bl, '0'
                add     eax, ebx
                mov     dword[intg], eax
                inc     esi
                loop    .loop
.done:
                ;; if negative, make eax neg
                cmp     byte[isneg], 0
                je      .return
                neg     eax
                mov     dword [intg], eax
       
                ;; restore registers and return result in eax
.return:

                popad
                mov     eax, [intg]
                ret    
       
       
;;; ;------------------------------------------------------
;;; ;------------------------------------------------------
;;; ; _printDec: takes the double word in eax and prints it
;;; ; to STDOUT in decimal.
;;; ;
;;; ; Examples:
;;; ; print a byte variable
;;; ;          mov     eax, 0
;;; ;          mov     al, byte[someVar]
;;; ;          call    _printDec
;;; ;
;;; ; print a word variable
;;; ;          mov     eax
;;; ;          mov     ax, word[otherVar]
;;; ;          call    _printDec
;;; ;
;;; ; print a double-word variable
;;; ;          mov     eax, dword[thirdVar]
;;; ;          call    _printDec
;;; ;
;;; ; print register edx in decimal
;;; ;
;;; ;          mov     eax, edx
;;; ;          call    _printDec
;;; ;
;;; ;REGISTERS MODIFIED: NONE
;;; ;------------------------------------------------------
;;; ;------------------------------------------------------

_printDec:
;;; saves all the registers so that they are not changed by the function

                section         .bss
.decstr         resb            10
.ct1            resd            1  ; to keep track of the size of the string

                section .text
                pushad                          ; save all registers

                mov             dword[.ct1],0   ; assume initially 0
                mov             edi,.decstr     ; edi points to decstring
                add             edi,9           ; moved to the last element of string
                xor             edx,edx         ; clear edx for 64-bit division
.whileNotZero:
                mov             ebx,10          ; get ready to divide by 10
                div             ebx             ; divide by 10
                add             edx,'0'         ; converts to ascii char
                mov             byte[edi],dl    ; put it in sring
                dec             edi             ; mov to next char in string
                inc             dword[.ct1]     ; increment char counter
                xor             edx,edx         ; clear edx
                cmp             eax,0           ; is remainder of division 0?
                jne             .whileNotZero   ; no, keep on looping

                inc             edi             ; conversion, finish, bring edi
                mov             ecx, edi        ; back to beg of string. make ecx
                mov             edx, [.ct1]     ; point to it, and edx gets # chars
                mov             eax, SYS_WRITE  ; and print!
                mov             ebx, STDOUT
                int             0x80

                popad                           ; restore all registers

                ret
       
;;; ; ------------------------------------------------------------
;;; ; _printString:        prints a string whose address is in
;;; ;                      ecx, and whose total number of chars
;;; ;                      is in edx.
;;; ; Examples:
;;; ; Assume a string labeled msg, containing "Hello World!",
;;; ; and a constant MSGLEN equal to 12.  To print this string:
;;; ;
;;; ;           mov        ecx, msg
;;; ;           mov        edx, MSGLEN
;;; ;           call       _printSTring
;;; ;
;;; ; REGISTERS MODIFIED:  NONE
;;; ; ------------------------------------------------------------

;;; ;save eax and ebx so that it is not modified by the function

_printString:
                push            eax
                push            ebx
       
                mov             eax,SYS_WRITE
                mov             ebx,STDOUT
                int             0x80

                pop             ebx
                pop             eax
                ret

;;; ; ------------------------------------------------------------
;;; ; _println             put the cursor on the next line.
;;; ;
;;; ; Example:
;;; ;           call      _println
;;; ;
;;; ; REGISTERS MODIFIED:  NONE
;;; ; ------------------------------------------------------------
_println:  
                section .data
.nl             db              10
       
                section .text
                push            ecx
                push            edx
       
                mov             ecx, .nl
                mov             edx, 1
                call            _printString

                pop             edx
                pop             ecx
                ret


Test4.asm


;;; test program for Hw6_4.asm
;;; D. Thiebaut
;;; Fall 2014
       
;;; the two functions to test
extern          f5, f6

;;; utility functions in 231Lib.asm
extern          _printDec
extern          _println
       
;;;  ------------------------------------------------------------
;;;  data areas
;;;  ------------------------------------------------------------
                section .data
;;; test input and expected output of the functions.
DATA1           equ     10
DATA2           equ     5      
EXPECTED1       equ     55
EXPECTED2       equ     35

       
linefeed        db      10
msg             db      10,"ERROR:",10
                db      "A function modifies some of the registers (ebx, ecx, edx, esi, or edi)"
                db      10,10,10
MSGLEN          equ     $-msg
msgStack        db      10, "ERROR", 10
                db      "A function modifies  esp or ebp"
                db      10, 10, 10
MSGLENSTACK     equ     $-msgStack

msgOK           db      "Output OK", 10, 10, 10
MSGLENOK        equ     $-msgOK

msgWrongOutput  db      10, "ERROR", 10
                db      "Incorrect Output", 10, 10, 10
MSGLENWRONG     equ     $- msgWrongOutput
       
saveEsp         dd      0
saveEbp         dd      0
result          dd      0
       
                section .text
                global  _start

;;; ; --- MACRO -----------------------------------------------
;;;  writeStr message, messageLength
%macro          writeStr2   2
                mov eax,4
                mov ebx,1
                mov ecx,%1
                mov edx,%2
                int 0x80
%endmacro
       
;;;  ------------------------------------------------------------
;;;  setRegs to predefined values
;;;  ------------------------------------------------------------
setRegs:        mov     eax, 0x11112222
                mov     ebx, 0x1234567A
                mov     edx, 0xaaaa5555
                mov     ecx, 0xffff0000
                mov     esi, 0xaaaabbbb
                mov     edi, 0xeeeeffff
                mov     dword[saveEbp], ebp
                mov     dword[saveEsp], esp
                ret

;;;  ------------------------------------------------------------
;;;  testRegs: displays message and exits if different values
;;;  detected in registers.
;;;  ------------------------------------------------------------
testRegs:       cmp     eax, 0x11112222
                jne     bad
                cmp     ebx, 0x1234567A
                jne     bad
                cmp     edx, 0xaaaa5555
                jne     bad
                cmp     ecx, 0xffff0000
                jne     bad
                cmp     esi, 0xaaaabbbb
                jne     bad
                cmp     edi, 0xeeeeffff
                jne     bad

                cmp     esp, dword[saveEsp]
                jne     badStack

                cmp     ebp, dword[saveEbp]
                jne     badStack
                ret
       
bad:            writeStr2 msg, MSGLEN
                jmp     exit

badStack:       writeStr2 msgStack, MSGLENSTACK
                jmp     exit

;;; ====================================================
;;;                        M A I N
;;; ====================================================

_start:

;;; test function f5
                call    setRegs
       
                push    dword 0
                push    dword DATA1
                call    f5
                pop     dword[result]
                call    testRegs

                mov     eax, dword[result]                
                call    _printDec
                call    _println
       


                cmp     dword[result],EXPECTED1
                je      next
                jmp     notOk

;;; test f6

next:           call    setRegs
                push    dword 0
                push    dword DATA2
                call    f6
                pop     dword[result]

                call    testRegs
           
                mov     eax, dword[result]
                call    _printDec
                call    _println

       
                cmp     dword[result],EXPECTED2
                je      Ok
                jmp     notOk
       
Ok:             writeStr2 msgOK, MSGLENOK
                jmp     exit
               
notOk:          writeStr2 msgWrongOutput, MSGLENWRONG
                jp      exit
       
;;;  exit()
exit:  
                mov     eax,1
                mov     ebx,0
                int     0x80    ; final system call