;----------------------------------------------------------;
;  Virtual memory system, Xms/Disk  - Tylisha C. Andersen  ;
;                                                          ;
; This code is hereby released into the public domain;  no ;
; restrictions are placed on use, copying or distribution. ;
;----------------------------------------------------------;

ideal

public      VMproc

model tiny
p386
codeseg

xmshandle   dw    0                 ; xms handle        -6
xmssize     dd    0                 ; xms length        -4
xmscall     dd    0                 ; xms call point    +0

fhandle     dw    0                 ; file handle       +4
fname       db    'C:\$VM$.TMP',0   ; file name         +6

;---------------------------- VMproc:  VM call point
;                             ------------------------
;                             -- ah = 0:  init
;                             -- ah = 1:  close
;                             -- ah = 2:  read
;                                   ds:dx = buffer
;                                   ebx = offset in VM
;                                   cx = length
;                             -- ah = 3:  write
;                                   ds:dx = buffer
;                                   ebx = offset in VM
;                                   cx = length
;                             ------------------------
;                             Returns cf = error flag
;                             ------------------------
;                             Offsets and lengths for
;                             read/write must be even.

proc        VMproc

            pushad                  ; save all registers
            push  ds es             ; load frame pointer
            mov   di, offset xmscall

            test  ah, ah            ; service 0, init?
            jne   vmm_cont

            cmp   [word cs:di+4], 0 ; already initialized?
            jne   vmm_bad

            push  cs                ; ds = cs
            pop   ds

            mov   ah, 3Ch           ; create the temp file
            xor   cx, cx
            lea   dx, [di+6]
            int   21h
            jc    vmm_done

            mov   [di+4], ax        ; save file handle

            mov   ax, 4300h         ; check for xms
            int   2Fh
            test  al, al
            jz    vmm_noxms

            mov   al, 10h           ; get xms call point
            int   2Fh
            mov   [di], bx
            mov   [di+2], es

            mov   ah, 8             ; get free xms size
            call  [dword di]
            test  ax, ax            ; no xms memory free?
            jz    vmm_noxms

            push  ax
            xchg  dx, ax            ; allocate all xms
            mov   ah, 9
            call  [dword di]
            test  ax, ax            ; didn't work?
            jz    vmm_noxms

            xor   ebx,ebx           ; ebx = xms size
            pop   bx
            shl   ebx, 10
            mov   [di-4], ebx       ; save xms size
            mov   [di-6], dx        ; save xms handle
            jmp   vmm_done          ; return

vmm_noxms:  xor   eax, eax          ; set xms size 0
            mov   [di-4], eax
            jmp   vmm_done

vmm_bad:    stc                     ; invalid request
            jmp   vmm_done

vmm_cont:   cmp   ah, 4             ; service out of range?
            jae   vmm_bad

            cmp   [word cs:di+4], 0 ; not initialized yet?
            je    vmm_bad

            cmp   ah, 1             ; check service number
            jne   vmm_rw  

            push  cs                ; ds = cs
            pop   ds

            xor   eax, eax          ; clear the handle
            mov   [di+4], ax
            cmp   [di-4], eax       ; check for xms
            je    vmm_skip

            mov   ah, 10            ; free xms block
            mov   dx, [di-6]
            call  [dword di]

vmm_skip:   mov   ah, 3Eh           ; close temp file
            mov   bx, [di+4]
            int   21h

            mov   ah, 41h           ; delete temp file
            lea   dx, [di+6]
            int   21h

vmm_done:   pop   es ds             ; restore registers
            popad
            ret                     ; return

vmm_rw:     test  ebx, 0F0000001h   ; offset and length must be
            jnz   vmm_bad           ; even for read/write, also
            test  cl, 1             ; the length can't be zero.
            jnz   vmm_bad
            test  cx, cx
            jz    vmm_bad

            mov   ebp, [cs:di-4]    ; ebp = xms length
            movzx ecx, cx           ; ecx = end offset
            add   ecx, ebx

            cmp   ebx, ebp          ; check for xms part
            jae   vmm_file

            pushad                  ; save all registers
            push  ds

            cmp   ecx, ebp          ; partly in file?
            jb    $+5
            mov   ecx, ebp

            sub   ecx, ebx          ; ecx = length, si = block
            pusha                   ; get 16 bytes space
            mov   si, sp
            push  ss

            cmp   ah, 2             ; check service number
            jne   vmm_write

            mov   [ss:si+14], ds    ; set segment, ds = ss
            pop   ds

            mov   ax, [di-6]        ; set xms handle
            mov   [si+4], ax
            mov   [si+6], ebx       ; set xms offset
            mov   [word si+10], 0
            mov   [si+12], dx       ; set buffer offset
                  
            jmp   vmm_xcont         ; jump to continue

vmm_write:  mov   [ss:si+8], ds     ; set segment, ds = cs
            pop   ds

            mov   ax, [di-6]        ; set xms handle
            mov   [si+10], ax
            mov   [si+12], ebx      ; set xms offset
            mov   [word si+4], 0
            mov   [si+6], dx        ; set buffer offset

vmm_xcont:  mov   [si], ecx         ; set length
            mov   ah, 11            ; do it
            call  [dword cs:di]
            add   sp, 16            ; release the block
            cmp   al, 1             ; check return value

            pop   ds                ; restore registers
            popad

            jc    vmm_jdone         ; jump if error

vmm_file:   sub   ecx, ebp          ; no file part?
            cmc
            jnc   vmm_jdone

            sub   ebx, ebp          ; ebx = start in file
            jge   $+5
            xor   ebx, ebx

            xchg  bp, ax            ; save service number

            sub   ecx, ebx          ; ecx = length
            push  cx dx

            mov   dx, bx            ; cx:dx = offset
            shr   ebx, 16
            mov   cx, bx

            mov   ax, 4200h         ; seek to position
            mov   bx, [cs:di+4]
            int   21h
            pop   dx cx

            jc    vmm_jdone         ; jump if error

            xchg  bp, ax            ; ah = service
            add   ah, 3Dh
            int   21h               ; read/write file data
vmm_jdone:  jmp   vmm_done          ; return

endp        VMproc

end
