;----------------------------------------------------------;
;  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

xmsblock    dw    8 dup(0)          ; xms block         +4

fhandle     dw    0                 ; file handle       +20
fname       db    'C:\$VM$€€.TMP',0 ; file name         +22

;---------------------------- 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             ; bp = base address
            mov   bp, offset xmscall

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

            cmp   [word cs:bp+20], 0 ; already initialized?
            jne   vmm_bad

            push  cs                ; ds = cs
            pop   ds

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

            mov   [bp+20], 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   [bp], bx
            mov   [bp+2], es

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

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

            pop   ax
            movzx ebx, ax           ; ebx = xms size
            shl   ebx, 10
            mov   [bp-4], ebx       ; save xms size
            mov   [bp-6], dx        ; save xms handle
            jmp   vmm_done          ; return

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

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

            cmp   [word cs:bp+20], 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   [bp+20], ax
            cmp   [bp-4], eax       ; check for xms
            je    vmm_skip

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

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

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

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

vmm_bad:    stc                     ; invalid request
            jmp   vmm_done

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   edi, [cs:bp-4]    ; edi = xms length
            movzx ecx, cx           ; ecx = end offset
            add   ecx, ebx

            cmp   ah, 2             ; check service number
            jne   vmm_write

            cmp   ebx, edi          ; check for xms part
            jae   vmm_rfile

            pushad                  ; save all registers
            push  ds

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

            sub   ecx, ebx          ; ecx = length, ds:si = block
            lea   si, [bp+4]
            mov   [cs:si+14], ds
            push  cs
            pop   ds

            mov   [si], ecx         ; set length
            mov   ax, [bp-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
                  
            mov   ah, 11            ; do it
            call  [dword bp]
            cmp   al, 1             ; check return value

            pop   ds                ; restore registers
            popad

            jc    vmm_done          ; jump if error

vmm_rfile:  mov   si, 3F00h         ; si = read service
            jmp   vmm_file          ; do file access

vmm_write:  cmp   ebx, edi          ; check for xms part
            jae   vmm_wfile

            pushad                  ; save all registers
            push  ds

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

            sub   ecx, ebx          ; ecx = length, ds:si = block
            lea   si, [bp+4]
            mov   [cs:si+8], ds
            push  cs
            pop   ds

            mov   [si], ecx         ; set length
            mov   ax, [bp-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

            mov   ah, 11            ; do it
            call  [dword bp]
            cmp   al, 1             ; check return value

            pop   ds                ; restore registers
            popad

            jc    vmm_jdone         ; jump if error

vmm_wfile:  mov   si, 4000h         ; si = write service

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

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

            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:bp+20]
            int   21h
            pop   dx cx

            jc    vmm_jdone         ; jump if error

            mov   ax, si            ; read file data
            int   21h
vmm_jdone:  jmp   vmm_done          ; return

endp        VMproc

end
