; CIS-261
; implementation of I/O procedures called by macros in io.obj
; flat memory model version

.386
.MODEL FLAT

PUBLIC itoaproc, atoiproc, dtoaproc, atodproc, outproc, inproc, szlenproc

.CODE

; itoaproc(source, dest)
; convert integer (source) to string of 6 characters at given destination address
itoaproc    PROC   NEAR32
            push   ebp                  ; save base pointer
            mov    ebp, esp             ; establish stack frame
            push   eax                  ; Save registers
            push   ebx                  ;   used by
            push   ecx                  ;   procedure
            push   edx
            push   edi
            pushf                      ; save flags

            mov    ax, [ebp+12]        ; first parameter (source integer)
            mov    edi, [ebp+8]        ; second parameter (dest offset)
ifSpecial:  cmp    ax,8000h            ; special case -32,768?
            jne    EndIfSpecial        ; if not, then normal case
            mov    BYTE PTR [edi],'-'  ; manually put in ASCII codes
            mov    BYTE PTR [edi+1],'3'  ;   for -32,768
            mov    BYTE PTR [edi+2],'2'
            mov    BYTE PTR [edi+3],'7'
            mov    BYTE PTR [edi+4],'6'
            mov    BYTE PTR [edi+5],'8'
            jmp    ExitIToA            ; done with special case
EndIfSpecial:

            mov    dx, ax              ; save source number

            mov    al,' '              ; put blanks in
            mov    ecx,5               ;   first five
            cld                        ;   bytes of
            rep stosb                  ;   destination field    

            mov    ax, dx              ; copy source number
            mov    cl,' '              ; default sign (blank for +)
IfNeg:      cmp    ax,0                ; check sign of number
            jge    EndIfNeg            ; skip if not negative
            mov    cl,'-'              ; sign for negative number
            neg    ax                  ; number in AX now >= 0
EndIfNeg:

            mov    bx,10               ; divisor

WhileMore:  mov    dx,0                ; extend number to doubleword
            div    bx                  ; divide by 10
            add    dl,30h              ; convert remainder to character
            mov    [edi],dl            ; put character in string
            dec    edi                 ; move forward to next position
            cmp    ax,0                ; check quotient
            jnz    WhileMore           ; continue if quotient not zero

            mov    [edi],cl            ; insert blank or "-" for sign

ExitIToA:   popf                       ; restore flags and registers
            pop    edi
            pop    edx
            pop    ecx
            pop    ebx 
            pop    eax
            pop    ebp 
            ret    6                   ;exit, discarding parameters
itoaproc    ENDP

; dtoaproc(source, dest)
; convert double (source) to string of 11 characters at given offset in DS (dest)
dtoaproc    PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            push   eax                 ; Save registers
            push   ebx                 ;   used by
            push   ecx                 ;   procedure
            push   edx
            push   edi
            pushf                      ; save flags

            mov    eax, [ebp+12]         ; first parameter (source double)
            mov    edi, [ebp+8]          ; second parameter (dest addr)
ifSpecialD: cmp    eax,80000000h       ; special case -2,147,483,648?
            jne    EndIfSpecialD       ; if not, then normal case
            mov    BYTE PTR [edi],'-'   ; manually put in ASCII codes
            mov    BYTE PTR [edi+1],'2' ;   for -2,147,483,648
            mov    BYTE PTR [edi+2],'1'
            mov    BYTE PTR [edi+3],'4'
            mov    BYTE PTR [edi+4],'7'
            mov    BYTE PTR [edi+5],'4'
            mov    BYTE PTR [edi+6],'8'
            mov    BYTE PTR [edi+7],'3'
            mov    BYTE PTR [edi+8],'6'
            mov    BYTE PTR [edi+9],'4'
            mov    BYTE PTR [edi+10],'8'
            jmp    ExitDToA            ; done with special case
EndIfSpecialD:

            mov    edx, eax            ; save source number

            mov    al,' '              ; put blanks in
            mov    ecx,10              ;   first ten
            cld                        ;   bytes of
            rep stosb                  ;   destination field    

            mov    eax, edx            ; copy source number
            mov    cl,' '              ; default sign (blank for +)
IfNegD:     cmp    eax,0               ; check sign of number
            jge    EndIfNegD           ; skip if not negative
            mov    cl,'-'              ; sign for negative number
            neg    eax                 ; number in EAX now >= 0
EndIfNegD:

            mov    ebx,10              ; divisor

WhileMoreD: mov    edx,0               ; extend number to doubleword
            div    ebx                 ; divide by 10
            add    dl,30h              ; convert remainder to character
            mov    [edi],dl            ; put character in string
            dec    edi                 ; move forward to next position
            cmp    eax,0               ; check quotient
            jnz    WhileMoreD          ; continue if quotient not zero

            mov    [edi],cl            ; insert blank or "-" for sign

ExitDToA:   popf                       ; restore flags and registers
            pop    edi
            pop    edx
            pop    ecx
            pop    ebx 
            pop    eax
            pop    ebp 
            ret    8                   ;exit, discarding parameters
dtoaproc    ENDP

; atoiproc(source)
; Procedure to scan data segment starting at source address, interpreting 
; ASCII characters as an integer value which is returned in AX.

; Leading blanks are skipped.  A leading - or + sign is acceptable.
; Digit(s) must immediately follow the sign (if any).
; Memory scan is terminated by any non-digit, and the address of 
; the terminating character is in ESI.

; The following flags are affected:
;   AC is undefined
;   PF, SF and ZF reflect sign of number returned in AX.
;   CF reset to 0
;   OF set to indicate error.  Possible error conditions are:
;     - no digits in input
;     - value outside range -32,768 to 32,767
;   (AX) will be 0 if OF is set.

atoiproc    PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            sub    esp, 2              ; local space for sign
            push   ebx                 ; Save registers
            push   ecx
            push   edx
            pushf                      ; save flags

            mov    esi,[ebp+8]         ; get parameter (source addr)

WhileBlank: cmp    BYTE PTR [esi],' '  ; space?
            jne    EndWhileBlank       ; exit if not
            inc    esi                 ; increment character pointer
            jmp    WhileBlank          ; and try again
EndWhileBlank:

            mov    ax,1                ; default sign multiplier
IfPlus:     cmp    BYTE PTR [esi],'+'  ; leading + ?
            je     SkipSign            ; if so, skip over
IfMinus:    cmp    BYTE PTR [esi],'-'  ; leading - ?
            jne    EndIfSign           ; if not, save default +
            mov    ax,-1               ; -1 for minus sign
SkipSign:   inc    esi                 ; move past sign
EndIfSign:

            mov    [ebp-2],ax          ; save sign multiplier
            mov    ax,0                ; number being accumulated
            mov    cx,0                ; count of digits so far

WhileDigit: cmp    BYTE PTR [esi],'0'  ; compare next character to '0'
            jl     EndWhileDigit       ; not a digit if smaller than '0'
            cmp    BYTE PTR [esi],'9'  ; compare to '9'
            jg     EndWhileDigit       ; not a digit if bigger than '9'
            imul   ax,10               ; multiply old number by 10
            jo     overflow            ; exit if product too large
            mov    bl,[esi]            ; ASCII character to BL
            and    bx,000Fh            ; convert to single-digit integer
            add    ax,bx               ; add to sum
            jc     overflow            ; exit if sum too large
            inc    cx                  ; increment digit count
            inc    esi                 ; increment character pointer
            jmp    WhileDigit          ; go try next character
EndWhileDigit:

            cmp    cx,0                ; no digits?
            jz     overflow            ; if so, set overflow error flag

; if value is 8000h and sign is '-',  want to return 8000h (-32,768)

            cmp    ax,8000h            ; 8000h ?
            jne    TooBig?
            cmp    WORD PTR [ebp-2],-1 ; multiplier -1 ?
            je     ok1                 ; if so, return 8000h

TooBig?:    test   ax,ax               ; check sign flag
            jns    ok                  ; will be set if number > 32,767

overflow:   pop    ax                  ; get flags
            or     ax,0000100001000100B  ; set overflow, zero & parity flags
            and    ax,1111111101111110B  ; reset sign and carry flags
            push   ax                  ; push new flag values
            mov    ax,0                ; return value of zero
            jmp    AToIExit            ; quit

ok:         imul   WORD PTR [ebp-2]    ; make signed number
ok1:        popf                       ; get original flags
            test   ax,ax               ; set flags for new number
            pushf                      ; save flags

AToIExit:   popf                       ; get flags
            pop    edx                 ; restore registers
            pop    ecx
            pop    ebx
            mov    esp, ebp            ; delete local variable space
            pop    ebp 
            ret    4                   ; exit, removing parameter
atoiproc    ENDP

; atodproc(source)
; Procedure to scan data segment starting at source address, interpreting 
; ASCII characters as an integer value which is returned in EAX.

; Leading blanks are skipped.  A leading - or + sign is acceptable.
; Digit(s) must immediately follow the sign (if any).
; Memory scan is terminated by any non-digit, and the address of 
; the terminating character is in ESI.

; The following flags are affected:
;   AC is undefined
;   PF, SF and ZF reflect sign of number returned in EAX.
;   CF reset to 0
;   OF set to indicate error.  Possible error conditions are:
;     - no digits in input
;     - value outside range -2,147,483,648 to 2,147,483,647
;   (EAX) will be 0 if OF is set.

atodproc    PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            sub    esp, 4              ; local space for sign
            push   ebx                 ; Save registers
            push   ecx
            push   edx
            pushf                      ; save flags

            mov    esi,[ebp+8]         ; get parameter (source addr)

WhileBlankD:cmp    BYTE PTR [esi],' '  ; space?
            jne    EndWhileBlankD      ; exit if not
            inc    esi                 ; increment character pointer
            jmp    WhileBlankD         ; and try again
EndWhileBlankD:

            mov    eax,1               ; default sign multiplier
IfPlusD:    cmp    BYTE PTR [esi],'+'  ; leading + ?
            je     SkipSignD           ; if so, skip over
IfMinusD:   cmp    BYTE PTR [esi],'-'  ; leading - ?
            jne    EndIfSignD          ; if not, save default +
            mov    eax,-1              ; -1 for minus sign
SkipSignD:  inc    esi                 ; move past sign
EndIfSignD:

            mov    [ebp-4],eax         ; save sign multiplier
            mov    eax,0               ; number being accumulated
            mov    cx,0                ; count of digits so far

WhileDigitD:cmp    BYTE PTR [esi],'0'  ; compare next character to '0'
            jl     EndWhileDigitD      ; not a digit if smaller than '0'
            cmp    BYTE PTR [esi],'9'  ; compare to '9'
            jg     EndWhileDigitD      ; not a digit if bigger than '9'
            imul   eax,10              ; multiply old number by 10
            jo     overflowD           ; exit if product too large
            mov    bl,[esi]            ; ASCII character to BL
            and    ebx,0000000Fh       ; convert to single-digit integer
            add    eax,ebx             ; add to sum
            jc     overflowD           ; exit if sum too large
            inc    cx                  ; increment digit count
            inc    esi                 ; increment character pointer
            jmp    WhileDigitD         ; go try next character
EndWhileDigitD:

            cmp    cx,0                ; no digits?
            jz     overflowD           ; if so, set overflow error flag

; if value is 80000000h and sign is '-',  want to return 80000000h (-2^32)

            cmp    eax,80000000h       ; 80000000h ?
            jne    TooBigD?
            cmp    DWORD PTR [ebp-4],-1 ; multiplier -1 ?
            je     ok1D                ; if so, return 8000h

TooBigD?:   test   eax,eax             ; check sign flag
            jns    okD                 ; will be set if number > 2^32 - 1

overflowD:  pop    ax                  ; get flags
            or     ax,0000100001000100B  ; set overflow, zero & parity flags
            and    ax,1111111101111110B  ; reset sign and carry flags
            push   ax                  ; push new flag values
            mov    eax,0               ; return value of zero
            jmp    AToDExit            ; quit

okD:        imul   DWORD PTR [ebp-4]   ; make signed number
ok1D:       popf                       ; get original flags
            test   eax,eax             ; set flags for new number
            pushf                      ; save flags

AToDExit:   popf                       ; get flags
            pop    edx                 ; restore registers
            pop    ecx
            pop    ebx
            mov    esp, ebp            ; delete local variable space
            pop    ebp 
            ret    4                   ; exit, removing parameter
atodproc    ENDP

; *************** setup for Win32 I/O ****************

STD_OUTPUT EQU -11
STD_INPUT  EQU -10

GetStdHandle PROTO NEAR32 stdcall,
    nStdHandle:DWORD

ReadFile PROTO NEAR32 stdcall,
    hFile:DWORD, lpBuffer:NEAR32, nNumberOfCharsToRead:DWORD,
    lpNumberOfBytesRead:NEAR32, lpOverlapped:NEAR32

WriteFile PROTO NEAR32 stdcall,
    hFile:DWORD, lpBuffer:NEAR32, nNumberOfCharsToWrite:DWORD,
    lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32

.DATA

written    DWORD  ?
read       DWORD  ?
strAddr    DWORD  ?
strLength  DWORD  ?
hStdOut    DWORD  ?
hStdIn     DWORD  ?

.CODE

; outproc(source)
; Procedure to display null-terminated string
; No registers are changed; flags are not affected.

outproc     PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            pushad
            pushfd                     ; save flags

            mov    esi,[ebp+8]         ; source address
            mov    strAddr, esi

; find string length
            mov    strLength, 0        ; initialize string length
WhileChar:  cmp    BYTE PTR [esi], 0   ; character = null?
            jz     EndWhileChar        ; exit if so
            inc    strLength           ; increment character count
            inc    esi                 ; point at next character
            jmp    WhileChar
EndWhileChar:

            INVOKE GetStdHandle,       ; get handle for console output
              STD_OUTPUT
            mov    hStdOut, eax

            INVOKE WriteFile,
              hStdOut,                 ; file handle for screen
              strAddr,                 ; address of string
              strLength,               ; length of string
              NEAR32 PTR written,      ; bytes written
              0                        ; overlapped mode

            popfd                      ; restore flags
            popad                      ; restore registers
            pop    ebp
            ret    4                   ;exit, discarding parameter
outproc     ENDP

; inproc(dest,length)
; Procedure to input a string from keyboard.

; The string will be stored at the address given by dest.

; The length parameter gives the size of the user's buffer.  It is assumed 
; that there will be room for the string and a null byte.
  
; The string will be terminated by a null character (00h).

; Flags are unchanged.

inproc      PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            pushad                     ; save all registers
            pushfd                     ; save flags

            INVOKE GetStdHandle,       ; get handle for console
              STD_INPUT
            mov    hStdIn, eax

            mov    ecx, [ebp+8]        ; string length
            mov    strLength, ecx

            mov    esi, [ebp+12]       ; source address
            mov    strAddr, esi

            INVOKE ReadFile,
              hStdIn,                  ; file handle for keyboard
              strAddr,                  ; address of string
              strLength,                ; length of string
              NEAR32 PTR read,          ; bytes read
              0                         ; overlapped mode

            mov    ecx, read           ; number of bytes read
            mov    BYTE PTR [esi+ecx-2],0 ; replace CR/LF by trailing null


            popfd                      ; restore flags
            popad                      ; restore registers
            pop    ebp 
            ret    8                   ; exit, discarding parameters
inproc      ENDP

;ik-10/4/2008
; szlenproc(source)
; Procedure to calculate length of a null-terminated string
; No registers are changed; flags are not affected.

szlenproc   PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            pushad
            pushfd                     ; save flags

            mov    esi,[ebp+8]         ; source address
            mov    strAddr, esi

; find string length
            mov    strLength, 0        ; initialize string length
WhileChar:  cmp    BYTE PTR [esi], 0   ; character = null?
            jz     EndWhileChar        ; exit if so
            inc    strLength           ; increment character count
            inc    esi                 ; point at next character
            jmp    WhileChar
EndWhileChar:
            popfd                      ; restore flags
            popad                      ; restore registers
            pop    ebp 
            mov    eax, strLength      ; rerun string length in EAX
            ret    4                   ;exit, discarding parameter
szlenproc   ENDP


            END
