home *** CD-ROM | disk | FTP | other *** search
- ; ==== Adjustable Memory Disk Device Driver ====
- ;
- ; (c) Copyright 1986 by Gary R. Cramblitt
- ;
- ; v2.2 1 Jul 86 Initial version
- ; v2.3 24 Aug 86 Bug. FAT media byte not updated properly
- ; v2.4 29 Aug 86 Sync version # with ADJRAM.C No other changes.
- ; v2.5 30 Aug 86 Add /E (LOTUS/INTEL,Microsoft Expanded Memory) support;
- ; Increase root directory to 128 entries;
- ; Permit maximum size of 2043K.
- ; Get rid of second FAT.
- ; v3.0 5 Sep 86 Sync version # with ADJRAM.C. No other changes.
- ;
- ; ==== Constant Definition ===================
- ;
- ; ---- Customizable Definitions -----
- ; If these are changed, the corresponding symbols in adjram.c
- ; must be altered.
-
- min_size_K equ 64 ; minimum 64K ram disk
- max_size_K equ 2043 ; maximum 2043K ram disk
- sec_per_blk equ 64 ; 32K increments
- bytes_per_sec equ 512 ; bytes per sector
- sec_per_cl equ 1 ; sectors per cluster
- par_per_sec_lg2 equ 5 ; 16-byte paragraphs per sector
- ; log 2. Must match bytes_per_sec
- fats_per_disk equ 1 ; number of FATS
-
- ; ---- Derived quantities ----
-
- sec_per_K equ 1024/bytes_per_sec ; sectors per 1024 bytes
- par_per_sec equ bytes_per_sec/16 ; 16-byte paragraphs per sector
- min_size_sec equ min_size_K*sec_per_K ; min size of disk in sectors
- max_size_sec equ max_size_K*sec_per_K ; max size of disk in sectors
- bytes_per_fat equ max_size_sec/sec_per_cl*3/2
- sec_per_fat equ (bytes_per_fat+bytes_per_sec-1)/bytes_per_sec
- K_per_blk equ sec_per_blk/sec_per_K
- em_pag_per_mem_blk equ K_per_blk/16 ; EM pages per memory block
-
- ; ---- Request Header Structure Definitions ----
-
- rh_length equ 0 ; (byte) length field
- rh_unit equ 1 + rh_length ; (byte) unit field
- rh_command equ 1 + rh_unit ; (byte) command code
- rh_status equ 1 + rh_command ; (word) status field
- rh_reserved equ 2 + rh_status ; (8 bytes)
- rh_size equ 8 + rh_reserved ; request header size
-
- ; ---- Request Header Status values ----
-
- s_done equ 0100h ; done, no errors
- s_busy equ 0200h ; busy, no errors
-
- e_err equ 8000h
- e_protect equ e_err+0h ; error: write protect
- e_unknown_unit equ e_err+1h ; error: unknown unit
- e_not_ready equ e_err+2h ; error: not ready
- e_command equ e_err+3h ; error: unknown command
- e_crc equ e_err+4h ; error: bad CRC
- e_bad_length equ e_err+5h ; error: bad structure length
- e_seek equ e_err+6h ; error: bad seek
- e_media equ e_err+7h ; error: unknown media
- e_not_found equ e_err+8h ; error: sector not found
- e_paper equ e_err+9h ; error: out of paper
- e_write equ e_err+ah ; error: write fault
- e_read equ e_err+bh ; error: read fault
- e_general equ e_err+ch ; error: general error not listed above
-
- ; ---- Non-destructive read parameter block ----
-
- rh_read_data equ rh_size ; (byte) non-destructive data
-
- ; ---- Input/output parameter block ----
-
- rh_media equ 0 + rh_size ; (byte) media descriptor
- rh_buf_offset equ 1 + rh_media ; (word) transfer buffer offset
- rh_buf_segment equ 2 + rh_buf_offset ; (word) transfer buffer segment
- rh_buf_size equ 2 + rh_buf_segment ; (word) transfer buffer size
- rh_start equ 2 + rh_buf_size ; (word) transfer starting sector
-
- m_fized equ 0f8h ; media: fixed disk
-
- m_ss9 equ 0fch ; media: single sided, 8 sectors/track
- m_ds9 equ 0fdh
- m_ss8 equ 0feh
- m_ds8 equ 0ffh
-
- ; ---- Build BPB parameter block ----
- ; preceeded by media descriptor
- rh_bpb equ 1 + rh_media ; (dword) bpb buffer address
- rh_tbl_offset equ 4 + rh_bpb ; (dword) bpb table offset/segment
- rh_tbl_segment equ 2 + rh_tbl_offset
-
- ; ---- Media Check parameter block ----
-
- rh_check equ 1 + rh_media ; (byte) media check result
-
- mc_changed equ -1 ; media has changed
- mc_maybe equ 0 ; media may have been changed
- mc_same equ 1 ; media has not changed
-
- ; ---- Initialize parameter block ----
-
- rh_units equ 0 + rh_size ; (byte) number of units supported
- rh_end_offset equ 1 + rh_units ; (word) end address of driver
- rh_end_segment equ 2 + rh_end_offset
- rh_bpb_offset equ 2 + rh_end_segment ; (word) BPB array address
- rh_bpb_segment equ 2 + rh_bpb_offset
-
- ; ---- DOS interrupts ----
-
- dosi_dosf equ 21h ; DOS function dispatcher
-
- ; ---- User Interrupts ----
-
- usri_emm equ 67h ; Expanded Memory Manager
-
- ; ---- DOS interrupt 21 functions ----
-
- dosf_outstr equ 9 ; display string
- dosf_seldisk equ 0eh ; select disk
- dosf_getdisk equ 19h ; get current disk
-
- ; ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ----
-
- emm_status equ 40h ; get manager status
- emm_get_PFseg equ 41h ; get page frame segment
- emm_get_pages equ 42h ; get number of pages
- emm_get_handle equ 43h ; get handle and allocate memory
- emm_map_memory equ 44h ; map memory
- emm_fre_handle equ 45h ; free handle and memory
- emm_get_ver equ 46h ; get EMM version
- emm_sav_map equ 47h ; save mapping context
- emm_res_map equ 48h ; restore mapping context
- emm_num_handles equ 4bh ; get number of EMM handles
- emm_hdl_pages equ 4ch ; get pages owned by handle
- emm_all_pages equ 4dh ; get pages for all handles
- emm_pag_map equ 4eh ; get or set page map
-
- ; ---- Device Driver Header Attribute Definitions ----
-
- a_input equ 0001h ; standard input device
- a_output equ 0002h ; standard output device
- a_nul equ 0004h ; NUL device
- a_clock equ 0008h ; CLOCK$ device
- a_dos equ 0 ; DOS block device (bit 13)
- a_not_dos equ 2000h ; non-DOS block device (bit 13)
- a_ioctl equ 4000h ; IOCTL functions supported
- a_block equ 0 ; block device (bit 15)
- a_character equ 8000h ; character device (bit 15)
-
- ; ==== Device Driver Header Definition ====
-
- cseg segment para public 'CODE'
-
- driver proc far
-
- assume cs:cseg,ds:cseg,es:cseg
-
- dd -1 ; last driver in chain
- dw a_block + a_dos ; driver attribute
- dw dev_strategy ; offset to strategy routine
- dw dev_interrupt ; offset to interrupt routine
- db 1 ; number of devices or device name
- db 7 dup ( ? ) ; filler for block device
-
- ; ==== Device Driver Tables ====
- ;
- ; ---- BIOS Parameter Block Table and Entries ----
-
- bpb_table dw bpb ; one entry for each unit
-
- ; ---- Request Header Address set by dev_strategy ----
-
- rh_address dd 1 dup ( ? ) ; request header base address
-
- rh_offset equ word ptr rh_address
- rh_segment equ word ptr rh_address + 2
-
- ; ---- Request Header Command Dispatch Table ----
-
- cmd_table dw initialize ; initialize driver
- dw media_check ; media check
- dw build_bpb ; build BPB
- dw ioctl_read ; IOCTL read
- dw read ; normal read
- dw check_input ; non-destructive read/status
- dw input_status ; input status
- dw input_flush ; flush input buffers
- dw write ; normal write
- dw write_verify ; normal write with read verify
- dw output_status ; output status
- dw output_flush ; flush output buffer
- dw ioctl_write ; IOCTL write
-
- ; ==== Common Device Driver Routines ====
- ;
- ; ---- Device Driver Strategy Routine ----
- ;
- ; es:bs == request header address
-
- dev_strategy proc far
- mov cs:rh_offset,bx
- mov cs:rh_segment,es
- ret
- dev_strategy endp
-
- ; ---- Device Driver Interrupt Routine ----
-
- dev_interrupt proc far
- push ax ; save registers used
- push bx
- push cx
- push dx
- push di
- push si
- push ds
- push es
- cld ; clear direction flag
- push cs ; setup small memory model
- pop ds ; ds := program segment
- les bx,rh_address ; es:bs := request header index
- mov si,es:rh_command[bx] ; si := request command (byte)
- and si,0ffh ; si := request command
- add si,si ; si := word table offset
- call word ptr cmd_table[si] ; ax := command result
- lds bx,cs:rh_address ; ds:bx := request header index
- mov rh_status[bx],ax ; update request status
- pop es ; restore register
- pop ds
- pop si
- pop di
- pop dx
- pop cx
- pop bx
- pop ax
- ret
-
- dev_interrupt endp
-
- ; **** END OF DEVICE INDEPENDENT PORTION OF DRIVER ****
-
- ; ---- Memory Block Table that follows BPB in boot sector ----
-
- mem_blk_table_entry struc
- typ db ? ; 0 = normal 1 = Expanded Memory
- par dw ? ; paragraph address of block
- siz dw ? ; number of sectors in the memory block
- hdl dw ? ; EM handle for the block
- mem_blk_table_entry ends
-
- nor_flg equ 0 ; Memory Block is in normal memory
- em_flg equ 1 ; Memory Block is in expanded memory
-
- ; ---- Flag indicating whether expanded memory mapping context has been
- ; saved yet or not. If nonzero, it indicates that the context has
- ; been saved.
-
- em_context_flg db 0 ; 0 = not saved; 1 = context saved
- em_context_hdl dw ? ; EM handle underwhich context saved
-
- ; ==== Memory Disk Device Driver Code ====
- ;
- ; ---- Driver support functions (near functions) ----
- ;
- ; es:bx == request header
- ; ds == cs
- ; All other registers are usable.
- ;
- ; ax := result status
- ;
- ; ---- Initialize driver ----
-
- initialize proc near ; initialize driver
-
- lea ax,word ptr mdisk_data ; ax := end of driver
- mov cl,4 ; cl := paragraph size log2
- shr ax,cl ; ax := paragraphs in driver
- mov dx,cs ; dx := driver segment
- add ax,dx ; ax := memory disk segment
- mov mem_blk_table.par,ax ; update for subsequent transfers
-
- mov ax,cs
- mov es,ax
- lea di,word ptr fat1 ; es:di := first FAT
- mov cx,sec_per_fat*bytes_per_sec ; cx = size
- push di ; save first FAT address
- push es
-
- mov al,cs:bpb_media ; al := media byte
- mov [di],al ; update FAT media byte
- mov word ptr 1[di],0ffffh ; allocate initial sectors
- add di,3 ; adjust FAT index
- sub cx,3 ; adjust FAT size
- xor al,al ; al := 0
- rep stosb ; clear rest of FAT
-
- pop ds
- pop si ; ds:si := first FAT index
-
- if fats_per_disk - 1 ; assemble if 2 FATS
- lea di,word ptr fat2 ; es:di := second FAT
- mov cx,sec_per_fat*bytes_per_sec ; cx := size
- rep movsb ; copy first FAT to second FAT
- endif
-
- mov ax,cs:bpb_root ; ax := number of directory entries
- mov cl,5 ; cl := size of entry log2 (32 bytes)
- shl ax,cl ; ax := directory size
-
- mov cx,ax ; cx := directory size
- xor al,al ; al := 0
- rep stosb ; zero directory
-
- lds bx,cs:rh_address ; ds:bx := request header address
- mov byte ptr rh_units[bx],1 ; return number of units
- mov word ptr rh_end_offset[bx],offset mdisk_data
- mov rh_end_segment[bx],cs ; Calc end address of disk
- mov ax,cs:mem_blk_table.siz
- mov cl,par_per_sec_lg2
- shl ax,cl
- add rh_end_segment[bx],ax ; return ending address of driver
- mov word ptr rh_bpb_offset[bx],offset bpb_table
- mov rh_bpb_segment[bx],cs ; return BPB table address
-
- push cs
- pop ds
- mov ah,dosf_getdisk ; get current default disk
- int dosi_dosf
- mov dl,al ; select it
- mov ah,dosf_seldisk
- int dosi_dosf ; DL := number of drives
- add al,'A' ; convert to letter
- mov drive,al ; store into message
- mov dx,offset initok
- mov ah,dosf_outstr
- int dosi_dosf
- mov ax,s_done ; ax := done, no errors
- ret
-
- initok db 10,13,'AMDISK v3.0 (c) Copyright 1986 by Gary Cramblitt'
- db '-- Initialized as disk '
- drive db ' '
- db ':',10,13,'$'
-
- initialize endp
-
- ; ---- Media check ----
- ;
- ; Memory disk is non-removable media, however, the size of the media
- ; can be changed. We check the number of 64K memory blocks allocated,
- ; which is located in the boot record. If it has changed, then the media
- ; has changed.
-
- media_check proc near ; media check
-
- lds bx,cs:rh_address ; ds:bx := request header address
- mov al,cs:bpb_media ; Get our media byte
- cmp al,rh_media[bx] ; Changed from what DOS has?
- jz media_same ; -- Yes
- mov byte ptr rh_check[bx],mc_changed
- mov ax,s_done
- ret
-
- media_same:
- mov byte ptr rh_check[bx],mc_same
- mov ax,s_done ; ax := function done, no errors
- ret
-
- media_check endp
-
- ; ---- Build BPB ----
- ;
- ; Read boot sector and copy into BPB buffer
- ; Adjust request header values
-
- build_bpb proc near ; build BPB
-
- mov al,cs:bpb_media ; al := media byte
- lds bx,cs:rh_address ; ds:bx := request header address
- mov byte ptr rh_media[bx],al
- mov word ptr rh_tbl_offset[bx],offset bpb
- mov word ptr rh_tbl_segment[bx],cs
- mov ax,s_done ; ax := function done, no errors
- ret
-
- build_bpb endp
-
- ; ---- Read from device ----
-
- read proc near ; normal read
-
- push es
- pop ds ; ds := request header segment
- mov di,rh_buf_offset[bx]
- mov ax,di
- and di,000fh ; di := buffer offset within paragraph
- mov cl,4
- shr ax,cl
- add ax,rh_buf_segment[bx] ; ax := paragraph of buffer
- mov es,ax ; es:di := normalized buffer address
- mov dx,rh_buf_size[bx] ; dx := sectors to read
- mov ax,rh_start[bx] ; ax := first sector to read
-
- mov bx,-(size mem_blk_table_entry) ; find the memory block which..
- find_mem_blk_2_read: ; contains the desired sector..
- add bx,size mem_blk_table ; by subtracting the # of sectors..
- sub ax,cs:mem_blk_table.siz[bx] ; in each block.
- jnc find_mem_blk_2_read
- add ax,cs:mem_blk_table.siz[bx] ; bx := ptr to memory block table
- ; ax := sector within the block
-
- push ax ; save sector within block
- mov cl,par_per_sec_lg2 ; paragraphs per sector log 2
- shl ax,cl ; ax := paragraph within memory block
-
- cmp cs:mem_blk_table.typ[bx],em_flg ; Expanded Memory?
- jnz read_set_par
- call map_hdl_2_pages ; - Yes, map EM to log mem
-
- read_set_par:
- add ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
- mov ds,ax
- xor si,si ; ds:si := address of sector
-
- mov cx,cs:mem_blk_table.siz[bx] ; cx := end sector
- pop ax ; ax := start sector
- sub cx,ax ; cx := secs remaining in mem blk
-
- read_one_sector:
- or dx,dx ; All sectors transfered?
- jz read_finish ; -- Yes
- dec dx
- push cx ; save sector count for this blk
- mov cx,bytes_per_sec ; 512 bytes per sector
- rep movsb ; copy sector to buffer
- pop cx ; cx := secs remaining in this blk+1
- mov ax,ds
- add ax,par_per_sec ; 32 paragraphs per sector
- mov ds,ax
- xor si,si ; ds:si := address of next sector
- mov ax,es ; advance es to next sector
- add ax,par_per_sec
- mov es,ax
- and di,000fh ; es:di := normalized transfer address
- loop read_one_sector ; if not end of mem blk, do another
- add bx,size mem_blk_table_entry
-
- cmp cs:mem_blk_table.typ[bx],em_flg ; Expanded Memory?
- jnz read_set_par_in_loop
- call map_hdl_2_pages ; - Yes, map EM to log mem
-
- read_set_par_in_loop:
- mov ax,cs:mem_blk_table.par[bx]
- mov ds,ax ; ds:si := start of new memory block
- mov cx,cs:mem_blk_table.siz[bx] ; cx := size of blk in sectors
- jmp read_one_sector
-
- read_finish:
- call restore_em_context ; If needed, restore em context
- mov ax,s_done ; ax := transfer done
- ret
-
- read endp
-
- ; ---- Write to device ----
-
- write proc near ; normal write
-
- push es
- pop ds ; ds := request header segment
- mov dx,rh_buf_size[bx] ; dx := sectors to write
- mov si,rh_buf_offset[bx]
- mov ax,si
- and si,000fh ; si := buffer offset within paragraph
- mov cl,4
- shr ax,cl
- add ax,rh_buf_segment[bx] ; ax := paragraph of buffer
- mov cx,ax
- mov ax,rh_start[bx] ; ax := first sector to write
- mov ds,cx ; ds:si := normalized buffer address
-
- mov bx,-(size mem_blk_table_entry) ; find the memory block which..
- find_mem_blk_2_write: ; contains the desired sector..
- add bx,size mem_blk_table_entry ; by subtracting the # of sectors..
- sub ax,cs:mem_blk_table.siz[bx] ; in each block.
- jnc find_mem_blk_2_write
- add ax,cs:mem_blk_table.siz[bx]
- ; bx := ptr to memory block table
- ; ax := sector within the block
- push ax ; save sector within the block
-
- mov cl,par_per_sec_lg2 ; 32 paragraphs per sector log 2
- shl ax,cl ; ax := paragraph within memory block
-
- cmp cs:mem_blk_table.typ[bx],em_flg ; Expanded Memory?
- jnz write_set_par
- call map_hdl_2_pages ; - Yes, map EM
-
- write_set_par:
- add ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
- mov es,ax
- xor di,di ; es:di := address of sector
-
- mov cx,cs:mem_blk_table.siz[bx] ; cx := size of blk in sectors
- pop ax ; ax := start sector within blk
- sub cx,ax ; cx := secs remaining in mem blk
-
- write_one_sector:
- or dx,dx ; All sectors transfered?
- jz write_finish ; -- Yes
- dec dx
- push cx ; save sector counter
- mov cx,bytes_per_sec ; 512 bytes per sector
- rep movsb ; copy sector to buffer
- pop cx ; cx := secs remaining in blk + 1
- mov ax,es
- add ax,par_per_sec ; 32 paragraphs per sector
- mov es,ax
- xor di,di ; es:di := address of next sector
- mov ax,ds ; advance ds to next sector
- add ax,par_per_sec
- mov ds,ax
- and si,000fh ; ds:si := normalized transfer address
- loop write_one_sector ; if not end of block, do another
- add bx,size mem_blk_table_entry
-
- cmp cs:mem_blk_table.typ[bx],em_flg ; Expanded Memory?
- jnz write_set_par_in_loop
- call map_hdl_2_pages ; - Yes, map EM
-
- write_set_par_in_loop:
- mov ax,cs:mem_blk_table.par[bx]
- mov es,ax ; es:di := start of new memory block
- mov cx,cs:mem_blk_table.siz[bx] ; cx := size of new blk in secs
- jmp write_one_sector
-
- write_finish:
- call restore_em_context ; If needed, restore em context
- mov ax,s_done ; ax := transfer done
- ret
-
- write endp
-
-
- ; ---- Write and verify ----
-
- write_verify proc near ; normal write with read verify
-
- jmp write ; assume no errors
-
- write_verify endp
-
-
- ; ==== Support Functions ====
- ;
- ; ---- Map a handle of Expanded Memory to the Memory Block pages
-
- ; ENTRY: BX = pointer to Memory Block Table entry (in CS)
- ; EXIT: Memory is mapped and paragraph address updated in entry.
- ; Flag at CS:em_context_flg is set to nonzero to indicate that
- ; expanded memory mapping context has been saved. The handle
- ; underwhich the context was saved is stored at
- ; CS:em_context_hdl.
- ; USES: CX
-
- map_hdl_2_pages proc near
- push ax
- push bx
- push dx
- mov dx,cs:mem_blk_table.hdl[bx] ; get handle of mem blk
- cmp cs:em_context_flg,0 ; em context saved yet?
- jnz do_map
- mov ah,emm_sav_map ; - No, then save it
- int usri_emm
- mov cs:em_context_hdl,dx ; store handle underwhich saved
- mov cs:em_context_flg,1 ; flag as saved
- do_map:
- push bx
- mov ah,emm_get_PFseg ; get page frame segment
- int usri_emm
- mov ax,bx
- pop bx
- mov cs:mem_blk_table.par[bx],ax ; store page frame segment
- mov cx,em_pag_per_mem_blk ; # of 16K EM pages to map
- map_log_2_phy:
- mov ah,emm_map_memory ; map memory
- mov al,cl ; physical page number
- mov bx,cx ; logical page number
- dec al ; adjust relative to 0
- dec bx ; adjust relative to 0
- int usri_emm
- loop map_log_2_phy
- pop dx
- pop bx
- pop ax
- ret
- map_hdl_2_pages endp
-
- ; ---- Restore expanded memory context, if needed. ---
- ; USES: AX,DX
-
- restore_em_context proc near
- cmp cs:em_context_flg,0 ; was context saved?
- jz restore_em_context_xit ; - No, then just exit
- mov ah,emm_res_map ; - Yes, then restore it..
- mov dx,cs:em_context_hdl ; ..using this handle
- int usri_emm
- mov cs:em_context_flg,0 ; clear context flag
- restore_em_context_xit:
- ret
- restore_em_context endp
-
- ; ---- Unimplemented functions ----
-
- unimplemented proc near
- mov ax,e_command
- ret
- unimplemented endp
-
- ioctl_read equ unimplemented ; IOCTL read
- check_input equ unimplemented ; non-destructive read/status
- input_status equ unimplemented ; input status
- input_flush equ unimplemented ; flush input buffers
- output_status equ unimplemented ; output status
- output_flush equ unimplemented ; flush output buffers
- ioctl_write equ unimplemented ; IOCTL write
-
- ; ==== Memory Disk Data Area ====
- ;
- ; Align to paragraph boundary for easy computation of the sector address
-
- if (( $ - driver) mod 16 )
- org ( $ - driver) + (16 - (( $- driver) mod 16 ))
- endif
-
- mdisk_data equ $ ; memory disk starts here
-
- ; ---- Boot record for initialization ----
-
- boot_record db 3 dup ( 0 ) ; non-bootable (no jump instruction )
- db 'AMDISK ' ; identification
-
- bpb: ; BIOS Parameter Block
- bytes_in_sector dw bytes_per_sec ; bytes/sector
- bpb_sec_per_cl db sec_per_cl ; sectors/cluster
- bpb_reserved dw 1 ; reserved sectors
- bpb_fats db fats_per_disk ; number of FAT's
- bpb_root dw 128 ; directory entries in root
- bpb_total dw min_size_sec ; total number of sectors
- bpb_media db 1 ; media type byte ...
- mem_blk_cnt equ bpb_media ; ...is used for number of mem blocks
- bpb_fat_size dw sec_per_fat ; sectors/FAT
-
- dw 1 ; sectors/track
- dw 1 ; number of heads
- dw 0 ; hidden sectors
-
- mem_blk_table mem_blk_table_entry <nor_flg,,min_size_sec,0> ; memory block table
- end_mem_blk_table equ $+(size mem_blk_table)*(max_size_sec/sec_per_blk)
-
- fat1 equ boot_record + bytes_per_sec
- fat2 equ fat1 + (bytes_per_sec*sec_per_fat)
- root_dir equ fat1+(fats_per_disk*sec_per_fat*bytes_per_sec)
- first_data_sec equ root_dir + (64*32)
-
- ; ==== End of Memory Disk Device Driver ====
-
- driver endp
- cseg ends
- end
-
-