; animals.asm
;
;       This is a demonstration program to show how one can employ
;       random access data files within an assembly language program.
;
;       written on Wed  10-16-1996  by Ed Beroset
;               and released to the public domain by the author
;
;
        .MODEL small
        .STACK 1000h
        .386

DOS_FUNCTION            =        21h

DOS_CREATE_FILE         =       03Ch
DOS_OPEN_HANDLE         =       03Dh
        READ_ONLY       =       0
        WRITE_ONLY      =       1
        READ_WRITE      =       2
DOS_CLOSE_HANDLE        =       03Eh
DOS_READ_HANDLE         =       03Fh
DOS_WRITE_HANDLE        =       040h
DOS_MOVE_FILE_PTR       =       042h
        FROM_BEGIN      =       0
        FROM_CURRENT    =       1
        FROM_END        =       2

; pre-defined file handles
STDIN  = 0
STDOUT = 1
STDERR = 2
STDAUX = 3
STDPRN = 4

DosInt MACRO function, subfunction
        ifnb <subfunction>
          mov ax,(function SHL 8) OR (subfunction AND 0ffh)
        else
          mov ah,function
        endif
        int DOS_FUNCTION
ENDM

PutString MACRO zstring
        mov     dx,zstring
        call    puts
ENDM

        .DATA

animal STRUC
        yes      dd     ?
        no       dd     ?
        question db    80 dup (?)
animal ENDS

correct db      "Yea!  I guessed it!",13,10,0
What    db      "What animal were you thinking of?",13,10,0
getq    db      "Write a yes/no question which would differentiate a ",0
from    db      " from a ",0
caseof  db      "What's the answer in the case of a ",0
afilename db    "animal.dat",0
Isit    db      "Is it a ",0
qmark   db      '?'
crlf    db      13,10,0
cow     db      "cow",0
COWLEN  =       $ - cow

        .DATA?
curptr  dd ?
a       animal  <>
d       animal  <>
b       animal  <>
BUFFLEN =       80
linebuf db      BUFFLEN dup (?)


        .CODE
main proc
        .STARTUP
        push    ds                      ;
        pop     es                      ;
        call    initialize              ;
        jc      ErrorExit               ;
more:                                   ;
        call    ask                     ;
        jnc     more                    ;
        DosInt  DOS_CLOSE_HANDLE        ; close file
        jnc     NormalExit              ;
ErrorExit:
        mov     al,1
NormalExit:        
        .EXIT
main endp

; initialize
;
; this function opens the data file (or creates it if it doesn't yet
; exist) and reads the the first struc in the file.
;
initialize proc
        mov     dx,offset afilename     ; point to filename
        DosInt  DOS_OPEN_HANDLE, READ_WRITE ; open file for read/write
        jc      NoFile                  ;  jump if file doesn't exist
        mov     bx,ax                   ; save file handle
        mov     dx,offset a             ; point to a
        mov     cx,SIZE animal          ;
        DosInt  DOS_READ_HANDLE         ; read from file or device
        jmp     exit                    ;
NoFile:
        xor     cx,cx                   ; file attribute = normal file
        DosInt  DOS_CREATE_FILE         ; create/truncate file
        jc      exit                    ;
        mov     bx,ax                   ; save file handle
        mov     di,offset a.question    ;
        mov     si,offset cow           ;
        mov     cx,COWLEN               ; length of 'cow' string
        cld                             ;
        rep     movsb                   ;
        xor     eax,eax                 ;
        mov     [curptr],eax            ;
        mov     [a.yes],eax             ;
        mov     [a.no],eax              ;
        mov     dx,offset a             ;
        mov     cx,SIZE animal          ;
        DosInt  DOS_WRITE_HANDLE        ; write to file handle
exit:
        ret
initialize endp

ask proc
        cmp     [a.yes],0               ;
        jnz     notAnAnimal             ;
        PutString <offset Isit>         ;
notAnAnimal:
        PutString <offset a.question>   ;
        PutString <offset qmark>        ;
        mov     dx,word ptr [a.no]      ;
        mov     cx,word ptr [2 + a.no]  ;
        call    getyn                   ;
        jz      loaded                  ;
        mov     dx,word ptr [a.yes]     ;
        mov     cx,word ptr [2 + a.yes] ;
loaded:
        push    cx                      ;
        or      cx,dx                   ; Q: are both values zero?
        pop     cx                      ;
        jz      terminal                ;
        ; we have another question
        mov     word ptr [curptr],dx    ; update curptr
        mov     word ptr [curptr+2],cx  ;
        DosInt  DOS_MOVE_FILE_PTR,FROM_BEGIN ; seek from beginning
        jc      exit2                   ;
        mov     dx,offset a             ; point to a
        mov     cx,SIZE animal          ;
        DosInt  DOS_READ_HANDLE         ; read from file or device
        jmp     exit2                   ;
terminal:                               ;
        cmp     al,'y'                  ;
        jnz     learn_animal            ;
        ; we guessed the correct answer
        PutString <offset correct>      ;
        stc                             ;
        jmp     exit2                   ;
learn_animal:                           ;
        call    learn                   ;
        stc                             ;
exit2:                                  ;
        ret                             ;
ask endp

getyn proc
        push    bx                      ;
        push    cx                      ;
        push    dx                      ;
ask_again:                              ;
        mov     bx,STDIN                ; stdin
        mov     cx,1                    ; read one character
        mov     dx,offset linebuf       ;
        DosInt  DOS_READ_HANDLE         ; read from stdin
        mov     al,[linebuf]            ;
        or      al,040h                 ; set to lower case
        cmp     al,'y'                  ;
        jz      got_answer              ;
        cmp     al,'n'                  ;
        jnz     ask_again               ;
got_answer:                             ;
        push    ax                      ;
        mov     cx,BUFFLEN              ;
ignore:                                 ;
        DosInt  DOS_READ_HANDLE         ;
        cmp     ax,cx                   ;
        jz      ignore                  ;
        pop     ax                      ;
        cmp     al,'n'                  ;
        pop     dx                      ;
        pop     cx                      ;
        pop     bx                      ;
        ret                             ;
getyn endp

puts proc
        push    ax                      ;
        push    bx                      ;
        push    cx                      ;
        push    di                      ;
        mov     di,dx                   ;
        xor     al,al                   ;
        mov     cx,0ffffh               ;
        repne   scasb                   ;
        mov     cx,di                   ;
        sub     cx,dx                   ;
        dec     cx                      ;
        mov     bx,STDOUT               ; stdout
        DosInt  DOS_WRITE_HANDLE        ; write to file or handle
        pop     di                      ;
        pop     cx                      ;
        pop     bx                      ;
        pop     ax                      ;
        ret                             ;
puts endp

learn proc
        xor     eax,eax                 ;
        mov     [d.yes],eax             ;
        mov     [d.no],eax              ;
        mov     cx,ax                   ; zero out pointer
        mov     dx,ax                   ;
        DosInt  DOS_MOVE_FILE_PTR,FROM_END ; seek from end of file
        push    dx                      ; save high half of pointer
        push    ax                      ; save low half of pointer
        PutString <offset What>         ;
        mov     dx,offset d.question    ;
        mov     cx,BUFFLEN              ;
        call    getline                 ;
        PutString <offset getq>         ;
        PutString <offset a.question>   ;
        PutString <offset from>         ;
        PutString <offset d.question>   ;
        PutString <offset crlf>         ;
        mov     dx,offset b.question    ;
        mov     cx,BUFFLEN              ;
        call    getline                 ;
        PutString <offset caseof>       ;
        PutString <offset d.question>   ;
        PutString <offset qmark>        ;
        pop     edx                     ; pop file pointer
        mov     ecx,edx                 ;
        add     ecx,SIZE animal         ;
        call    getyn                   ;
        jnz     affirmative             ;
        xchg    edx,ecx                 ;
affirmative:                            ;
        mov     [b.no],edx              ;
        mov     [b.yes],ecx             ;
        mov     dx,offset a             ;
        mov     cx,(SIZE animal) * 2    ; write both a and d
        DosInt  DOS_WRITE_HANDLE        ; write to file handle
        mov     dx,word ptr [curptr]    ; go to curptr location
        mov     cx,word ptr [curptr+2]  ;
        DosInt  DOS_MOVE_FILE_PTR,FROM_BEGIN ; seek from beginning
        mov     dx,offset b             ; write b
        mov     cx,SIZE animal          ;
        DosInt  DOS_WRITE_HANDLE        ; write to file handle
        ret
learn endp

getline proc
        push    bx
        mov     bx,STDIN                ; stdin
        DosInt  DOS_READ_HANDLE         ;
        dec     ax                      ; remove CR
        dec     ax                      ;  and LF
        mov     bx,dx                   ;
        add     bx,ax                   ;
        mov     byte ptr [bx],0         ; insert null terminator
        pop     bx
        ret
getline endp

        END
