1 TITLE EXTENDED
/EXPANDED MEMORY DISK CACHE
6 ;; Will use IBM extended memory on PC-AT or
7 ;; use Above Board on PC, XT, or AT, and
8 ;; use extended, expanded, or upper extended memory on AT&T 6300 PLUS
11 ;; device = SMARTDRV.sys [bbbb] [/a] [/u]
13 ; bbbb First numeric argument, if present, is memory size
14 ; in K bytes. Default value is 256. Min is 128. Max
17 ; By default PC AT Extended Memory is to be used.
18 ; It is an error if /E is specified on a machine other
19 ; than an IBM PC AT. /E is the default.
20 ; NOTE: Information in cache in PC AT extended memory
21 ; will be lost at system re-boot (warm or cold). This is
22 ; due to the fact that the IBM PC AT ROM bootstrap code
23 ; zeroes all of memory.
24 ; NOTE: There is 1k of memory overhead. That is to say,
25 ; if there are 512k bytes of extended memory, there
26 ; will be 511k bytes available for assignment to int13.
27 ; This 1k overhead is fixed and makes int13 compatible
29 ; NOTE: The same allocation strategy as is used in RAMDrive
30 ; is used. This allows RAMDrive and INT13 to coexist on
31 ; the same system. Mixing with IBM VDISK is NOT supported.
33 ; /a Specifies that Above Board memory is to be used. It
34 ; is an error if the above board device driver is not
36 ; NOTE: Information in cache in Above Board memory
37 ; will be lost at system re-boot (warm or cold). This is
38 ; due to the fact that the EMM device driver performs a
39 ; destructive test when it is installed which zeros all
40 ; of the Above Board memory.
41 ;; /u Specifies that upper extended memory will be used
42 ;; on the AT&T 6300 PLUS. Upper extended memory
43 ;; is the memory beginning at FA0000. It is used
44 ;; to hold the UNIX kernel when the machine is running
45 ;; Simul-Task. However, when operating as a pure
46 ;; MS-DOS machine, this 384K of memory is available
48 ;; Note that it is an error to specify this switch
49 ;; if the machine is not a 6300 PLUS.
51 ; NOTE WARNING: ALL OF THIS CODE ASSUMES THAT ALL HARDFILES ARE 512 BYTES
52 ; PER SECTOR!!! All other hardfile parameters are read via INT 13, but
53 ; Bytes/sector MUST be IBM standard 512.
55 ; MODIFICATION HISTORY
57 ; 1.00 5/10/86 ARR Initial version based on RAMDrive 1.16.
58 ; 1.01 5/20/86 ARR Slight re-organization of places where FLUSH_CACHE
59 ; is called to discard a track to make sure
60 ; TRACK_BUFFER is invalidated correctly.
61 ; 1.10 5/26/86 ARR Added Timer Int to flush cache after passage
62 ; of user setable time.
63 ; 1.20 5/27/86 ARR Additions at request of Neilk. /t:nnnnn /d /wb:on
64 ; /wb:off /wt:on /wt:off can be on device = line.
65 ; Lock cache function added.
66 ; 1.21 5/29/86 ARR Lock code made more intelligent.
67 ; 1.22 5/30/86 ARR /r reboot flush code added
68 ; 1.23 6/03/86 ARR Cache statistics added
69 ; 1.24 6/05/86 ARR Added /a "all cache" code
70 ; 1.25 6/10/86 ARR Added total used, total locked to status
71 ; 1.26 6/12/86 ARR /wb changed to /wc to align with docs. Discard
72 ; of track when write to locked track changed to
73 ; unlock. Discard of track when write with /wc:off
74 ; changed to immediate write through.
75 ; 1.27 6/17/86 ARR Bug regarding the INT 13 error which is not
76 ; an error (error 11H, ECC error corrected).
77 ; changed error handling logic to handle this
78 ; correctly (ignore it).
79 ; 1.28 7/31/86 ARR Default seg reg access byte changed from
80 ; 82H to 92H. This was needed for 80386 functionality.
81 ; Change to LOADALL.ASM, also RAMDrive problem.
82 ; 1.30 8/04/86 ARR Default cache size uped to 256K
83 ; Min cache size uped to 128K
84 ; 1.31 8/07/86 ARR Moved SMSW SIDT SGDT set into BLKMOV code for
86 ; 1.32 8/27/86 ARR Added code to A20 routine to provide approp
87 ; settle time for A20 switch to occur. This will
88 ; help us on Compaq machines and faster ATs and
89 ; 80386 machines. Thanks to CC of Compaq for fix.
90 ; 1.33 9/22/86 ARR Added more info to startup header, in particular,
91 ; tells you whether /A or /E cache.
96 ; 1.00 5/13/87 SUNILP, GREGH, DAVIDW:
97 ; Modified INT13 to take care of multi track caching
98 ; Reduced functionality
99 ; Added two new IOCTL calls to increase/decrease
100 ; cache size, dynamically
103 ;; 7/24/87 WSH Added 6300 PLUS support. This code is marked by
104 ;; the use of double semi-colons to make it easy to
108 ; New extended memory allocation scheme. 386 support.
109 ; Support for new ps/2 systems. better 286 loadall
110 ; transfer. more complete expanded memory access.
113 ; 1.01 9/17/87 SUNILP
114 ; Removed check that was limiting tracks to 32k bytes
115 ; Tracks can be upto 64k bytes now
117 ; 1.02 10/22/87 SUNILP
118 ; Reduced statically allocated track buffer size to
121 ; 1.03 10/29/87 SUNILP
122 ; Changed name reported in messages to SMARTDrive
125 ; Added support for OMTI controller. This code
126 ; is ifdef'd in with the OMTI keyword.
128 ; 1.04 3/02/88 SUNILP
129 ; fix for recognition of 20MHz model 80.
130 ; fix for READ DASD int 13 dispatch.
132 ; 1.05 5/13/88 SUNILP
133 ; fixed version checking to include dos 4.00
135 ; 2.10 6/13/88 CHIPA Merged in these changes for HP Vectra
137 ; Fixed a20 enabling/disabling problems on
139 ; 8/24/88 MRW Merged changes from Windows tree into DOS tree
147 .286p
; Use some 286 instructions in /E code
150 S_OLIVETTI EQU
1 ; Flag for olivetti 6300 plus machine
151 S_VECTRA EQU
2 ; Flag for HP Vectra machines
152 WINDOWS_SWITCHES EQU
1 ; 1 = uses switches for windows, 0 = all switches
153 ;OMTI EQU 1 ; Used for code specific to the OMTI Controller
155 MAX_HARD_FILES EQU
16 ; Max number of hardfiles our data structures support
157 MIN_CACHE_SIZE_K EQU
128 ; Minimum size for cache in K (multiple of 16)
161 %
out OMTI Controller release
167 %
out DEBUG VERSION!!!!!!
171 ;; In order to address memory above 1 MB on the AT&T 6300 PLUS, it is
172 ;; necessary to use the special OS-MERGE hardware to activate lines
173 ;; A20 to A23. However, these lines can be disabled only by resetting
174 ;; the processor. The return address offset and segment can be found
175 ;; at 40:a2, noted here as RealLoc1.
177 BiosSeg
segment at 40h
;; Used to locate 6300 PLUS reset address
182 R_Mode_IDT
segment at 0h
191 ; The INT13 device driver has 2 basic configurations.
193 ; TYPE 1 - /E configuration using PC-AT extended memory and the LOADALL
195 ;; The /U configuration using upper extended memory on the
196 ;; 6300 PLUS is a special case of the type 1 configuration.
198 ; TYPE 2 - /A configuration using Above Board memory and EMM device
201 ; The TYPE 2 driver uses the Above Board EMM device driver via INT 67H
202 ; to control access to, and to access the available memory.
204 ; The TYPE 1 configuration uses the EMM control sector to
205 ; control access to the available memory
216 BREAK <I
/O Packet offset declarations
>
219 ; Define I/O packet offsets for useful values.
222 ; MS-DOS Technical Reference manual section on Installable Device Drivers
225 ; READ/WRITE PACKET OFFSETS
226 RW_COUNT EQU
WORD PTR (SIZE SRHEAD
) + 5
227 RW_TRANS EQU
DWORD PTR (SIZE SRHEAD
) + 1
228 RW_START EQU
WORD PTR (SIZE SRHEAD
) + 7
230 ; MEDIA CHECK PACKET OFFSETS
231 MCH_RETVAL EQU
BYTE PTR (SIZE SRHEAD
) + 1
232 MCH_MEDIA EQU
BYTE PTR (SIZE SRHEAD
) + 0
234 ; BUILD BPB PACKET OFFSETS
235 BPB_BUFFER EQU
DWORD PTR (SIZE SRHEAD
) + 1
236 BPB_MEDIA EQU
BYTE PTR (SIZE SRHEAD
) + 0
237 BPB_BPB EQU
DWORD PTR (SIZE SRHEAD
) + 5
239 ; INIT PACKET OFFSETS
240 INIT_NUM EQU
BYTE PTR (SIZE SRHEAD
) + 0
241 INIT_BREAK EQU
DWORD PTR (SIZE SRHEAD
) + 1
242 INIT_BPB EQU
DWORD PTR (SIZE SRHEAD
) + 5
243 INIT_DOSDEV EQU
BYTE PTR (SIZE SRHEAD
) + 9
245 BREAK <Cache control structure
>
248 ; The cache control structure is the "management" data associated
249 ; with all of the data in the cache. The cache structures are
250 ; part of the device driver and they contain pointers to the
251 ; actual cache memory where the data is. This is more efficient
252 ; than putting the structures with the data as there is more overhead
253 ; to access stuff in the data area. The structures form a double
254 ; linked list in LRU order. The head points to the MRU element. The
255 ; tail points to the LRU element. Scans start at MRU as this is the
256 ; highest probability of a hit. Selection for bump is at LRU. All of
257 ; the links are short pointers. This limits the size of the cache
258 ; as the cache structures together with the device code must all
259 ; fit in 64K. This is more efficient than FAR links. Each cache element
260 ; contains one complete track of one of the INT 13 hard files (INT 13
261 ; floppy drives are NOT cached). Cache "read ahead" is obtained by
262 ; reading complete tracks into the cache even though the INT 13 user
263 ; may have only requested one sector. Write behind is accomplished
264 ; (if enabled) by holding tracks for a while allowing user writes
265 ; on that track to "accumulate".
267 ; The original INT 13 caching algorithm (Aaronr) is sumarized as follows:
269 ; If user read is in cache
270 ; Perform user read from cache
272 ; If read is full track
273 ; Perform write through (if enabled)
274 ; Pass Read to old INT 13 handler (no cache operation)
276 ; Read track into cache using old INT 13 handler
277 ; Perform user read out of cache
279 ; If user write is in cache
280 ; If write buffering is enabled
281 ; Perform user write into cache
283 ; Discard cache for this track
284 ; Pass write through to old INT 13 handler
286 ; If write is full track
287 ; Perform write through (if enabled)
288 ; Pass Write to old INT 13 handler (no cache operation)
290 ; If write buffering is enabled
291 ; Read track into cache using old INT 13 handler
292 ; Perform user write into cache
294 ; Pass write through to old INT 13 handler
296 ; SMARTDRV modifications:
298 ; 1. Write through always.
299 ; 2. Multi track I/O capability support.
300 ; 3. Direct transfer between cache and user buffer address for
307 FWD_LRU_LNK
DW ?
; Link to next CACHE_CONTROL, -1 if last
308 BACK_LRU_LNK
DW ?
; Link to previous CACHE_CONTROL, -1 if first
309 BASE_OFFSET
DD ?
; Offset releative to start of cache
310 ; memory of start of this track buffer
311 TRACK_FLAGS
DW ?
; Flags
313 ; NOTE: The next two bytes are refed as a word.
314 ; MOV DX,WORD PTR STRC.TRACK_DRIVE
316 ; Puts the INT 13 drive in DL, and the head in DH which is correct
319 TRACK_DRIVE
DB ?
; INT 13 drive with high bit = 0
320 TRACK_HEAD
DB ?
; INT 13 head
321 TRACK_CYLN
DW ?
; INT 13 cylinder
322 ; High byte is low byte of cylinder #
323 ; High two bits of Low byte are high
324 ; two bits of cylinder #. Other bits
325 ; are 0. This makes it easy to load the
326 ; CX register for an INT 13 for this
328 ; MOV CX,STRC.CACHE_CYLN
335 TRACK_FREE EQU
0000000000000001B ; Track buffer is free
336 TRACK_DIRTY EQU
0000000000000010B ; Track needs to be written
337 TRACK_LOCKED EQU
0000000000000100B ; Track is locked
339 BREAK <Device header
>
342 ASSUME
CS:INT13CODE
,DS:NOTHING
,ES:NOTHING
,SS:NOTHING
345 public strategy
,int13$
in,cmderr
,err$cnt
,err$exit
,devexit
346 public INT13$IOCTL_Read
,INT13$Read_St
,INT13$Write_St
,INT13$IOCTL_Write
347 public int_1C_handler
,int_13_handler
,POP_NO_PROC
,INVALIDATE_CACHE
348 public CACHE_READ
,CACHE_WRITE
,FLUSH_PASS
,FLUSH_INVALID_PASS
349 public FLUSH_WHOLE_CACHE_SAV
,FLUSH_WHOLE_CACHE
,FLUSH_CACHE
350 public WRITE_FROM_CACHE
352 public blkmov
,INT_9
,INT_19
,RESET_SYSTEM
,DO_INIT
,SETBPB
353 public PRINT
,ITOA
,INT13$INIT
,DRIVEPARMS
,GETNUM
,DISK_ABORT
354 public CTRL_IO
,MM_SETDRIVE
,FIND_VDRIVE
,SET_RESET
355 public AT_EXT_INIT
,FIX_DESCRIPTOR
,ABOVE_INIT
356 public process_read_partial
,process_block_read
,pr_acc_trks
357 public pr_acc_trks
,pr_cur_trk
,process_write_partial
,process_block_write
358 public check_parameters
,process_regions
,bytes_in_trk
,sect_in_trk
359 public not_in_mem
,read_disk
360 public not_in_memw
,rd_partw
,rd_part
367 ; INT13 DEVICE HEADER
369 ; COMMON TO TYPE 1, 2 drivers
372 ; MS-DOS Technical Reference manual section on
373 ; Installable Device Drivers
376 ;; The internal name of the device driver has been changed from SMARTDRV
377 ;; to SMARTAAR to avoid DOS name conflicts with files named SMARTDRV.*
381 DEVATS
DW DEVOPCL
+ CharDev
+ DevIOCtl
384 DB "SMARTAAR" ;Name of device
387 BREAK <Command dispatch table
>
391 ; This is the device driver command dispatch table.
393 ; The first byte indicates the size of the table and therefore defines
394 ; which device function codes are valid.
396 ; The entries in the table are NEAR word addresses of the appropriate
397 ; device routine. Thus the address of the routine to handle device function
399 ; WORD at ((INT13TBL + 1) + (2 * 3))
401 ; COMMON TO TYPE 1, 2 drivers
406 DB 15 ; Max allowed command code
408 DW CMDERR
; Media check
409 DW CMDERR
; Build BPB
410 DW INT13$IOCTL_Read
; IOCTL input
412 DW CMDERR
; Non-des read no-wait
413 DW INT13$Read_St
; Read status
414 DW CMDERR
; Read flush
416 DW CMDERR
; Write with verify
417 DW INT13$Write_St
; Output status
418 DW CMDERR
; Output flush
419 DW INT13$IOCTL_Write
; IOCTL output
422 DW CMDERR
; Rem media?
424 BREAK <Device Control
data>
426 STATISTICS_SIZE EQU
40
428 DRIVER_SEL
DB 0 ; 0 if /E (TYPE 1), 1 if /A (TYPE 2),
430 DEV_SIZE
DW 256 ; Size in K of the cache
432 SECTRACK
DW ?
; Sectors per track
434 current_dev_size dw ?
; Current size in K of cache
436 ;; Data peculiar to AT&T 6300 PLUS.
438 S5_FLAG
DB 0 ;; = S_OLIVETTI if 6300 plus machine
439 ;; = S_VECTRA if HP Vectra machine
447 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
448 ; Unfortunately the code in smartdrv is very machine dependent
449 ; necessitating the use of a system flag to store the machine
450 ; configuration. The system flag is initialised during init time
451 ; and used when the caching services are requested. One bit which
452 ; is set and tested during caching is the state of the a20 line
453 ; when the cache code is entered. This is used because there are
454 ; applications which enable the a20 line and leave it enabled
455 ; throughout the duration of execution. Since smartdrv is a device
456 ; driver it shouldn't change the state of the environment.
458 ; The system flag bit assignments are:
460 ; -------------------------------------------------
461 ; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
462 ; -------------------------------------------------
463 ; |-----|-----| | | | | |
464 ; | | | | | -----286 (and AT)
465 ; | | | | -----------386 (later than B0)
466 ; not | | -----------------PS/2 machine
467 ; used | -----------------------Olivetti (not used)
468 ; -----------------------------A20 state (enabled ?)
470 ; The Olivetti guys have defined a flag of their own. This should be removed
471 ; and the bit assigned out here for them should be used.
475 ; equates used for the system flag
483 OMTI_EXT equ
00100000B
486 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
487 ; flag to indicate that reset code is being executed
490 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
491 ; emm ctrl address, needed for the reset code
492 emm_ctrl_addr dw EXTMEM_LOW
497 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
498 ; A20 address line state determination addresses
508 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
514 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
518 cs_des desc
<0FFFFh,0,0,09Fh,0,0>
519 ss_des desc
<0FFFFh,0,0,093h,0,0>
520 ds_des desc
<0FFFFh,0,0,093h,0,0>
521 es_des desc
<0FFFFh,0,0,093h,0,0>
524 emm_gdt gdt_descriptor
<end_gdt
-start_gdt
,0,0>
529 desc
<> ;dummy descriptor
530 desc
<> ;descriptor for gdt itself
531 src desc
<0ffffh,,,93h
,,>
532 tgt desc
<0ffffh,,,93h
,,>
533 desc
<> ;bios cs descriptor
534 desc
<> ;stack segment descriptor
536 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
537 ; 0 if device is valid
538 ; Non-0 if device install failed (device non-functional)
540 ; We need this state because there is no way to "un-install"
541 ; a character device as there is with block devices
546 ; 0 if caching is off
547 ; Non-0 if caching is on
549 ENABLE_13
DB 1 ; 0 for debug
552 ; 0 if no write through
553 ; Non-0 if write through
558 ; 0 if no write buffering
559 ; Non-0 if write buffering enabled
564 ; 0 if cache is unlocked
565 ; Non-0 if cache is locked
570 ; 0 if full track I/O to tracks not in cache is not cached.
571 ; Non-0 if ALL I/O is to be cached
576 ; 0 if reboot flush is disabled
577 ; Non-0 if reboot flush is enabled
582 ; An exclusion sem so that the INT 13 handler and the timer interact
583 ; without re-entrancy problems
585 INT_13_BUSY
DB 0 ; Exclusion sem
587 EVEN
; Force word data to word align
589 ; Statistics counters
591 ; WARNING!!!! Do not disturb the order of these!!!! See IOCTL_READ code.
597 TTRACKS
DW ?
; Total number of track buffers that fit
598 ; in DEV_SIZE K (number of cache elements)
606 TICK_SETTING
DW 1092 ; Approx 1 minute
610 ; Non-zero if there are dirty buffers in the cache
612 DIRTY_CACHE
DW 0 ; 0 if no dirty elements in cache
614 BREAK <Common Device
code>
616 ; INT13 DEVICE ENTRY POINTS - STRATEGY, INT13$IN
618 ; This code is standard DOS device driver function dispatch
619 ; code. STRATEGY is the device driver strategy routine, INT13$IN
620 ; is the driver interrupt routine.
622 ; INT13$IN uses INT13TBL to dispatch to the appropriate handler
623 ; for each device function. It also does standard packet
627 ; MS-DOS Technical Reference manual section on
628 ; Installable Device Drivers
631 ASSUME
CS:INT13CODE
,DS:NOTHING
,ES:NOTHING
,SS:NOTHING
633 PTRSAV
DD 0 ; Storage location for packet addr
635 ;** STRATEGY - Device strategy routine
637 ; Standard DOS 2.X 3.X device driver strategy routine. All it does
638 ; is save the packet address in PTRSAV.
640 ; ENTRY ES:BX -> Device packet
644 ; COMMON TO TYPE 1, 2 drivers
651 MOV WORD PTR [PTRSAV
],BX ; Save packet addr
652 MOV WORD PTR [PTRSAV
+2],ES
657 ;** INT13$IN - Device interrupt routine
659 ; Standard DOS 2.X 3.X device driver interrupt routine.
662 ; ENTRY PTRSAV has packet address saved by previous STRATEGY call.
663 ; EXIT Dispatch to appropriate function handler
664 ; CX = Packet RW_COUNT
665 ; DX = Packet RW_START
666 ; ES:DI = Packet RW_TRANS
668 ; STACK has saved values of all regs but FLAGS
669 ; All function handlers must return through one of
670 ; the standard exit points
673 ; COMMON TO TYPE 1, 2 drivers
688 LDS BX,[PTRSAV
] ;GET POINTER TO I/O PACKET
690 ; Set up registers for READ or WRITE since this is the most common case
692 MOV CX,DS:[BX.RW_COUNT
] ;CX = COUNT
693 MOV DX,DS:[BX.RW_START
] ;DX = START SECTOR
694 MOV AL,DS:[BX.REQFUNC
] ; Command code
695 MOV AH,BYTE PTR [INT13TBL
] ; Valid range
697 JA CMDERR
; Out of range command code
698 MOV SI,OFFSET INT13TBL
+ 1 ; Table of routines
699 CBW ; Make command code a word
700 ADD SI,AX ; Add it twice since one word in
701 ADD SI,AX ; table per command.
703 LES DI,DS:[BX.RW_TRANS
] ; ES:DI transfer address
710 JMP WORD PTR [SI] ; GO DO COMMAND
712 ;** EXIT - ALL ROUTINES RETURN THROUGH ONE OF THESE PATHS
714 ; Exit code entry points:
717 ; MS-DOS Technical Reference manual section on
718 ; Installable Device Drivers
720 ; GENERAL ENTRY for all entry points
721 ; All packet values appropriate to the specific device function
722 ; filled in except for the status word in the static request
725 ; CMDERR - Used when an invalid device command is detected
727 ; ENTRY Stack has frame set up by INT13$IN
728 ; EXIT Standard Device driver with error 3
731 ; ERR$CNT - Used when READ or WRITE wants to return with error code.
732 ; The packet RW_COUNT field is zeroed
734 ; ENTRY AL is error code for low byte of packet status word
735 ; Stack has frame set up by INT13$IN
736 ; EXIT Standard Device driver with error AL
739 ; ERR$EXIT - Used when a function other that READ or WRITE wants to
742 ; ENTRY AL is error code for low byte of packet status word
743 ; Stack has frame set up by INT13$IN
744 ; EXIT Standard Device driver with error AL
747 ; DEVEXIT - Used when a function wants to return with no error
749 ; ENTRY AL is value for low byte of packet status word
750 ; NOTE: Typically there is no meaningful value
751 ; in the AL register when EXITing through here.
752 ; This is OK as the low 8 bits of the status word
753 ; have no meaning unless an error occured.
754 ; Stack has frame set up by INT13$IN
755 ; EXIT Standard Device driver with no error
758 ; ERR1 - Used when a function wants to return with a value
759 ; for the whole status word
761 ; ENTRY AX is value for packet status word
762 ; Stack has frame set up by INT13$IN
763 ; EXIT Standard Device driver with or without error
766 ; COMMON TO TYPE 1, 2 drivers
770 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
773 MOV AL,3 ;UNKNOWN COMMAND ERROR
778 MOV [BX.RW_COUNT
],0 ; NO sectors transferred
779 ERR$EXIT
: ; Error in AL
780 MOV AH,(STERR
+ STDON
) SHR 8 ;MARK ERROR RETURN
789 MOV [BX.REQSTAT
],AX ; Set return status
800 RET ;RESTORE REGS AND RETURN
804 ; The following functions are not supported at this time.
807 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
809 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
812 BREAK <IOCTL Read function
(get device control parms
)>
818 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
820 CMP CX,STATISTICS_SIZE
; Must have room to transfer data.
821 JB SET_ZRJ3
; Not enough room from user
826 ; Count all occupied, dirty and locked elements
833 TEST [SI.TRACK_FLAGS
],TRACK_FREE
836 TEST [SI.TRACK_FLAGS
],TRACK_LOCKED
840 TEST [SI.TRACK_FLAGS
],TRACK_DIRTY
844 MOV SI,[SI.FWD_LRU_LNK
]
848 MOV AL,[WRITE_THROUGH
]
854 MOV AX,[TICK_SETTING
]
857 MOV AH,[REBOOT_FLUSH
]
860 XOR AH,AH ; Unused currently
862 MOV SI,OFFSET TOTAL_WRITES
866 ; Transfer Above Board Information
872 cmp [driver_sel
],dl ; is it expanded memory?
873 jz no_ems
; no, info already set
875 mov ax,[current_dev_size
]
890 mov ax,MIN_CACHE_SIZE_K
/ 16
895 MOV [BX.RW_COUNT
],STATISTICS_SIZE
; transfer amount
898 BREAK <IOCTL Write functions
(set device control parms
and do flushes
)>
901 ; Command table for IOCTL Write functions. The first byte of the written
902 ; data contains the "function" code which is dispatched via this
903 ; table. The first byte is the maximum legal function code, then the word
904 ; addresses of the handlers for each function.
907 DW IOCTL_FLUSH
; Function 0h
908 DW IOCTL_FLUSH_INVALID
; Function 1h
909 DW IOCTL_DISABLE
; Function 2h
910 DW IOCTL_ENABLE
; Function 3h
911 DW IOCTL_WRITE_MOD
; Function 4h
912 DW IOCTL_SET_TICK
; Function 5h
913 DW IOCTL_LOCK
; Function 6h
914 DW IOCTL_UNLOCK
; Function 7h
915 DW IOCTL_REBOOT
; Function 8h
916 DW IOCTL_STAT_RESET
; Function 9h
917 DW IOCTL_ALL_CACHE
; Function Ah
918 dw ioctl_reduce_cache_size
; Function Bh
919 dw ioctl_increase_cache_size
; Function Ch
922 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
925 MOV [BX.RW_COUNT
],0 ; NO bytes transferred
929 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
930 MOV AL,3 ;UNKNOWN COMMAND ERROR
934 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
935 CMP [NULDEV
],0 ; Is the device valid?
936 JNZ SET_ZR
; No, you get an error
937 MOV AL,ES:[DI] ; Get command byte
938 MOV AH,BYTE PTR [IOCTLTBL
] ; Valid range
939 CMP AL,AH ; In range?
941 MOV SI,OFFSET IOCTLTBL
+ 1 ; Table of routines
942 CBW ; Make command code a word
943 ADD SI,AX ; Add it twice since one word in
944 ADD SI,AX ; table per command.
945 JMP WORD PTR [SI] ; GO DO COMMAND
947 ;** IOCTL_FLUSH -- Flush the cache, but keep the data
950 ; ES:DI is transfer address
951 ; CX is transfer count
953 ; Through one of the device exit paths
958 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
960 CALL FLUSH_WHOLE_CACHE
964 ;** IOCTL_FLUSH_INVALIDATE -- Flush the cache, and discard the data
967 ; ES:DI is transfer address
968 ; CX is transfer count
970 ; Through one of the device exit paths
975 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
977 CALL FLUSH_WHOLE_CACHE
978 CALL INVALIDATE_CACHE
982 ;** IOCTL_DISABLE -- Disable the caching for both reads and writes
984 ; Also flush and invalidate the cache
987 ; ES:DI is transfer address
988 ; CX is transfer count
990 ; Through one of the device exit paths
995 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
997 CALL FLUSH_WHOLE_CACHE
998 CALL INVALIDATE_CACHE
1003 ;** IOCTL_ENABLE -- Enable the caching for reads (and possibly writes)
1006 ; ES:DI is transfer address
1007 ; CX is transfer count
1009 ; Through one of the device exit paths
1014 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1018 ;** IOCTL_WRITE_MOD -- En/Disable Write through and write caching
1021 ; ES:DI is transfer address
1022 ; CX is transfer count
1023 ; Second byte of data indicates what to set
1024 ; 0 Turn off Write through
1025 ; 1 Turn on Write through
1026 ; 2 Turn off Write buffering (also flush)
1027 ; 3 Turn on Write buffering (also flush)
1029 ; Through one of the device exit paths
1034 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1035 CMP CX,2 ; Did user write enough?
1036 JB SET_ZR
; No, error
1037 MOV AL,ES:[DI.1] ; Get second byte
1038 CMP AL,3 ; In range?
1039 JA SET_ZR
; No, error
1040 CMP AL,2 ; WT or WB?
1041 JB SET_WRITE_TH
; WT
1043 DEC AL ; 2 = 0, 3 = 1
1046 CALL FLUSH_WHOLE_CACHE
1051 MOV [WRITE_THROUGH
],AL
1054 ;** IOCTL_SET_TICK -- Set tick count for auto flush
1057 ; ES:DI is transfer address
1058 ; CX is transfer count
1059 ; Second byte and third byte of data is value to set
1061 ; Through one of the device exit paths
1066 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1067 CMP CX,3 ; Did user write enough?
1068 JB SET_ZRJ
; No, error
1069 MOV AX,ES:[DI.1] ; Get second byte and third byte as word
1070 MOV [TICK_SETTING
],AX
1076 ;** IOCTL_LOCK -- Lock the current cache
1078 ; Also flush the cache
1081 ; ES:DI is transfer address
1082 ; CX is transfer count
1084 ; Through one of the device exit paths
1089 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1091 CALL FLUSH_WHOLE_CACHE
1094 ; Lock all cache elements that have something in them
1100 TEST [SI.TRACK_FLAGS
],TRACK_FREE
1102 OR [SI.TRACK_FLAGS
],TRACK_LOCKED
1104 MOV SI,[SI.FWD_LRU_LNK
]
1110 ;** IOCTL_UNLOCK -- Unlock the cache
1113 ; ES:DI is transfer address
1114 ; CX is transfer count
1116 ; Through one of the device exit paths
1121 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1125 ; UnLock all cache elements
1131 AND [SI.TRACK_FLAGS
],NOT TRACK_LOCKED
1132 MOV SI,[SI.FWD_LRU_LNK
]
1138 ;** IOCTL_REBOOT -- En/Disable Reboot flush
1141 ; ES:DI is transfer address
1142 ; CX is transfer count
1143 ; Second byte of data indicates what to set
1144 ; 0 Turn off reboot flush
1145 ; 1 Turn on reboot flush
1147 ; Through one of the device exit paths
1152 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1153 CMP CX,2 ; Did user write enough?
1154 JB SET_ZRJ
; No, error
1155 MOV AL,ES:[DI.1] ; Get second byte
1156 CMP AL,1 ; In range?
1157 JA SET_ZRJ
; No, error
1158 MOV [REBOOT_FLUSH
],AL
1162 ;** IOCTL_STAT_RESET -- Reset the INT 13 statistics counters
1165 ; ES:DI is transfer address
1166 ; CX is transfer count
1168 ; Through one of the device exit paths
1173 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1175 MOV WORD PTR [TOTAL_WRITES
],AX
1176 MOV WORD PTR [TOTAL_WRITES
+ 2],AX
1177 MOV WORD PTR [WRITE_HITS
],AX
1178 MOV WORD PTR [WRITE_HITS
+ 2],AX
1179 MOV WORD PTR [TOTAL_READS
],AX
1180 MOV WORD PTR [TOTAL_READS
+ 2],AX
1181 MOV WORD PTR [READ_HITS
],AX
1182 MOV WORD PTR [READ_HITS
+ 2],AX
1185 ;** IOCTL_ALL_CACHE -- En/Disable All cache
1188 ; ES:DI is transfer address
1189 ; CX is transfer count
1190 ; Second byte of data indicates what to set
1191 ; 0 Turn off all cache
1192 ; 1 Turn on all cache
1194 ; Through one of the device exit paths
1199 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1200 CMP CX,2 ; Did user write enough?
1201 JB SET_ZRJ2
; No, error
1202 MOV AL,ES:[DI.1] ; Get second byte
1203 CMP AL,1 ; In range?
1204 JA SET_ZRJ2
; No, error
1211 ;** ioctl_reduce_cache_size Dynamically reduce the size of the cache
1213 ; This routine dynamically reduces the size of an Above Board memory (/A)
1214 ; cache. The routine is passed the number of pages that the cache should
1215 ; be reduced by. The minimum size for a cache is 64K or 4 pages. In
1216 ; removing the tracks from the cache, the memory is returned to the EMM
1217 ; and then the cache control structures for that memory are taken off the
1218 ; LRU list, and set to free.
1220 ; NOTE: In this version of INT13, only reads are cached, so that when
1221 ; a cached track is "removed" from memory, it's contents are not
1225 ; ES:DI is transfer address
1227 ; Through one of the device exit paths
1232 jmp error_reduce_exit
1234 ioctl_reduce_cache_size:
1239 cmp byte ptr [driver_sel
],1 ; Make sure using Above Board
1240 jnz error_reduce_exitj
1241 mov ax,es:[di.1] ; Get second byte
1242 or ax,ax ; Reduce by a non-0 amount?
1245 do_it: mov cl,4 ; Multiply by 16 to get # of K bytes
1246 shl ax,cl ; AX has requested reduction in K
1247 mov bx,word ptr [current_dev_size
]
1248 mov si,bx ; Save current_dev_size in SI for error
1249 sub bx,ax ; BX now has remaining cache memory in K
1250 cmp bx,MIN_CACHE_SIZE_K
; Compare BX with min cache in K bytes
1251 jl error_reduce_exitj
1252 mov word ptr [current_dev_size
],bx
1253 shr bx,cl ; BX has new cache size in pages
1254 mov byte ptr [int_13_busy
],1
1256 ; Request Reallocation of Pages from EMM
1259 mov dx,word ptr [above_pid
]
1260 mov ah,ABOVE_REALLOCATE_PID
1265 ; Determine how many cache tracks will be lost
1267 mov bx,word ptr [sectrack
] ; Init code checked for too large track
1269 shl bx,cl ; BX has bytes/track
1270 mov ax,word ptr [ttracks
]
1271 mul bx ; AX:DX has bytes used by tracks
1275 mul cx ; AX:DX has total bytes allocated
1276 sub ax,si ; Find difference in allocated and used
1280 pop ax ; AX still has requested reduction in K
1281 mul cx ; DX:AX has requested reduction in byte
1284 ; Make sure the we have to free up tracks in order to satisfy the request
1285 ; HACK! HACK! HACK! Smartdrv should only allocate as many pages as it needs.
1290 jb no_need_to_free_tracks
1293 no_need_to_free_tracks:
1299 sub ax,si ; Account for part not used
1301 div bx ; AX has number of tracks being "lost"
1304 inc ax ; Add one more track if remainder
1306 ; Determine which cache control structures we are to remove
1310 mov cx,SIZE CACHE_CONTROL
1312 xchg ax,bx ; BX has backward offset into CCS's
1313 mov ax,word ptr [ttracks
]
1314 sub word ptr [ttracks
],si
1315 mul cx ; AX has end of Cache Control
1316 sub ax,bx ; AX has start offset of CCS's to remove
1317 mov cx,si ; CX has number of CCS's to remove
1318 mov si,word ptr [cache_control_ptr
]
1319 add si,ax ; SI points to first CCS to modify
1321 ; Loop through each cache control structure, removing from LRU list
1323 remove_cache_entries:
1324 mov word ptr [si].track_flags
,TRACK_FREE
1326 add si,SIZE CACHE_CONTROL
1327 loop remove_cache_entries
1330 mov byte ptr [int_13_busy
],0
1335 mov [current_dev_size
],si ; Restore former dev size
1336 mov byte ptr [int_13_busy
],0
1338 mov al,0Ch ; General failure
1342 ;** ioctl_increase_cache_size Dynamically increase the size of the cache
1344 ; This routine dynamically increases the size of an Above Board memory (/A)
1345 ; cache. The routine is passed the number of pages that the cache should
1346 ; be increased by. The maximum size allowed is the size specified from the
1347 ; INT13.SYS command line. The cache control structures for the memory added
1348 ; to the cache are placed on the LRU list at the LRU position, so that they
1349 ; will be immediately available for incoming tracks.
1352 ; ES:DI is transfer address
1354 ; Through one of the device exit paths
1358 error_increase_exitj:
1359 jmp error_increase_exit
1361 ioctl_increase_cache_size:
1366 cmp [driver_sel
],1 ; Make sure using Above Board
1367 jnz error_increase_exitj
1368 mov ax,es:[di.1] ; Get second byte
1369 mov cl,4 ; Multiply by 16 to get # of K bytes
1370 shl ax,cl ; AX has requested addition in K
1371 mov bx,word ptr [current_dev_size
]
1372 mov si,bx ; Save current dev size for error
1373 add bx,ax ; BX now has new cache memory size in K
1374 cmp bx,[dev_size
] ; Compare BX with largest size in K bytes
1375 jbe increase_size_ok
1376 mov bx, [dev_size
] ; Go to MAX size
1378 sub ax, si ; Correct increase
1380 mov word ptr [current_dev_size
],bx
1381 shr bx,cl ; BX has new cache size in pages
1384 ; Request Reallocation of Pages from EMM
1388 mov ah,ABOVE_REALLOCATE_PID
1393 ; Determine how many cache tracks will be gained
1395 mov bx,word ptr [sectrack
] ; Init code checked for too large track
1397 shl bx,cl ; BX has bytes/track
1398 mov ax,word ptr [ttracks
]
1399 mul bx ; AX:DX has bytes used by tracks
1403 mul cx ; AX:DX has total bytes allocated
1404 sub ax,si ; Find difference in allocated and used
1408 pop ax ; AX still has requested reduction in K
1409 mul cx ; DX:AX has requested reduction in byte
1410 add ax,si ; Account for part not used
1412 div bx ; AX has number of tracks being "gained"
1413 or ax, ax ; DIV trashes flags
1416 ; Determine which cache control structures we are to add
1419 mov cx,SIZE CACHE_CONTROL
1421 add [ttracks
],si ; Update TTRACKS
1422 mul cx ; AX has end of Cache Control
1423 mov cx,si ; CX has number of CCS's to add
1424 mov si,[cache_control_ptr
]
1425 add si,ax ; SI points to first CCS to modify
1427 ; Loop through each cache control structure, adding to LRU list
1430 mov di,si ; Place element at LRU position
1431 xchg di,[cache_tail
]
1432 mov [di].fwd_lru_lnk
,si
1433 mov [si].back_lru_lnk
,di
1434 mov [si].fwd_lru_lnk
,-1
1435 mov [si].track_flags
,TRACK_FREE
1437 add si,SIZE CACHE_CONTROL
1438 loop add_cache_entries
1445 mov [current_dev_size
],si ; Restore to original size
1447 error_increase_exit:
1448 mov al,0Ch ; General failure
1453 ; If the device errors out during install, we set the break address here.
1455 ERROR_END
LABEL BYTE
1457 BREAK <INT 1C
(timer
) handler
>
1459 EVEN
; Force word align
1461 ; Storage for the INT 1C vector BEFORE cache installed
1465 ;** INT_1C_HANDLER - Handler for INT 1C timer ticks
1471 ; To next 1C handler, EOI may be sent
1477 ; IBM PC TECH REF MANUAL section on INT 1C
1478 ; DOS PRINT utility (most of this is stolen from there)
1480 INT_1C_HANDLER PROC
FAR
1481 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
1488 INC [TICK_CNT
] ; Set it back to 1 so next tick triggers
1494 mov al,00001011b ; Select ISR in 8259
1500 in al,20H
; Get ISR register
1501 and al,0FEH ; Mask timer int
1502 jnz RE_TRIGGER
; Another int is in progress
1503 INC [INT_13_BUSY
] ; Exclude
1504 CMP [DIRTY_CACHE
],0 ; Anything to do?
1509 CALL FLUSH_WHOLE_CACHE_SAV
1511 MOV AX,[TICK_SETTING
]
1519 BREAK <INT 13 handler
>
1522 ; INT 13 stack frame
1525 USER_OFF
DW ?
; added for user transfer address
1541 EVEN
; Force word align
1543 ; Storage for the INT 13 vector BEFORE cache installed
1548 ; Array of sec/track for all hardfiles on system. First element cooresponds
1549 ; to first hard file.
1550 ; Value = 0 indicates no hardfile
1552 SECTRKARRAY
DB MAX_HARD_FILES
DUP (0)
1554 ; ARRAY OF MAXIMUM USEABLE HEADS FOR ALL THE HARDFILES IN THE SYSTEM. FIRST
1555 ; ELEMENT CORRESPONDS TO FIRST HARDFILE.
1556 ; VALUE = FF INDICATES NO HARDFILE (SUNILP)
1558 HDARRAY
DB MAX_HARD_FILES
DUP (0FFH)
1561 OMTI_SET_CYL EQU
0EEh
1562 OMTI_GET_CYL EQU
0FEh
1563 OMTI_GET_REV EQU
0F9h
1566 ; INT 13 function dispatch
1567 ; Addresses of routines for AH INT 13 functions
1569 INT13DISPATCH
LABEL WORD
1570 DW POP_NO_PROC
; 0, reset
1571 DW POP_NO_PROC
; 1, Read status
1572 DW CACHE_READ
; 2, read
1573 DW CACHE_WRITE
; 3, write
1574 DW POP_NO_PROC
; 4, verify
1575 DW INVALID_PASS
; 5, format
1576 DW INVALID_PASS
; 6, format
1577 DW INVALID_PASS
; 7, format
1578 DW POP_NO_PROC
; 8, drive parms
1579 DW INVALID_PASS
; 9, Init drive characteristic
1580 DW INVALID_PASS
; A, Read long
1581 DW INVALID_PASS
; B, Write long
1582 DW POP_NO_PROC
; C, Seek
1583 DW POP_NO_PROC
; D, Alt reset
1584 DW INVALID_PASS
; E, Read buffer
1585 DW INVALID_PASS
; F, Write buffer
1586 DW POP_NO_PROC
; 10, Test drive rdy
1587 DW POP_NO_PROC
; 11, Recalibrate
1588 DW POP_NO_PROC
; 12, Controller diag
1589 DW INVALID_PASS
; 13, Drive diag
1590 DW POP_NO_PROC
; 14, Controller diag internal
1591 DW POP_NO_PROC
; 15, READ DASD
1592 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1594 ; MODIFICATIONS TO DATA STRUCT TO SUPPORT MULTI-TRACK I/O
1598 ; extra declarations needed
1600 max_hd db ?
;maximum useable head number for curr. drive
1601 sect_in_trk db ?
;maximum sector number in trk for curr drive
1602 bytes_in_trk dw ?
;numb of bytes in trk for current drive
1612 START_H db ?
;start head
1613 START_T dw ?
;start track
1614 COUNT dw ?
;number of words
1615 START_S dw ?
;start sector
1621 TRACKS db ?
;number of tracks
1627 dw ?
;number of words
1632 REG1 db size REG1t
dup(?
)
1633 REG2 db size REG2t
dup(?
)
1634 REG3 db size REG3t
dup(?
)
1638 REGION db size REGIONt
dup(?
)
1640 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1642 ; INT_13_HANDLER - Handler for INT 13 requests
1645 ; All regs as for INT 13
1648 ; To old INT 13 handler with regs unchanged if cache not involved
1649 ; Else return in AH and flags as per INT 13.
1652 ; AH and carry bit of FLAGS
1655 ; IBM PC TECH REF MANUAL section on INT 13
1657 INT_13_HANDLER PROC
FAR
1658 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
1659 MOV [INT_13_BUSY
],1 ; Exclude
1660 TEST DL,80H
; Hard file?
1665 CALL [OLD_13
] ; Let old handler do it
1666 MOV [INT_13_BUSY
],0 ; Clear sem
1667 RET 2 ; "IRET" chucking flags
1670 ; Pop off the stack frame and pass to old handler
1680 POP BX ; Dummy for pop sp
1691 ; Set up standard stack frame
1698 PUSH BX ; dummy for push sp
1709 XOR BH,BH ; Command in BX
1713 CMP [ENABLE_13
],0 ; Are we enabled?
1714 JZ POP_NO_PROC
; No, ignore
1718 ; The following code is used to handle the extended cylinder access method
1719 ; used by the OMTI controller. This controller has a modified INT13 routine
1720 ; that uses a unique function number to tell the controller that the next
1721 ; access to INT13 is for an extended cylinder.
1723 test sys_flg
,OMTI_EXT
; Are we in extended state?
1724 jnz in_extended_state
1725 cmp bx,OMTI_SET_CYL
; Is this the OMTI extended function?
1726 jnz check_command_range
1731 ; If we are in the extended state, we want to make sure that this call
1732 ; does not go through into the cache. Therefore, we will check to see
1733 ; if this is a read or write function, and will return to the old INT13
1736 and sys_flg
,NOT OMTI_EXT
; Clear the extended flag
1741 check_command_range:
1743 ; Allow the other OMTI functions to pass through
1751 CMP BX,15H
; Command in range?
1752 JA INVALID_PASS
; No, throw out cache
1753 SHL BX,1 ; Times two bytes per table entry
1754 JMP [BX.INT13DISPATCH
] ; Dispatch to handler
1756 ;** FLUSH_INVALID_PASS -- Discard cache and pass through
1759 ; INT 13 regs except for BP,BX and DS
1761 ; To old INT13 handler
1764 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1765 CALL FLUSH_WHOLE_CACHE
1766 CALL INVALIDATE_CACHE
1769 ;** INVALID_PASS -- DISCARD CACHE, NO NEED TO FLUSH, PASS THROUGH
1774 ; INT 13 regs except for BP,BX and DS
1776 ; To old INT 13 handler
1779 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1780 CALL INVALIDATE_CACHE
1783 ;** FLUSH_PASS -- Flush cache (but retain data) and pass through
1786 ; INT 13 regs except for BP,BX and DS
1788 ; To old INT13 handler
1791 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1792 CALL FLUSH_WHOLE_CACHE
1795 BREAK <CACHE_READ
-- Read via cache
>
1797 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
1801 ; inputs: int13 regs except for bp - user stack frame
1802 ; bx - function (read,write)
1803 ; ds - int13code segment
1805 ; exits: to user success routine
1806 ; to user error routine
1807 ; to old int13 handler call
1811 ; written by: sunil pai
1813 call check_parameters
; check user params
1814 jc pop_no_proc
; if error go to old int 13 handler
1816 call process_regions
; for multi-track business find the
1817 ; initial partial track, middle block
1818 ; and final partial track
1819 ; sets up REGION struct
1821 ; mov [int13err],FALSE ; clear int 13 error flag
1823 test [REGION
.FLAG
],REG1_P
; is region1 present
1824 je cr$2
; if not present try region2
1826 mov dh,[REGION
.REG1
.START_H
] ;start head
1827 mov cx,[REGION
.REG1
.START_T
] ;get start track
1828 mov bx,[REGION
.REG1
.START_S
] ;start sector
1829 mov ax,[REGION
.REG1
.COUNT
] ;number of words
1830 call process_read_partial
;
1831 jc error
;go to process error
1833 cr$2
: test [REGION
.FLAG
],REG2_P
; is region2 present
1834 je cr$3
; if not present try region3
1836 mov dh,[REGION
.REG2
.START_H
] ; get start head
1837 mov cx,[REGION
.REG2
.START_T
] ; get start track
1838 mov al,[REGION
.REG2
.TRACKS
] ; get number of tracks
1839 call process_block_read
; multi track capability
1842 cr$3
: test [REGION
.FLAG
],REG3_P
; is region3 present
1843 je suc
; if not we are done
1845 mov dh,[REGION
.REG3
.START_H
] ; start head
1846 mov cx,[REGION
.REG3
.START_T
] ; start track
1847 mov bx,1 ; start sector is 1
1848 mov ax,[REGION
.REG3
.COUNT
] ; number of words
1849 call process_read_partial
;
1850 jc error
; just as we were about to fin!
1852 ; exit points - suc and error.
1854 suc: and [bp.USER_FL
],NOT f_Carry
;
1855 mov byte ptr [bp.USER_AX
.1],0 ;
1858 mov cs:[int_13_busy
],0 ;clear semaphore
1878 ; the next two instructions were removed because of the way Compaq
1879 ; handles bad sectors. they mark sectors bad not tracks. so in a
1880 ; track there may be good and bad sectors. However our int13 caching
1881 ; system does i/o from disk in tracks and will not get any track with
1882 ; bad sectors. to take care of this, we pass the read to the old int13
1883 ; handler even when there is an int 13 error
1885 ; test [int13err],TRUE
1886 ; jne er$1 ;if not int13 error go to call
1889 ;er$1: or [bp.USER_FL],f_Carry ;
1890 ; mov byte ptr [bp.USER_AX.1],AL ;int13 error code
1891 ; jmp operation_done
1894 call invalidate_cache
1899 ; inputs: int13 regs except for bp - user stack frame
1900 ; bx - function (read,write)
1901 ; ds - int13code segment
1905 ; written by: sunil pai
1907 call check_parameters
;check user params
1908 jc pop_no_procw
;error
1910 call process_regions
;for multi-track business find the
1911 ;initial partial track, middle block
1912 ;and final partial track
1913 ;sets up REGION struct
1915 ; writes always update disk, i.e., write through always operational
1917 mov ax,[bp.user_ax
] ; restore int13 registers
1918 mov cx,[bp.user_cx
] ;
1919 mov dx,[bp.user_dx
] ;
1920 mov bx,[bp.user_bx
] ;
1921 mov es,[bp.user_es
] ;
1922 pushf ; since interrupt routine being called
1924 call [old_13
] ; call old int 13 handler
1925 jnc cw$1
; no error in writing to disk, continue
1927 or [bp.user_fl
],f_Carry
; error then set carry
1928 mov byte ptr [bp.user_ax
.1],ah ; and error code
1929 jmp short errorw
; and take error exit
1931 ; int 13 was successful, now we have to update cache as well
1933 cw$1
: and [bp.user_fl
],not f_Carry
; int 13 success
1934 mov byte ptr [bp.user_ax
.1],0 ;
1938 test [REGION
.FLAG
],REG1_P
; is region1 present
1939 je cw$2
; if not present try region2
1941 mov dh,[REGION
.REG1
.START_H
] ; get start head
1942 mov cx,[REGION
.REG1
.START_T
] ; get start track
1943 mov bx,[REGION
.REG1
.START_S
] ; start sector
1944 mov ax,[REGION
.REG1
.COUNT
] ; number of words
1945 call process_write_partial
; partial write
1946 jc errorw
; go to process error
1948 cw$2
: test [REGION
.FLAG
],REG2_P
; is region2 present
1949 je cw$3
; if not present try region3
1951 mov dh,[REGION
.REG2
.START_H
] ; get start head
1952 mov cx,[REGION
.REG2
.START_T
] ; get start track
1953 mov al,[REGION
.REG2
.TRACKS
] ; get number of tracks
1954 call process_block_write
;
1957 cw$3
: test [REGION
.FLAG
],REG3_P
; is region3 present
1958 je operation_donew
; if not we are done
1960 mov dh,[REGION
.REG3
.START_H
] ; start head
1961 mov cx,[REGION
.REG3
.START_T
] ; start track
1962 mov bx,1 ; start sector is 1
1963 mov ax,[REGION
.REG3
.COUNT
] ; number of words
1964 call process_write_partial
;
1965 jnc operation_donew
; no error - finish
1967 errorw: call invalidate_cache
; we are not sure of the
1974 process_read_partial proc
near
1976 ; is used to read a partial track
1978 ; inputs: dx: head and drive (8th bit stripped)
1981 ; ax: number of words
1983 ; outputs:cy set if error
1987 ; if (track in cache) then {
1988 ; if (track in track_buffer) then
1989 ; perform user read from track buffer;
1991 ; perform user read from cache;
1994 ; read track into track buffer;
1995 ; read track buffer into cache;
1996 ; perform user read from track buffer;
1999 ; cache transfers handled by freeing cache element if possible
2002 ; uses: only dx assumed unchanged
2004 call track_in_cache
; is track in cache
2005 jc read_disk
; if not we have to read from disk
2007 cmp cx,[track_buffer_cyln
] ; is it in the track buffer
2009 cmp dx,[track_buffer_hddr
] ;
2010 jz read_bufferj
; yes
2012 ; read buffer from cache
2016 xchg ax,bx ;get number of words in bx and start sect in ax
2017 dec ax ;0 based number
2019 shl ax,cl ;byte offset
2022 mov cx,bx ;number of words
2023 mov es,[bp.user_es
] ;
2024 mov di,[bp.user_off
];
2025 shl bx,1 ; number of bytes
2026 add [bp.user_off
],bx ; update user offset
2028 add ax,word ptr [si.base_offset
]
2029 adc dx,word ptr [si.base_offset
.2]
2043 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2045 ; track not in cache. see if free cache available to initiate transfer
2048 cmp di,-1 ;free cache
2049 jnz rd_part
;if present we can initiate read
2050 stc ; else error exit
2052 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2055 jmp short read_buffer
2057 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2059 ; cache element available. we can start by transferring track from disk
2063 mov si,di ;get element in si
2064 mov [track_buffer_cyln
],-1 ;invalidate present track buffer
2065 mov [track_buffer_hddr
],-1
2066 mov [si.track_flags
],track_free
;if read fails
2073 mov al,[sect_in_trk
]
2077 mov bx,[track_buffer_ptr
]
2082 cmp ah,11h
;the int13 error which is not an error
2090 ; transfer track buffer to cache
2093 mov di,[track_buffer_ptr
] ;es:di is transfer address
2094 mov cx,[bytes_in_trk
]
2095 shr cx,1 ; cx is number words in track
2097 mov ax,word ptr [si.base_offset
] ;address of cache
2098 mov dx,word ptr [si.base_offset
.2] ;
2112 ; error in transferring info to cache. invalidate cache element and
2116 mov [si.track_flags
],track_free
2117 call cache_is_lru
; make cache element lru
2121 ; info transfer to cache successful. fill in track, head and drive
2122 ; info into cache control element and track buffer control element
2128 mov word ptr [si.track_cyln
],cx
2129 mov word ptr [si.track_drive
],dx
2130 and [si.track_flags
], not track_free
2131 mov word ptr [track_buffer_cyln
],cx
2132 mov word ptr [track_buffer_hddr
],dx
2134 ; perform user i/o from track buffer
2139 mov si,bx ; start sector
2140 dec si ; 0 based number
2142 shl si,cl ; byte offset in si
2143 add si,[track_buffer_ptr
]
2146 mov di,[bp.user_off
]
2149 mov [bp.user_off
],di
2153 process_read_partial endp
2155 process_block_read proc
near
2157 ; inputs: ax = number of tracks
2158 ; dx = drive and head (8th bit stripped)
2160 ; bp = user stack frame
2162 ; outputs: cy set if error
2168 ; if (cur_trk in cache) then
2169 ; transfer track from cache to user buffer;
2170 ; no_of_trks = no_of_trks - 1
2172 ; accumulate tracks which are not in cache
2173 ; read these in one disk operation
2174 ; transfer these from user buffer to cache
2175 ; no_of_trks = no_of_trks - accumulated_trks
2176 ; until no_of_trks == 0
2180 ; since we are going to look ahead and see how many tracks can
2181 ; be dealt with together, we have to save start head and track
2183 push cx ; save start head
2184 push dx ; save start track
2185 xor ah,ah ;number of accumulated tracks
2186 pbr$2
: call track_in_cache
;
2187 jnc pbr$4
; if current track in cache start processing
2188 inc dh ; go to next track
2190 inc ah ;accumulate the tracks
2192 jne pbr$2
;go to see next track
2193 pop dx ;restore start head and track
2195 call pr_acc_trks
;process the accumulated tracks
2198 pop dx ;restore start head and track
2200 or ah,ah ;are there any accumulated
2202 call pr_acc_trks
;process the accumulated tracks
2203 jc pbr$7
;if carry set finish with error
2204 add dh,ah ;adjust track and head
2208 pbr$5
: call pr_cur_trk
;process current track which is in cache
2209 jc pbr$7
;if carry set finish with error
2213 dec al ;are we done?
2218 process_block_read endp
2220 pr_acc_trks proc
near
2222 ; inputs: cx = start track
2224 ; ah = number of accumulated tracks
2226 ; outputs: if success :- cy clear, [bp.user_off] modified
2227 ; if failure :- cy set
2229 ; regs to be preserved: al,cx,si
2233 ; read buffer from disk;
2234 ; for (cur_trk=start_trk,transfer_off=user_off; no_of_trks-- > 0;
2235 ; transfer_off=transfer_off+size_of_trk) do
2236 ; if ((cache=get_cache())!=-1) then
2237 ; transfer_trk_to_cache;
2245 push ax ; stack:- {ax,cx,dx,si}
2247 ; initialise int13 registers for reading the accumulated tracks into
2250 mov al,ah ;number of tracks
2253 mul [sect_in_trk
] ;get number of sectors in ax
2256 or cx,1 ;start sector
2258 or dl,80h
;set hard disk bit
2259 mov bx,[bp.user_off
];
2260 mov es,[bp.user_es
] ;
2264 call [old_13
] ;perform multi track read
2266 ; check for int 13 error
2268 jnc pat$1
;if okay proceed
2270 add sp,8 ;clear stack
2272 mov [int13err
],TRUE
;
2276 ; we have succesfully read al tracks into user memory. now these have
2277 ; to transfer these to cache.
2279 pat$1
: and dl,not 80h
;clear off 8th bit
2285 mov di,[bp.user_off
];initialise transfer offset
2287 ; ah has number of tracks still left to be transferred
2288 ; di has transfer offset
2289 ; cx has current track number
2290 ; dx has current drive and head
2293 call get_cache
;get free cache element
2295 ; si = cache element
2297 cmp si,-1 ;was there an element
2298 je pat$5
;cache saturated exit
2300 ; check if track is in track buffer
2302 cmp cx,[track_buffer_cyln
]
2304 cmp dx,[track_buffer_hddr
]
2307 ; track is in track buffer. to avoid two transfers invalidate
2310 mov [track_buffer_cyln
],-1
2311 mov [track_buffer_hddr
],-1
2313 ; transfer track from user buffer to cache
2315 pat$21
: mov [si.track_flags
],track_free
;if write fails
2325 mov cx,[bytes_in_trk
]
2327 mov ax,word ptr [si.base_offset
]
2328 mov dx,word ptr [si.base_offset
.2]
2341 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2342 ; error in transferring information to cache. make it lru
2349 ret ;cache error exit
2350 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2352 ; successfully transferred information to cache. fill in track and
2353 ; drive and head information into cache control element
2355 pat$3
: mov word ptr [si.track_cyln
],cx
2356 mov word ptr [si.track_drive
],dx
2357 and [si.track_flags
],not track_free
2361 add di,[bytes_in_trk
] ;advance pointer in user transfer buffer to
2364 inc dh ;advance track
2367 pat$4
: dec ah ;have we processed all the accumulated trks
2369 mov [bp.user_off
],di ;update user transfer address to beyond
2370 ;this accumulated block
2380 pr_cur_trk proc
near
2383 ; si = cache element
2384 ; cx = current track
2385 ; dx = drive and head
2389 ; = clear if success
2392 ; transfer track from cache to user memory
2401 mov cx,[bytes_in_trk
]
2403 mov ax,word ptr [si.base_offset
]
2404 mov dx,word ptr [si.base_offset
.2]
2406 mov di,[bp.user_off
]
2419 mov [si.track_flags
],track_free
2424 pct$1
: call cache_is_mru
2425 mov di,[bytes_in_trk
]
2426 add [bp.user_off
],di
2432 process_write_partial proc
near
2434 ; is used to write a partial track
2436 ; inputs: dx: int13 dx with high bit of dl set off
2439 ; ax: number of words
2441 ; outputs:cy set if error
2445 ; if (track in cache) then {
2446 ; if (track in track_buffer) then
2447 ; invalidate track buffer;
2448 ; perform user write into cache;
2451 ; read track into track buffer;
2452 ; write track buffer into cache;
2456 call track_in_cache
;is track in cache
2457 jc read_diskw
;if not we have to read from disk
2459 cmp cx,[track_buffer_cyln
] ;is it in the track buffer
2461 cmp dx,[track_buffer_hddr
] ;
2465 mov [track_buffer_cyln
],-1 ;invalidate trk buf
2466 mov [track_buffer_hddr
],-1 ;to avoid two transfers
2468 ; update cache element from user buffer
2472 xchg ax,bx ;get number of words in bx and start sect in ax
2473 dec ax ;0 based number
2475 shl ax,cl ;byte offset
2478 mov cx,bx ;number of words
2479 mov es,[bp.user_es
] ;
2480 mov di,[bp.user_off
];
2482 add [bp.user_off
],bx
2484 add ax,word ptr [si.base_offset
]
2485 adc dx,word ptr [si.base_offset
.2]
2502 err_cw: mov [si.track_flags
],track_free
2507 ; track not in cache. see if free cache element available
2510 cmp di,-1 ;free cache
2511 jnz rd_partw
;if present we can initiate read
2515 ; free cache element available. read track from disk into track buffer
2518 mov si,di ;get element in si
2519 mov [track_buffer_cyln
],-1
2520 mov [track_buffer_hddr
],-1
2521 mov [si.track_flags
],track_free
2528 mov al,[sect_in_trk
]
2532 mov bx,[track_buffer_ptr
]
2537 cmp ah,11h
;the int13 error which is not an error
2545 ; since we have already updated disk, the track read doesn't need
2546 ; to be updated from user buffer. transfer track from track buffer
2549 wr$1
: mov di,[track_buffer_ptr
] ;es:di is transfer address
2550 mov al,[sect_in_trk
]
2556 mov ax,word ptr [si.base_offset
]
2557 mov dx,word ptr [si.base_offset
.2]
2572 ; information successfully transferred to cache. fill in cache
2573 ; control element with the info on head, drive and track
2576 mov word ptr [si.track_cyln
],cx
2577 mov word ptr [si.track_drive
],dx
2578 and [si.track_flags
], not track_free
2579 mov word ptr [track_buffer_cyln
],cx
2580 mov word ptr [track_buffer_hddr
],dx
2585 add [bp.user_off
],ax
2589 process_write_partial endp
2592 process_block_write proc
near
2594 ; al = number of tracks
2596 ; dx = drive and start head
2597 ; bp = user stack frame
2600 ; cy clear if success
2602 ; algorithm: for (cur_trk=st_trk; trks-- > 0; )
2603 ; if (cur_trk in cache) then
2604 ; write cur_trk from user buffer into cache;
2606 ; get a free cache element;
2607 ; write cur_trk from user buffer into this cache elem
2610 mov di,[bp.user_off
]
2612 pbw$1
: push di ;save it because next call destroys di
2613 call track_in_cache
;is the track in cache
2616 cmp di,-1 ;no free cache
2617 je pbw$4
;error exit
2619 mov si,di ;track was not in cache. now there will be one
2620 mov [si.track_flags
],track_free
2621 mov word ptr [si.track_cyln
],cx
2622 mov word ptr [si.track_drive
],dx
2626 ; check if it is in track buffer also in which case invalidate trk buffer
2628 cmp cx,[track_buffer_cyln
]
2630 cmp dx,[track_buffer_hddr
]
2633 ; invalidate track buffer to avoid two transfers
2635 mov [track_buffer_cyln
],-1
2636 mov [track_buffer_hddr
],-1
2638 ; update cache from user buffer
2649 mov cx,[bytes_in_trk
]
2651 mov ax,word ptr [si.base_offset
]
2652 mov dx,word ptr [si.base_offset
.2]
2667 pbw$22
: and [si.track_flags
],not track_free
2669 inc dh ;go to next track
2671 pbw$3
: add di,[bytes_in_trk
]
2674 dec al ; are we done
2678 mov [bp.user_off
],di
2688 process_block_write endp
2690 check_parameters proc
near
2692 ; inputs: same as int13 registers except for
2693 ; BP - user_stack_frame
2694 ; BX - function (either read or write)
2695 ; DS - int13code segment
2697 ; outputs: carry set if params invalid
2698 ; carry clear if params okay
2700 ; if parameters okay :
2701 ; dl - drive with high bit off
2702 ; bx - sector number
2703 ; cl - cleared of the sector number
2706 ; check for number of drives
2708 and dl, not 80H
; turn off high bit of drive
2709 cmp dl,MAX_HARD_FILES
; more than allowed drives?
2710 jae bad_parmx
; error.
2712 ; check for number of sectors
2714 or al,al ; zero sectors ?
2715 je bad_parmx
; error.
2717 cmp al,80h
; more than 80h sectors ?
2718 ja bad_parmx
; error.
2720 ; check for wrap in user transfer address
2722 mov di,[bp.user_bx
] ; es:di is transfer address
2723 xor ah,ah ; ax has number of sectors
2724 mov si,ax ; get it into si
2727 shl si,cl ; convert number of sectors into
2729 dec si ; convert into zero based number
2731 add di,si ; add to transfer address offset
2732 jc bad_parmx
; if exceeds 64k offset then wrap
2734 ; get drive number into di to use as offset into sect / trk table
2736 mov di,dx ; drive number
2737 and di,0000000011111111B ; just get the relevant bits
2739 ; form sector number in bx and compare against allowed number of sectors
2740 ; in track on the drive indicated
2743 and bx,0000000000111111B ; bl will then have sector number
2744 or bx,bx ; zero sector number
2745 je bad_parmx
; is bad
2746 cmp bl, [di.sectrkarray
] ; is it more than number of sectors
2750 ; check head parameter
2752 cmp dh,[di.hdarray
] ; is it more than max useable val for drive
2755 ; do we need some code to check if read exceeds tracks in system ?
2756 ; should we also put a limit on the number of sectors that could
2757 ; be looked up in cache ?
2760 ; clear off sector number from cl to leave just trk number in cx
2762 and cl,11000000B ; clear off sector number
2764 ; store sectors in track for current drive and bytes in track for
2765 ; current drive in memory
2770 mov [max_hd
],bl ; maximum head number for cur. drive
2771 mov bl,[di.sectrkarray
] ; number of sectors in trk
2772 mov [sect_in_trk
],bl ; store this
2774 shl bx,cl ; bytes in track
2775 mov [bytes_in_trk
],bx ; store this
2779 ret ;return with no error
2785 check_parameters endp
2787 process_regions proc
near
2789 ; inputs: bx - start sector
2790 ; al - number of sectors
2795 ; action: initialise regions struct
2797 mov byte ptr [REGION
.FLAG
],0 ; clear regions flag
2798 cmp bx,1 ; if start sector is one
2799 je pr$2
; might as well start with region2
2803 or [REGION
.FLAG
],REG1_P
; mark region1 present
2804 mov ah,[sect_in_trk
] ; get sectors in track
2805 sub ah,bl ; remaining number of sectors in track
2806 inc ah ; adjust 0 based numb to one based numb
2808 jbe pr$1
; if below or equal ah has number of sectors
2810 mov ah,al ; else all the sectors are in region1
2811 pr$1
: sub al,ah ; adjust number of sectors
2812 mov [REGION
.REG1
.START_H
],dh
2813 mov [REGION
.REG1
.START_T
],cx
2814 mov [REGION
.REG1
.START_S
],bx
2815 push cx ; save these registers
2818 xor ah,ah ; ax has number of sectors now
2820 shl ax,cl ; multiply by 256 to get number of words
2821 mov [REGION
.REG1
.COUNT
],ax ; store this count
2829 pr$2
: or al,al ; are we done
2831 xor ah,ah ; ax has number of sectors
2832 div [sect_in_trk
] ; find number of tracks
2833 or al,al ; al will have number of full tracks
2834 ; ah will have number of sectors left
2835 je pr$3
; if no full tracks no region2
2837 or [REGION
.FLAG
],REG2_P
; mark region2 present
2838 mov [REGION
.REG2
.START_H
],dh
2839 mov [REGION
.REG2
.START_T
],cx; store start track
2840 mov [REGION
.REG2
.TRACKS
],al ; and number of tracks
2841 add dh,al ; adjust track number
2846 pr$3
: or ah,ah ; are we done (no sectors left)
2847 je pr$
end ; if yes go to fin
2848 or [REGION
.FLAG
],REG3_P
; else mark region3 present
2849 mov [REGION
.REG3
.START_H
],dh
2850 mov [REGION
.REG3
.START_T
],cx ;store track number
2853 xor ah,ah ; convert number of sectors into number of
2855 mov [REGION
.REG3
.COUNT
],ax ; store this
2860 process_regions endp
2865 track_in_cache proc
near
2867 ; input: dl = drive number with bit 8 set off
2868 ; cx = cylinder number
2870 mov di,-1 ; di will return lru item nearest to matching
2871 ; element, -1 if none
2872 mov si,[cache_head
] ; start with mru cache entry
2873 inc si ; to counter next instruction
2875 dec si ; to counter last instruction in the loop
2876 test [si.track_flags
],track_locked
; is the track locked?
2878 mov di,si ; if not locked update di to this element
2880 test [si.track_flags
],track_free
; is element free
2881 jnz skipe
; if free we need not check this one
2882 cmp dx,word ptr [si.track_drive
] ; if not free check drive+head
2884 cmp cx,[si.track_cyln
] ; and cylinder number
2885 jz tic$1
; if found exit routine
2887 mov si,[si.fwd_lru_lnk
] ; else go to check next cache element
2888 inc si ; if last element was end then si = -1
2889 jnz nexte
; and incrementing it will set zero flag
2900 ; outputs: si = lru cache element not locked
2901 ; = -1 if all elems locked
2903 mov si,[cache_tail
] ;start with lru element
2904 inc si ;to counter next instruction
2906 dec si ; to counter last instruction in loop
2907 test [si.track_flags
],track_locked
; is the element locked
2908 jz gc$2
; if not locked this is the lucky(?) guy
2910 mov si,[si.back_lru_lnk
] ; else go back in chain to check the
2911 ; next recently used cache element
2912 inc si ; as before -1 is the end of chain
2913 jnz gc$1
; incrementing it will set zero flag
2915 dec si ; no element found, si = -1
2921 adj_hd_trk proc
near
2923 ; inputs: ch,cl track number
2924 ; dh changed head number to be checked and adjusted
2926 ; outputs: cx and dh updated
2929 aht$1
: cmp dh,[max_hd
] ;is the head number > heads on drive
2931 sub dh,[max_hd
] ;if so decrease head number by number of
2934 add ch,1 ;and step to next track
2944 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
2948 ;** INVALIDATE_CACHE -- Discard all cache info
2951 ; Cache is flushed (If it is not, all dirty info will simply be chucked)
2953 ; All elements of cache are marked free
2957 INVALIDATE_CACHE PROC
NEAR
2958 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
2960 INC BX ; Counter next instruction
2963 MOV [BX.TRACK_FLAGS
],TRACK_FREE
2964 MOV BX,[BX.FWD_LRU_LNK
]
2968 ; Track buffer invalid too
2970 MOV [TRACK_BUFFER_CYLN
],-1
2971 MOV [TRACK_BUFFER_HDDR
],-1
2975 INVALIDATE_CACHE ENDP
2977 ;** CACHE_IS_MRU -- Put cache element in LRU chain at MRU position
2980 ; SI points cache element to place at MRU position
2982 ; SI is at MRU position (head)
2986 CACHE_IS_MRU PROC
NEAR
2987 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
2993 XCHG DI,[CACHE_HEAD
]
2994 MOV [DI.BACK_LRU_LNK
],SI
2995 MOV [SI.FWD_LRU_LNK
],DI
2996 MOV [SI.BACK_LRU_LNK
],-1
3002 ;** CACHE_IS_LRU -- Put cache element in LRU chain at LRU position
3005 ; SI points to cache element to place at LRU position
3007 ; SI is at LRU position (tail)
3011 CACHE_IS_LRU PROC
NEAR
3012 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
3018 XCHG DI,[CACHE_TAIL
]
3019 MOV [DI.FWD_LRU_LNK
],SI
3020 MOV [SI.BACK_LRU_LNK
],DI
3021 MOV [SI.FWD_LRU_LNK
],-1
3027 ;** UNLINK_CACHE -- Unlink cache element from LRU chain
3030 ; SI points to element to unlink
3036 UNLINK_CACHE PROC
NEAR
3037 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
3039 MOV DI,[SI.BACK_LRU_LNK
] ; Get prev guy
3043 MOV BX,[SI.FWD_LRU_LNK
] ; Get next guy
3044 MOV [DI.FWD_LRU_LNK
],BX ; Prev fwd is my fwd
3045 INC BX ; Is that guy last?
3048 MOV [BX.BACK_LRU_LNK
],DI ; Next back is my back
3054 MOV DI,[SI.FWD_LRU_LNK
] ; Is head also tail?
3058 MOV [CACHE_HEAD
],DI ; New head
3059 MOV [DI.BACK_LRU_LNK
],-1 ; New head has no back link
3064 MOV [CACHE_TAIL
],DI ; New tail
3071 ;** WRITE_FROM_CACHE -- Write out cache element to disk
3074 ; SI -> cache element to write
3078 ; Track buffer is set to this track
3080 ; Error, AL is error code
3081 ; Track buffer is set to empty
3083 ; AX,BX,CX,DX,ES,DI,FLAGS
3085 WRITE_FROM_CACHE PROC
NEAR
3086 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
3088 ; First transfer track down to track buffer because we can't write
3089 ; direct to extended or expanded mem with INT 13
3093 MOV DI,[TRACK_BUFFER_PTR
] ; ES:DI is transfer addr for BLKMOV
3094 MOV BX,WORD PTR [SI.TRACK_DRIVE
]
3096 MOV AL,[BX.SECTRKARRAY
]
3100 SHL AX,CL ; AX is words in track
3103 MOV AX,WORD PTR [SI.BASE_OFFSET
]
3104 MOV DX,WORD PTR [SI.BASE_OFFSET
.2] ; DX:AX is address in mem
3108 CALL BLKMOV
; Track buffer contents to track buffer
3113 POP AX ; AL is sec/trk
3115 ; Now write it out to drive from the track buffer
3117 MOV CX,[SI.TRACK_CYLN
]
3118 MOV DX,WORD PTR [SI.TRACK_DRIVE
]
3119 MOV [TRACK_BUFFER_CYLN
],CX ; Set track buffer currency
3120 MOV [TRACK_BUFFER_HDDR
],DX
3126 MOV BX,[TRACK_BUFFER_PTR
]
3131 CALL [OLD_13
] ; Write it out
3139 CMP AH,11H
; The error that is not an error?
3140 JZ NO_ERR1
; Yes, cmp cleared carry
3141 PUSH AX ; Save error in AH
3144 POP AX ; Get error back
3146 JZ SET_ERR
; Return error
3147 POP AX ; Recover correct AX for INT 13
3152 MOV AL,AH ; INT 13 error to AL
3156 MOV [TRACK_BUFFER_CYLN
],-1 ; Zap the track buffer
3157 MOV [TRACK_BUFFER_HDDR
],-1
3160 WRITE_FROM_CACHE ENDP
3162 ;** FLUSH_CACHE -- Flush specific cache element if it's dirty
3165 ; SI points to element to flush
3168 ; SI is flushed if it was dirty
3169 ; SI.TRACK_FLAGS dirty bit clear
3170 ; Track buffer set to this track
3172 ; SI could not be flushed
3173 ; SI.TRACK_FLAGS = free
3175 ; Track buffer set to empty
3177 ; AX,BX,CX,DX,ES,DI,FLAGS
3179 FLUSH_CACHE PROC
NEAR
3180 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
3181 TEST [SI.TRACK_FLAGS
],TRACK_FREE
; Clears carry
3183 TEST [SI.TRACK_FLAGS
],TRACK_DIRTY
; Clears carry
3185 CALL WRITE_FROM_CACHE
3186 DEC [DIRTY_CACHE
] ; Doesn't effect carry
3188 AND [SI.TRACK_FLAGS
],NOT TRACK_DIRTY
; Clears carry
3193 MOV [SI.TRACK_FLAGS
],TRACK_FREE
; Track gone, unlocked
3198 ;** FLUSH_WHOLE_CACHE_SAV -- Flush all dirty cache elements saving regs
3207 FLUSH_WHOLE_CACHE_SAV PROC
NEAR
3208 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
3214 PUSH BX ; dummy for push sp
3224 CALL FLUSH_WHOLE_CACHE
3231 POP BX ; Dummy for pop sp
3238 FLUSH_WHOLE_CACHE_SAV ENDP
3240 ;** FLUSH_WHOLE_CACHE -- Flush all dirty cache elements
3247 ; AX,BX,CX,DX,ES,SI,DI,FLAGS
3249 FLUSH_WHOLE_CACHE PROC
NEAR
3250 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
3252 INC SI ; Counter next instruction
3256 MOV SI,[SI.FWD_LRU_LNK
]
3260 MOV [DIRTY_CACHE
],0 ; No dirty guys in cache
3263 FLUSH_WHOLE_CACHE ENDP
3265 BREAK <Drive
code for
/E driver
>
3268 ; The following label defines the start of the I/O code which is driver type
3271 ; THE TYPE 2 driver must REPLACE this code with code appropriate
3272 ; to the driver type.
3274 EVEN
; Force start of drive code to word boundary
3276 DRIVE_CODE
LABEL WORD
3278 EXTMEM_LOW EQU
0000H ; 24 bit addr of start of extended memory
3279 EXTMEM_HIGH EQU
0010H
3281 ;** BASE_ADDR data element
3283 ; The next value defines the 24 bit address of the start of the memory for
3284 ; the cache. It is equal to the EMM_BASE value in the
3285 ; EMM_REC structure for the cache.
3287 ; NOTE THAT IT IS INITIALIZED TO THE START OF EXTENDED MEMORY. This is
3288 ; because BLKMOV is used to read the EMM_CTRL sector during initialization
3289 ; of a TYPE 1 driver.
3291 ; NOTE: This data element is shared by TYPE 1, 2 drivers, but
3292 ; its meaning and correct initial value are driver type specific.
3295 ;; NOTE: The value at BASE_ADDR is patched during initialization when
3296 ;; loading a RAMDrive into upper extended memory on a PLUS
3298 BASE_ADDR
LABEL DWORD ; 24 bit address of start of this RAMDRV
3301 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3302 ;** BLKMOV - Perform transfer for TYPE 1 driver
3304 ; This routine is the transfer routine for moving bytes
3305 ; to and from the AT extended memory in real mode using
3306 ; the LOADALL instruction. The LOADALL instruction is used
3307 ; to set up a segment descriptor which has a 24 bit address.
3308 ; During the time the LOADALL 24 bit segment descriptor is
3309 ; in effect we must have interrupts disabled. If a real mode
3310 ; 8086 interrupt handler was given control it might perform
3311 ; a segment register operation which would destroy the special
3312 ; segment descriptor set up by LOADALL. This is prevented by
3313 ; doing a CLI during the "LOADALL time".
3315 ; WARNING NUMBER ONE:
3316 ; THIS CODE WILL NOT WORK ON ANY 80286 MACHINE WHERE THE NMI
3317 ; INTERRUPT IS ANYTHING BUT A FATAL, SYSTEM HALTING ERROR.
3319 ; Since it is bad to leave interrupts disabled for a long
3320 ; time, the I/O is performed 256 words at a time enabling
3321 ; interrupts between each 256 word piece. This keeps the time
3322 ; interrupts are disabled down to a reasonable figure in the 100mSec
3325 ; To use the LOADALL instruction 102 bytes at location 80:0 must
3326 ; be used. INT13 copies the contents of 80:0 into its own buffer,
3327 ; copies in the LOADALL info, performs the LOADALL, and then copies
3328 ; back the previous contents of 80:0. These operations are all
3329 ; performed during the time interrupts are disabled for each 256 word
3330 ; block. This must be done with interrupts disabled because this area
3331 ; on DOS 2.X and 3.X contains variable BIOS data.
3333 ; In order to gain full 24 bit addressing it is also required
3334 ; that address line 20 be enabled. This effects 8086 compatibility
3335 ; on 80286 systems. This code leaves address line 20 enabled
3336 ; for the ENTIRE duration of the I/O because it is too time
3337 ; expensive to disable/enable it for each 256 word block.
3339 ; WARNING NUMBER TWO:
3340 ; IF A MULTITASKING PRE-EMPTIVE SYSTEM SCHEDULES AND RUNS
3341 ; AN APPLICATION WHICH RELIES ON THE 1 MEG ADDRESS WRAP
3342 ; PROPERTY OF THE 8086 AND 8088 DURING THE TIME INT13
3343 ; IS IN THE MIDDLE OF DOING AN I/O WITH ADDRESS LINE 20 ENABLED,
3344 ; THE APPLICATION WILL NOT RUN PROPERLY AND MAY DESTRUCT THE
3348 ; Perform various LOADALL setup operations
3349 ; Enable address line 20
3350 ; While there is I/O to perform
3351 ; Do "per 256 word block" LOADALL setup operations
3352 ; Set up copy of 80:0 to INT13 buffer
3354 ; copy 80:0 to INT13 buffer
3355 ; copy LOADALL info to 80:0
3357 ; do 256 word transfer
3358 ; copy INT13 80:0 buffer back to 80:0
3360 ; Disable address line 20
3363 ; INTEL special documentation of LOADALL instruction
3366 ; ES:DI is packet transfer address.
3367 ; CX is number of words to transfer.
3368 ; DX:AX is 32 bit start byte offset (0 = start of cache)
3369 ; BH is 1 for WRITE, 0 for READ
3371 ; BASE_ADDR set to point to start of cache memory
3372 ; This "input" is not the responsibility of the caller. It
3373 ; is up to the initialization code to set it up when the
3374 ; device is installed
3378 ; OK, operation performed successfully
3380 ; Error during operation, AL is error number (INT 13 error)
3385 ; This routine is specific to TYPE 1 driver
3387 ; sunilp - incorporated blkmov_386 (thanks to gregh)
3388 ; incorporated loadall_286 trick (thanks to scottra)
3389 ; added new a20 functionality
3390 ; ideally the code should be all relocatable abd the 386
3391 ; blkmov should be relocated on the 286 blkmov for the
3392 ; 386 case. Also the A20 routines for the Olivetti or PS/2
3393 ; should also ideally be relocated on top of the normal A20
3396 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
3397 test [sys_flg
],M_386
3401 ; Compute 32 bit address of start of I/O
3404 ADD AX,WORD PTR [BASE_ADDR
]
3405 ADC DX,WORD PTR [BASE_ADDR
+ 2]
3407 ; Dispatch on function
3414 MOV WORD PTR [ESDES
.SEG_BASE
],AX
3415 MOV BYTE PTR [ESDES
.SEG_BASE
+ 2],DL
3423 MOV WORD PTR [DSDES
.SEG_BASE
],AX
3424 MOV BYTE PTR [DSDES
.SEG_BASE
+ 2],DL
3431 CALL SEG_SET
; Set ES or DS segreg
3433 ; Set stack descriptor
3440 ; SUB [LSP],2 ; CX is on stack at LOADALL
3442 ; the loadall kludge
3447 mov si,offset CSDES
;sp
3451 ; Set Other LOADALL stuff
3454 SIDT FWORD PTR [IDTDES
]
3455 SGDT FWORD PTR [GDTDES
]
3457 ; NOW The damn SXXX instructions store the desriptors in a
3458 ; different order than LOADALL wants
3460 MOV SI,OFFSET IDTDES
3462 MOV SI,OFFSET GDTDES
3465 ; Enable address line 20
3469 ;; Enable address line 20 on the PC AT or activate A20-A23 on the 6300 PLUS.
3470 ;; The former can be done by placing 0dfh in AH and activating the keyboard
3471 ;; processor. On the PLUS, 90h goes in AL and the port at 03f20h is written.
3472 ;; So the combined value of 0df90h can be used for both machines with
3473 ;; appropriate coding of the called routine A20.
3477 mov ax,cs:[A20On
] ;; set up for PLUS or AT
3480 ; JMP SHORT IO_START ;sp
3481 jmp short move_main_loop
;sp
3484 MOV AL,0AAH ; Drive not ready error
3492 assume
ds:nothing
;sp
3500 MOV DI,OFFSET
cs:[SWAP_80
]
3504 CLI ; Un interruptable
3505 REP MOVSW ; Save contents of 80:0
3511 MOV SI,OFFSET
cs:LOADALL_TBL
3513 REP MOVSW ; Transfer in LOADALL info
3514 DW 050FH ; LOADALL INSTRUCTION
3516 ; set up stack for moving 80:0 information back again
3520 mov si,offset
cs:[swap_80
]
3523 lods word ptr cs:[si]
3538 mov ax,offset io_done
3542 db 16 dup (0fah) ; bugfix sunilp
3546 mov ax,offset resume_int
3555 ; REP MOVSW ; Move data
3558 ; MOV WORD PTR [LCX],256 ; ASSUME full block
3561 ; ADD [LCX],CX ; OOPs, partial block
3562 ; XOR CX,CX ; This is the last block
3568 MOV CX,800H
; Retry this many times
3572 ;; Reset of line A20 on the PC AT requires writing 0ddh to the keyboard
3573 ;; processor. On the PLUS, the appropriate value is 00.
3577 mov ax,cs:[A20Off
] ;; setup for PLUS or AT. ah for IBM, al for PLUS
3578 CALL A20
; Disable address line 20
3585 ;** A20 - ENABLE/DISABLE ADDRESS LINE 20 ON IBM PC-AT
3587 ; This routine enables/disables address line 20 by twiddling bits
3588 ; in one of the keyboard controller registers.
3591 ; IBM Technical Reference Personal Computer AT Manual #1502243
3595 ; AH = 0DDH to disable A20
3596 ; AH = 0DFH to enable A20
3603 ; WARNING If this routine is called in a CLI state this routine has
3604 ; the side effect of enabling interrupts.
3606 ; This routine is specific to TYPE 1 driver
3610 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
3611 ;; CS override needed on S5_FLAG to avoid phase errors on
3612 ;; forward declaration of this variable.
3613 cmp cs:[S5_FLAG
],S_OLIVETTI
;; test for 6300 PLUS
3614 jne test_vec
;; yes, do this code
3617 cmp cs:[S5_FLAG
],S_VECTRA
3621 test cs:[sys_flg
],M_PS2
; is it a ps2 machine
3622 jne a20ps2
; if yes it has separate a20 routine
3625 call check_a20
; check to see if it can be enb /disb
3626 jc a20suc
; no it may not be toggled
3638 ; We must wait for the a20 line to settle down, which (on an AT)
3639 ; may not happen until up to 20 usec after the 8042 has accepted
3640 ; the command. We make use of the fact that the 8042 will not
3641 ; accept another command until it is finished with the last one.
3642 ; The 0FFh command does a NULL 'Pulse Output Port'. Total execution
3643 ; time is on the order of 30 usec, easily satisfying the IBM 8042
3644 ; settling requirement. (Thanks, CW!)
3646 mov al,0FFh ;* Pulse Output Port (pulse no lines)
3647 out 64H
,al ;* send cmd to 8042
3648 CALL E_8042
;* wait for 8042 to accept cmd
3658 ; Helper routine for A20. It waits for the keyboard controller to be "ready".
3661 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
3670 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3671 ; A20 status checking. If request is to enable a20 we must check to
3672 ; see if it is already enabled. If so we just set the sys_flg to
3673 ; indicate this. On disabling the routine checks to see if disabling
3677 assume
ds:nothing
,es:nothing
,ss:nothing
3678 cmp ah,0ddh ; is it a disable operation
3679 jne check_a20_enable
3681 ; check if a20 disabling allowed
3683 test cs:[sys_flg
],a20_st
3688 ; a20 enabling, check if allowed
3691 and cs:[sys_flg
], not A20_ST
3708 or cs:[sys_flg
],A20_ST
3717 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3718 ; A20 routine for PS2s. The PS2 A20 hardware has shifted and toggling
3719 ; a bit in the system port is all that is required.
3721 assume
ds:nothing
,es:nothing
,ss:nothing
3724 ; first separate disable operation from enable operation
3731 and cs:[sys_flg
],not A20_ST
3732 in al,PS2_PORTA
; input a20 status
3733 test al,GATE_A20
; is the a20 line set
3735 or cs:[sys_flg
],A20_ST
; indicate that it was already set
3744 out PS2_PORTA
,al ; set it
3746 in al,PS2_PORTA
; read status again
3758 test cs:[sys_flg
],A20_ST
3782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3783 ;;* VECA20 - Address enable/disable routine for Vectra family computers
3785 ;; This routine does the same function as A20 for Vectra machines.
3786 ;; Vectra machines require writing single byte as opposed to
3787 ;; double byte commands to the 8041. This is due to a bug
3788 ;; in older versions in the Vectra 8041 controllers. IBM
3789 ;; machines must use double byte commands due to lack of
3790 ;; implementation of single byte commands in some of their machines.
3793 ;; Has same results as A20
3801 mov al,ah ;sigle byte command is code passed
3805 ; See A20 for a description of the following code. It simply makes
3806 ; sure that the previous command has been completed. We cannot
3807 ; pulse the command reg since there is a bug in some Vectra 8041s
3808 ; instead we write the byte again knowing that when this one is
3809 ; accepted the previous one has been processed.
3824 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3825 ;;* A20S5 - Address enable/disable routine for the 6300 PLUS.
3827 ;; This routine enables lines A20-A23 on the PLUS by writing
3828 ;; to port 03f20h. Bit 7 turns the lines on, and bit 4 sets
3829 ;; the power-up bit. To disable the lines, the processor
3830 ;; must be reset. This is done by saving the world and
3831 ;; jumping to the ROM 80286 reset code. Since the power-up bit
3832 ;; is set, the data segment is set to the BiosSeg at 40h
3833 ;; and a jump is then made to the address at RealLoc1.
3834 ;; At RealLoc1, one can find the CS:IP where the code
3838 ;; Returns with zero flag set.
3840 A20S5: test [reboot_flg
],0ffh ;; sunilp
3841 jne a20s5boot
;; sunilp
3843 or al,al ;; if zero, then resetting processor
3845 call RSet
;; must return with entry value of ax
3847 push dx ;; set/reset port
3851 clc ;; sunilp modification cy flag now important
3855 ;;* a20S5BOOT - This code bypasses the processor reset on a reboot
3856 ;; of the 6300 PLUS. Otherwise the machine hangs.
3857 a20s5BOOT: ;; use this code before reboot
3861 OldStackSeg dw 0 ;; used during PLUS processor reset
3862 ;; to save the stack segment
3864 ;;* Rset - Reset the 80286 in order to turn off the address lines
3865 ;; on the 6300 PLUS. Only way to do this on the
3866 ;; current hardware. The processor itself can be
3867 ;; reset by reading or writing prot 03f00h
3873 push ds ;; save segments
3875 mov ax,BiosSeg
;; point to the bios segment
3876 mov ds,ax ;; ds -> 40h
3878 push word ptr [RealLoc1
] ;; save what might have been here
3879 push word ptr [RealLoc1
+2]
3880 mov word ptr [RealLoc1
],cs:[offset ReturnBack
] ;; load our return address
3881 mov word ptr [RealLoc1
+2],cs
3883 mov [OldStackSeg
],ss ;; save the stack segment, too
3884 mov dx,03f00h ;; reset the processor
3890 hlt ;; should never get here
3892 mov ss,[OldStackSeg
] ;; start the recovery
3894 pop word ptr [RealLoc1
+2]
3895 pop word ptr [RealLoc1
]
3900 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3901 blkmov_386: ;_protect:
3902 assume
ds:int13code
,es:nothing
,ss:nothing
3904 ; Compute 32 bit address of start of I/O
3906 add ax,word ptr [base_addr
]
3907 adc dx,word ptr [base_addr
+ 2]
3911 ; Are we in virtual mode
3914 test cx,01B ; is the pe bit set
3919 ; Dispatch on function
3927 ; Update ES descriptor with address of track in cache
3929 mov si,offset es_des
3930 mov [si].bas_0_15
,ax
3931 mov [si].bas_16_23
,dl
3932 mov [si].bas_24_31
,dh
3934 ; Update DS descriptor with transfer address
3939 mov si,offset ds_des
3940 mov [si].bas_0_15
,ax
3941 mov [si].bas_16_23
,dl
3942 mov [si].bas_24_31
,dh
3945 ; Switch SI and DI for write transfer
3950 jmp short set_trans_1
3954 ; Update DS descriptor with address of track in cache
3956 mov si,offset ds_des
3957 mov [si].bas_0_15
,ax
3958 mov [si].bas_16_23
,dl
3959 mov [si].bas_24_31
,dh
3961 ; Update ES descriptor with transfer address
3966 mov si,offset es_des
3967 mov [si].bas_0_15
,ax
3968 mov [si].bas_16_23
,dl
3969 mov [si].bas_24_31
,dh
3971 ; Keep SI and DI the same for read transfer
3977 ; Restore Transfer Count
3986 ; we shall do the transfer 1024 words at a time
3999 cli ; Un interruptable
4001 lgdt fword ptr emm_gdt
4005 ; Switch to protected mode
4007 db 66h
,0Fh, 20h
, 0 ;mov eax,cr0
4009 db 66h
,0Fh,22h
, 0 ;mov cr0,eax
4011 ; Clear prefetch queue
4014 dw offset flush_prefetch
4015 dw cs_des
- start_gdt
4020 ; Initialize segment registers
4022 mov ax,ds_des
- start_gdt
4025 mov ax,es_des
- start_gdt
4028 shr cx,1 ; convert word count to dword count
4029 db 0f3h,066h,0a5h ; rep movsd
4030 ; rep movsw ; Move data
4033 ; Return to Real Mode
4036 db 66h
,0Fh, 20h
, 0 ; mov eax,cr0
4038 db 66h
,0Fh, 22h
, 0 ; mov cr0,eax
4040 ; Flush Prefetch Queue
4044 cod_seg dw ?
; Fixed up at initialization time
4049 ; see if transfer done else go to do next block
4062 mov cx,800h
; Retry this many times
4065 call A20
; Disable address line 20
4074 mov al,0AAh ; Drive not ready error
4079 assume
ds:int13code
,es:nothing
,ss:nothing
4085 ; Update tgt descriptor with address of track in cache
4088 mov [si].bas_0_15
,ax
4089 mov [si].bas_16_23
,dl
4090 mov [si].bas_24_31
,dh
4092 ; Update src descriptor with transfer address
4100 mov [si].bas_0_15
,ax
4101 mov [si].bas_16_23
,dl
4102 mov [si].bas_24_31
,dh
4104 jmp short set_trans_2
4108 ; Update src descriptor with address of track in cache
4111 mov [si].bas_0_15
,ax
4112 mov [si].bas_16_23
,dl
4113 mov [si].bas_24_31
,dh
4115 ; Update tgt descriptor with transfer address
4123 mov [si].bas_0_15
,ax
4124 mov [si].bas_16_23
,dl
4125 mov [si].bas_24_31
,dh
4129 ; Restore Transfer Count
4134 ; we shall do the transfer 1024 words at a time
4145 mov si,offset int15_gdt
4146 mov ax,emm_blkm
shl 8
4151 ; see if transfer done else fo to do next block
4156 add [src
.bas_0_15
],2048
4157 adc [src
.bas_16_23
],0
4158 adc [src
.bas_24_31
],0
4160 add [tgt
.bas_0_15
],2048
4161 adc [tgt
.bas_16_23
],0
4162 adc [tgt
.bas_24_31
],0
4168 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4169 ;** SEG_SET - Set up a LOADALL segment descriptor as in REAL mode
4171 ; This routine sets the BASE value in the segment descriptor
4172 ; pointed to by DS:SI with the segment value in AX as the 80286
4173 ; does in REAL mode. This routine is used to set a descriptor
4174 ; which DOES NOT have an extended 24 bit address.
4177 ; INTEL special documentation of LOADALL instruction
4180 ; DS:SI -> Seg register descriptor
4181 ; AX is seg register value
4187 ; This routine is specific to TYPE 1 driver
4191 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
4195 MUL CX ; Believe or not, this is faster than a 32 bit SHIFT
4196 MOV WORD PTR [SI.SEG_BASE
],AX
4197 MOV BYTE PTR [SI.SEG_BASE
+ 2],DL
4202 ;** FIX_DESCRIPTOR - Shuffle GTD IDT descriptors
4204 ; The segment descriptors for the IDT and GDT are stored
4205 ; by the SIDT instruction in a slightly different format
4206 ; than the LOADALL instruction wants them. This routine
4207 ; performs the transformation by PUSHing the contents
4208 ; of the descriptor, and then POPing them in a different
4212 ; INTEL special documentation of LOADALL instruction
4213 ; INTEL 80286 processor handbook description of SIDT instruction
4216 ; DS:SI points to IDT or GDT descriptor in SIDT form
4218 ; DS:SI points to IDT or GDT descriptor in LOADALL form
4222 ; NOTE: The transformation is reversable, so this routine
4223 ; will also work to transform a descriptor in LOADALL
4224 ; format to one in SIDT format.
4226 ; Specific to TYPE 1 driver
4230 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
4231 PUSH WORD PTR [SI + 4]
4232 PUSH WORD PTR [SI + 2]
4234 POP WORD PTR [SI + 4]
4236 POP WORD PTR [SI + 2]
4239 ;** DATA SPECIFIC TO THE LOADALL INSTRUCTION USAGE
4241 ; SWAP_80 and LOADALL_TBL are data elements specific to the use
4242 ; of the LOADALL instruction by TYPE 1 drivers.
4246 ; Swap buffer for contents of 80:0
4248 EVEN
; Force word alignment of SWAP_80 and LOADALL_TBL
4250 SWAP_80
DB 102 DUP(?
)
4254 ; LOADALL data buffer placed at 80:0
4256 LOADALL_TBL
LABEL BYTE
4261 FLAGS
DW 0 ; High 4 bits 0, Int off, Direction clear
4262 ; Trace clear. Rest don't care.
4263 LIP
DW OFFSET AFTER_LOADALL
4277 ESDES SEGREG_DESCRIPTOR
<>
4278 CSDES SEGREG_DESCRIPTOR
<>
4279 SSDES SEGREG_DESCRIPTOR
<>
4280 DSDES SEGREG_DESCRIPTOR
<>
4281 GDTDES DTR_DESCRIPTOR
<>
4282 LDTDES DTR_DESCRIPTOR
<0D000H,0,0FFH,0088H>
4283 IDTDES DTR_DESCRIPTOR
<>
4284 TSSDES DTR_DESCRIPTOR
<0C000H,0,0FFH,0800H>
4286 ;** TRUE LOCATION OF ABOVE_PID
4288 ; Define the TRUE (runtime TYPE 2 driver) location of ABOVE_PID.
4289 ; This is the only piece of TYPE 2 specific data that we need
4290 ; in the resident image. We must define it HERE rather than down
4291 ; at ABOVE_BLKMOV so that we have its TRUE location after the
4292 ; TYPE 2 code is swapped in at initialization. If we defined
4293 ; it down at ABOVE_BLKMOV any instruction like:
4295 ; MOV DX,[ABOVE_PID]
4297 ; Would have to be "fixed up" when we moved the ABOVE_BLKMOV
4298 ; code into its final location.
4301 ABOVE_PID EQU
WORD PTR $ - 2 ; TRUE location of ABOVE_PID
4304 ; The following label defines the end of the region where BLKMOV code
4305 ; may be swapped in. BLKMOV code to be swapped in MUST fit
4306 ; between DRIVE_CODE and DRIVE_END
4308 DRIVE_END
LABEL WORD
4311 BREAK <INT 19/9 Handlers
>
4314 ; As discussed above in the documentation of the EMM_CTRL sector it
4315 ; is necessary to hear about system re-boots so that the EMM_ISDRIVER
4316 ; bits in the EMM_REC structure can be manipulated correctly.
4318 ; On the IBM PC family of machines there are two events which cause a
4319 ; "soft" system re-boot which we might expect the EMM_CTRL sector to
4320 ; survive through. One is software INT 19H, the other is the Ctrl-Alt-Del
4321 ; character sequence which can be detected by "listening" on INT 9 for
4322 ; it. The code below consists of a handler for INT 19H, a handler
4323 ; for INT 9, and a drive TYPE dependant piece of code.
4325 ; The drive TYPE dependant piece of code works as follows:
4327 ; TYPE 1 uses EMM_CTRL sector so it turnd off the
4328 ; EMM_ISDRIVER bit in the record indicated by MY_EMM_REC.
4329 ; EACH TYPE 1 driver in the system includes the INT 19/9
4332 ; TYPE 2 DOES NOT use the EMM_CTRL sector but it still has
4333 ; a handler. What this handler does is issue an
4334 ; ABOVE_DEALLOC call to deallocate the Above Board
4335 ; memory allocated to INT13. In current versions
4336 ; of the EMM device driver this step is unnecessary
4337 ; as the EMM device driver is thrown away together
4338 ; with all of the allocation information when the system
4339 ; is re-booted. We do it anyway because some future version
4340 ; of the EMM device driver may be smarter and retain
4341 ; allocation information through a warm-boot. Currently,
4342 ; doing this doesn't hurt anything. Since this code cannot
4343 ; do a global ABOVE_DEALLOC for all TYPE 2 drivers in the
4344 ; system, it does an ABOVE_DEALLOC only for its memory
4345 ; and EACH TYPE 2 driver in the system includes the INT 19/9
4350 ; Storage locations for the "next" INT 19 and INT 9 vectors, the ones
4351 ; that were in the interrupt table when the device driver was loaded.
4352 ; They are initialized to -1 to indicate they contain no useful information.
4361 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4362 ; modification to meet new memory allocation standard
4370 ASSUME
DS:NOTHING
,SS:NOTHING
,ES:NOTHING
4372 ; This piece of code determines the size of extended memory
4373 ; which was allocated before this driver and then subtracts
4374 ; the amount it has allocated for itself
4376 ; inputs: ah = 88h is of interest
4377 ; outputs: ax = size of extended memory allocated by all before and
4392 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4393 ;** INT 9 Keyboard handler
4395 ; All this piece of code does is look for the Ctrl-Alt-Del event.
4396 ; If key is not Ctrl-Alt-Del, it jumps to OLD_9 without doing
4397 ; anything. If the Ctrl-Alt-Del key is detected it calls
4398 ; RESET_SYSTEM to perform driver TYPE specific re-boot code.
4399 ; It then resets INT 13H to disable the cache and then jumps to
4400 ; OLD_9 to pass on the event.
4402 ; NOTE THAT UNLIKE INT 19 THIS HANDLER DOES NOT NEED TO RESET
4403 ; THE INT 9 AND INT 19 VECTORS. This is because the Ctrl-Alt-Del
4404 ; IBM ROM re-boot code resets these vectors.
4406 ; We would LIKE to ALSO flush the cache, but we can't. For one the
4407 ; keyboard is at a higher IRQ than the disk. We could EOI the keyboard,
4408 ; but this doesn't fix the second problem. INT 13s to write
4409 ; out any dirty tracks take a LONG time, so long that we lose
4410 ; the key. In other words we see Ctrl-Alt-Del, but none of the
4411 ; INT 9 handlers after us will.
4415 ; INT 9 IBM ROM code in ROM BIOS listing of
4416 ; IBM PC Technical Reference manual for any PC family member
4425 ; THIS CODE IS USED BY TYPE 1,2 drivers.
4429 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
4433 CMP AL,83 ; DEL key?
4437 MOV AL,BYTE PTR DS:[417H
] ; Get KB flag
4439 TEST AL,0CH ; Ctrl Alt?
4441 MOV [INT_13_BUSY
],1 ; Exclude
4443 ; We would LIKE to do this, always but we can't. For one the keyboard
4444 ; is at a higher IRQ than the disk. We can EOI the keyboard,
4445 ; but this doesn't fix the second problem. INT 13s to write
4446 ; out any dirty tracks take a LONG time, so long that we loose
4447 ; the key. In other words we see Ctrl-Alt-Del, but none of the
4448 ; INT 9 handlers after us will.
4450 CMP [REBOOT_FLUSH
],0 ; Reboot flush enabled?
4451 JZ NO_REBOOT_FLUSH
; No
4452 CMP [DIRTY_CACHE
],0 ; Anything to do?
4453 JZ NO_REBOOT_FLUSH
; No
4455 OUT 20H
,AL ; EOI the keyboard int
4456 CALL FLUSH_WHOLE_CACHE_SAV
; Flush cache
4459 CALL RESET_SYSTEM
; Ctrl Alt DEL
4461 ; Reset INT 13 vector to turn cache off
4463 MOV AX,WORD PTR [OLD_13
]
4465 MOV WORD PTR DS:[13H
* 4],AX
4466 MOV AX,WORD PTR [OLD_13
+ 2]
4467 MOV WORD PTR DS:[(13H
* 4) + 2],AX
4469 ; Reset INT 1C vector to turn cache off
4471 ; MOV AX,WORD PTR [OLD_1C]
4472 ; MOV WORD PTR DS:[1CH * 4],AX
4473 ; MOV AX,WORD PTR [OLD_1C + 2]
4474 ; MOV WORD PTR DS:[(1CH * 4) + 2],AX
4481 ;** INT 19 Software re-boot handler
4483 ; All this piece of code does is sit on INT 19 waiting for
4484 ; a re-boot to be signaled by being called. It calls
4485 ; FLUSH_WHOLE_CACHE_SAV to flush out any dirty cache info then
4486 ; RESET_SYSTEM to perform driver TYPE specific re-boot code,
4487 ; resets the INT 19, INT 13 and INT 9 vectors,
4488 ; and then jumps to OLD_19 to pass on the event.
4490 ; NOTE THAT UNLIKE INT 9 THIS HANDLER NEEDS TO RESET
4491 ; THE INT 9 AND INT 19 VECTORS. This is because the INT 19
4492 ; IBM ROM re-boot code DOES NOT reset these vectors, and we
4493 ; don't want to leave them pointing to routines that are not
4494 ; protected from getting stomped on by the re-boot.
4497 ; INT 19 IBM ROM code in ROM BIOS listing of
4498 ; IBM PC Technical Reference manual for any PC family member
4507 ; THIS CODE IS USED BY TYPE 1,2 drivers.
4511 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
4512 MOV [INT_13_BUSY
],1 ; Exclude
4513 cmp [reboot_flush
],0 ;
4515 CALL FLUSH_WHOLE_CACHE_SAV
; Flush out the cache
4522 MOV AX,WORD PTR [OLD_13
]
4525 ; Reset INT 13 vector to trun cache off
4527 MOV WORD PTR DS:[13H
* 4],AX
4528 MOV AX,WORD PTR [OLD_13
+ 2]
4529 MOV WORD PTR DS:[(13H
* 4) + 2],AX
4531 ; Reset INT 1C vector to turn cache off
4533 ; MOV AX,WORD PTR [OLD_1C]
4534 ; MOV WORD PTR DS:[1CH * 4],AX
4535 ; MOV AX,WORD PTR [OLD_1C + 2]
4536 ; MOV WORD PTR DS:[(1CH * 4) + 2],AX
4538 ; Since INT 19 DOES NOT reset any vectors (like INT 9 Ctrl Alt DEL does),
4539 ; we must replace those vectors we have mucked with.
4541 ; NOTE THAT WE RESET VECTORS DIRECTLY!!!!!!!!!!!!!!!!!!
4542 ; We are not sure that DOS is reliable enough to call.
4544 MOV AX,WORD PTR [OLD_19
]
4545 MOV WORD PTR DS:[19H
* 4],AX
4546 MOV AX,WORD PTR [OLD_19
+ 2]
4547 MOV WORD PTR DS:[(19H
* 4) + 2],AX
4548 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4549 ; removed from smartdrv
4550 ; MOV AX,WORD PTR [OLD_9]
4551 ; MOV WORD PTR DS:[9H * 4],AX
4552 ; MOV AX,WORD PTR [OLD_9 + 2]
4553 ; MOV WORD PTR DS:[(9H * 4) + 2],AX
4554 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4555 mov ax,word ptr [old_15
]
4556 cmp ax,word ptr [old_15
+2]
4561 mov word ptr ds:[15h
*4],ax
4562 mov ax,word ptr [old_15
+2]
4563 mov word ptr ds:[(15h
*4) +2],ax
4571 ;** RESET_SYSTEM perform TYPE 1 (/E) driver specific reboot code
4573 ; This code performs the EMM_ISDRIVER reset function as described
4574 ; in EMM.ASM for all EMM_REC structure for this device (offset
4575 ; stored in MY_EMM_REC). We use the same LOADALL
4576 ; method described at BLKMOV to address the EMM_CTRL sector
4577 ; at the start of extended memory and perform our changes in
4580 ; NOTE: RESET_SYSTEM ALSO defines the start of ANOTHER piece of
4581 ; driver TYPE specific code that TYPE 2 drivers
4582 ; will have to swap in a different piece of code for.
4591 ; This code is specific to TYPE 1 drivers
4595 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
4596 JMP SHORT TRUE_START
4598 MY_EMM_REC
DW 0 ; Offset into 1K EMM_CTRL of my record
4602 mov cs:[reboot_flg
],0ffh ; set the reboot flag
4603 cmp cs:[my_emm_rec
],0 ; was an emm record allocated?
4608 ; reset base_addr to be address of emm ctrl sector
4610 mov ax,cs:[emm_ctrl_addr
]
4611 mov dx,cs:[emm_ctrl_addr
+2]
4612 mov word ptr cs:[base_addr
],ax
4613 mov word ptr cs:[base_addr
+2],dx
4615 ; read 1k emm control sector into track buffer, I want to keep memory
4616 ; access methods separate from driver code. We end up wasting a lot of
4617 ; time here but is reboot code anyway so thats okay
4621 mov di,cs:[track_buffer_ptr
]
4633 ; fix the flags for my emm record so that it is no longer in use
4635 mov bx,cs:[my_emm_rec
]
4637 and es:[bx.emm_flags
],not emm_isdriver
4639 ; write out the modified emm record out to memory
4653 ; The following label defines the end of the
4654 ; Driver TYPE specific RESET_SYSTEM code which will have to be replaced
4655 ; for different driver TYPEs as the code between RESET_SYSTEM and
4656 ; RESET_INCLUDE. Swapped in code MUST FIT between RESET_SYSTEM and
4659 RESET_INCLUDE
LABEL BYTE
4662 ; This data is only used at INIT, but it must be protected from overwrite
4663 ; by the DO_INIT code.
4665 TERM_ADDR
LABEL DWORD ; Address to return as break address in INIT packet
4666 DW ?
; Computed at INIT time
4667 DW ?
; INT13 CS filled in at INIT
4670 ; THIS CODE MUST BE IN RESIDENT PORTION BECAUSE IT WRITES IN THE AREA
4671 ; OCCUPIED BY THE DISPOSABLE INIT CODE.
4674 ;** DO_INIT - Initialize cache structures to "empty"
4680 SHL AX,CL ; AX is bytes per track buffer
4681 MOV BX,[CACHE_CONTROL_PTR
]
4684 MOV [BX.BACK_LRU_LNK
],-1
4685 MOV WORD PTR [BX.BASE_OFFSET
],0
4686 MOV WORD PTR [BX.BASE_OFFSET
+2],0
4687 MOV [BX.TRACK_FLAGS
],TRACK_FREE
4689 ADD BX,SIZE CACHE_CONTROL
; Next structure
4690 DEC CX ; One less to do
4691 JCXZ SETDONE
; one buffer in cache
4693 MOV [DI.FWD_LRU_LNK
],BX
4694 MOV [BX.BACK_LRU_LNK
],DI
4695 MOV [BX.TRACK_FLAGS
],TRACK_FREE
4696 MOV DX,WORD PTR [DI.BASE_OFFSET
]
4698 MOV WORD PTR [BX.BASE_OFFSET
],DX
4699 MOV DX,WORD PTR [DI.BASE_OFFSET
+2]
4701 MOV WORD PTR [BX.BASE_OFFSET
+2],DX
4703 ADD BX,SIZE CACHE_CONTROL
; Next structure
4706 MOV [DI.FWD_LRU_LNK
],-1
4707 MOV [CACHE_TAIL
],DI ; That is the tail
4709 ; NOTE FALL THROUGH!!!!!!!
4712 ;** SETBPB - Set INIT packet I/O return values
4714 ; This entry is used to set the INIT packet Break address
4717 ; TERM_ADDR set to device end
4723 ; COMMON TO TYPE 1, 2 drivers
4729 ; 7. Set the return INIT I/O packet values
4732 MOV CX,WORD PTR [TERM_ADDR
]
4733 MOV WORD PTR [BX.INIT_BREAK
],CX ;SET BREAK ADDRESS
4734 MOV CX,WORD PTR [TERM_ADDR
+ 2]
4735 MOV WORD PTR [BX.INIT_BREAK
+ 2],CX
4739 EVEN
; Make sure we get word alignment of the track
4743 ; The following items define the "track buffer". When we want to I/O a track
4744 ; this is where we do it. We cannot I/O the track directly into extended
4745 ; memory because we have no way to specify a 24 bit address to INT 13. We
4746 ; Cannot I/O direct to expanded memory because DMA into the expanded memory
4747 ; window is not supported. This buffer also "holds" one track, so we keep
4748 ; track of the last track that was in here because it is faster to access
4749 ; it here than through extended/expanded memory. A value of -1 in the
4750 ; "currency" fields indicates that there is currently nothing interesting
4751 ; in the track buffer. NOTE: It is ASSUMED that the track buffer always
4752 ; represents a track that is IN the cache, therefore one must be sure to
4753 ; invalidate the track buffer when the cache element it represents is
4754 ; discarded. The offset of the track buffer is dynamic. Never talk about
4755 ; OFFSET TRACK_BUFFER. Always get the track buffer address out of
4756 ; TRACK_BUFFER_PTR. The initialization code "moves" the track buffer so
4757 ; that it does not cause a DMA Boundary error which would slow things
4762 ; Cylinder and hd/drv of track in track buffer
4764 TRACK_BUFFER_CYLN
DW -1
4765 TRACK_BUFFER_HDDR
DW -1
4768 ; Pointer to track buffer. May be adjusted for DMA boundary error prevention.
4770 TRACK_BUFFER_PTR
DW OFFSET TRACK_BUFFER
4773 ; Cache structure pointers
4777 ; Pointer to cache structures. This ends up being right after TRACK_BUFFER.
4779 CACHE_CONTROL_PTR
DW ?
4782 ; Cache head and tail pointers
4788 ; This is the "in the 1Meg address space" track buffer. Its TRUE start
4789 ; may be adjusted for DMA boundary violation error prevention, and its
4790 ; Size may be adjusted depending on how many sectors per track there
4791 ; are. WARNING! This buffer must be AT LEAST 1024 bytes for /E driver
4792 ; init code (buffer for 1K EMM control sector).
4794 TRACK_BUFFER
DB (512 * 2 + 16) DUP(0)
4795 ; we really don't need those 16 bytes, i
4798 ; The TERM_ADDR for the device will be set somewhere "around" here
4802 BREAK <COMMON INIT
CODE>
4804 ;** DISPOSABLE INIT DATA
4806 ; INIT data which need not be part of resident image
4809 U_SWITCH db 0 ;; upper extended memory requested on 6300 PLUS
4811 EXT_K
DW ?
; Size in K of Extended memory.
4813 NUM_ARG
DB 1 ; Counter for numeric
4816 GOTSWITCH
DB 0 ; Bit map of switches seen
4818 SWITCH_E EQU
00000001B
4819 SWITCH_A EQU
00000010B ; Only switch allowed
4820 SWITCH_T EQU
00000100B
4821 SWITCH_D EQU
00001000B
4822 SWITCH_WT EQU
00010000B
4823 SWITCH_WC EQU
00100000B
4824 SWITCH_R EQU
01000000B
4825 SWITCH_C EQU
10000000B
4826 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4829 ; author: sunilp, august 1, 1987. thanks to rickha for most of this
4832 ; purpose: to determine whether extended memory cache can be installed
4833 ; on this system. also to determine and store in the system
4834 ; flag the machine identification.
4838 ; outputs: CY set if this machine doesn't allow extended memory cache.
4839 ; CY clear if this machine allows extended memory cache and
4840 ; the system flag is set according to the machine type.
4842 ; registers used: ax,es,flags
4843 ;----------------------------------
4844 ; Clear the state of the system flag
4846 assume
ds:int13code
,es:nothing
,ss:nothing
4847 xor ax,ax ; 0000 into AX
4848 mov [sys_flg
],al ; clear system flag
4849 ;----------------------------------
4850 ; Determine if 8086/8088 system. If so we should abort immediately.
4853 popf ; try to put that in the flags
4855 pop ax ; look at what really went into flags
4856 and ax,0F000h ; mask off high flag bits
4857 cmp ax,0F000h ; Q: was high nibble all ones ?
4858 je cpu_err
; Y: it's an 8086 (or 8088)
4859 ;----------------------------------
4860 ; Determine if 80286/80386 machine.
4862 mov ax,0F000h ; N: try to set the high bits
4864 popf ; ... in the flags
4866 pop ax ; look at actual flags
4867 and ax,0F000h ; Q: any high bits set ?
4868 je cpu_286
; N: it's an 80286
4869 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4870 ; It is a 386 cpu. We should next try to determine if the ROM is
4871 ; B0 or earlier. We don't want these guys.
4876 and ax,not 0F000h ; and
4879 ;----------------------------------
4880 ; the next three instructions were removed because we are loaded
4881 ; in real mode. So there is no need to check for virtual mode.
4883 ; smsw ax ;check for Virtual Mode
4884 ; test ax,0001 ; Q: Currently in Virtual Mode ?
4885 ; jnz cpu_exit ; Y: quit with error message
4886 ;----------------------------------
4887 ; N: check 386 stepping for B0
4888 call is_b0
; Q: B0 ?
4889 jc cpu_err
; Y: abort
4890 ;----------------------------------
4891 ; We have a valid 386 guy. Set the flag to indicate this.
4893 or [sys_flg
],M_386
; set 386 bit
4896 ;----------------------------------
4897 ; This is a 286 guy. Check for AT model byte. We don't want non-ATs.
4898 ; Set 286 bit if AT type. Then check for PS/2
4903 cmp byte ptr es:[0eh],0fch ; AT model byte
4904 jne cpu_err
; if not abort
4906 or [sys_flg
],M_286
; set 286 flag bit
4909 ; Determine if this is a PS/2 system
4922 ; We're on an NCR machine, send D7 and D5 to the 8042 in order
4923 ; to toggle A20 instead of the DF and DD we usually send.
4925 mov cs:[A20On
],0D790h
4926 mov cs:[A20Off
],0D500h
4928 ;----------------------------------
4934 ;----------------------------------
4943 ;*--------------------------------------------------------------------------*
4945 ;* IsPS2Machine HARDWARE DEP. *
4947 ;* Check for PS/2 machine *
4950 ;* RETS: AX = 1 if we're on a valid PS/2 machine, 0 otherwise *
4951 ;* REGS: AX and Flags clobbered *
4953 ;*--------------------------------------------------------------------------*
4955 IsPS2Machine proc
near
4957 mov ah,0C0h ; Get System Description Vector
4960 jc short IPMNoPS2
; Error? Not a PS/2.
4962 ; Are we on a PS/2 Model 35?
4964 je short IPMFoundIt
; Yup, use the PS/2 method
4966 ; Do we have a "Micro Channel" computer?
4967 mov al,byte ptr es:[bx+5] ; Get "Feature Information Byte 1"
4968 test al,00000010b ; Test the "Micro Channel Implemented" bit
4971 IPMFoundIt: mov ax,1
4980 ;*--------------------------------------------------------------------------*
4982 ;* IsNCRMachine HARDWARE DEP. *
4984 ;* Check for NCR machine *
4987 ;* RETS: AX = 1 if we're on a valid NCR machine, 0 otherwise *
4988 ;* REGS: AX and Flags clobbered *
4990 ;*--------------------------------------------------------------------------*
4992 ; Look for 'NC' at F000:FFEA
4994 IsNCRMachine proc
near
4998 mov ax,word ptr es:[0FFEAh]
5004 INMFoundIt: mov ax,1
5010 ;******************************************************************************
5011 ; IS_B0 - check for 386-B0
5013 ; This routine takes advantage of the fact that the bit INSERT and
5014 ; EXTRACT instructions that existed in B0 and earlier versions of the
5015 ; 386 were removed in the B1 stepping. When executed on the B1, INSERT
5016 ; and EXTRACT cause an INT 6 (invalid opcode) exception. This routine
5017 ; can therefore discriminate between B1/later 386s and B0/earlier 386s.
5018 ; It is intended to be used in sequence with other checks to determine
5019 ; processor stepping by exercising specific bugs found in specific
5020 ; steppings of the 386.
5022 ; ENTRY: REAL MODE on 386 processor (CPU ID already performed)
5023 ; EXIT: CF = 0 if B1 or later
5024 ; CF = 1 if B0 or prior
5030 ;------------------------------------------------------------------------------
5038 mov ds,bx ; DS = 0000 (real mode IDT)
5039 assume
ds:R_Mode_IDT
5041 pop cs:[int6_save
] ; save old INT 6 offset
5043 pop cs:[int6_save
+2] ; save old INT 6 segment
5045 mov word ptr [bx+(6*4)],offset int6
5046 mov [bx+(6*4)+2],cs ; set vector to new INT 6 handler
5048 ; Attempt execution of Extract Bit String instruction. Execution on
5049 ; B0 or earlier with length (CL) = 0 will return 0 into the destination
5050 ; (CX in this case). Execution on B1 or later will fail and dummy INT 6
5051 ; handler will return execution to the instruction following the XBTS.
5052 ; CX will remain unchanged in this case.
5056 mov cx,0FF00h ; Extract length (CL)=0, CX=non-zero
5057 db 0Fh,0A6h,0CAh ; XBTS CX,DX,AX,CL
5060 mov ds,bx ; DS = 0000 (real mode IDT)
5061 push cs:[int6_save
] ; restore original INT 6 offset
5063 push cs:[int6_save
+2] ; restore original INT 6 segment
5066 or cx,cx ; Q: CX = 0 (meaning <=B0) ?
5067 jz ib_exit
; Y: exit (carry clear)
5068 stc ; N: set carry to indicate >=B1
5070 cmc ; flip carry tense
5075 ret ; *** RETURN ***
5078 ; Temporary INT 6 handler - assumes the cause of the exception was the
5079 ; attempted execution of an XTBS instruction.
5084 add word ptr [bp+2],3 ; bump IP past faulting instruction
5086 iret ; *** RETURN ***
5087 int6_save dw 0000,0000
5089 ;***************************************************************************
5091 ;** PRINT - Print a "$" terminated message on stdout
5093 ; This routine prints "$" terminated messages on stdout.
5094 ; It may be called with only the DX part of the DS:DX message
5095 ; pointer set, the routine puts the correct value in DS to point
5096 ; at the INT13 messages.
5099 ; DX pointer to "$" terminated message (INT13CODE relative)
5105 ; COMMON TO TYPE 1, 2, 3, 4 drivers
5109 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
5113 MOV AH,Std_Con_String_Output
5118 ;** ITOA - Print Decimal Integer on stdout
5120 ; Print an unsigned 16 bit value as a decimal integer on stdout
5121 ; with leading zero supression. Prints from 1 to 5 digits. Value
5124 ; Routine uses divide instruction and a recursive call. Maximum
5125 ; recursion is four (five digit number) plus one word on stack
5128 ; ENTRY AX has binary value to be printed
5130 ; USES AX,CX,DX,FLAGS
5132 ; COMMON TO TYPE 1, 2, 3, 4 drivers
5136 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
5140 DIV CX ; DX is low digit, AX is higher digits
5142 JZ PRINT_THIS_DIGIT
; No more higher digits
5143 PUSH DX ; Save this digit
5144 CALL ITOA
; Print higher digits first
5145 POP DX ; Recover this digit
5147 ADD DL,"0" ; Convert to ASCII
5148 MOV AH,Std_CON_Output
5153 ;** INT13$INIT - Device Driver Initialization routine
5155 ; INT13 Initialization routine. This is the COMMON initialization
5156 ; code used by ALL driver TYPEs. Its jobs are to:
5158 ; 1. Initialize various global values
5159 ; 2. Check for correct DOS version and do changes to the device
5160 ; based on the DOS version if needed.
5161 ; 3. Set OLD_13, OLD_1C and Parse the command line and set values accordingly
5162 ; 4. Set up the cache parameters and
5163 ; Call a TYPE specific INIT routine based on the Parse
5164 ; to set up a specific driver TYPE.
5165 ; 5. Print out report of INT13 parameters
5166 ; 6. Set the return INIT I/O packet values
5168 ; The first two lines perform step 1. Step two starts after and
5169 ; goes through VER_OK. Step 3 starts at VER_OK and goes through
5170 ; ARGS_DONE. Step 4 starts at ARGS_DONE and goes through I001.
5171 ; Step 5 starts at I001 and goes through DRIVE_SET. Step 6 starts
5172 ; at DRIVE_SET and goes through SETBPB. Step 7 starts at SETBPB
5173 ; and ends at the JMP DEVEXIT 10 lines later.
5175 ; At any time during the above steps an error may be detected. When
5176 ; this happens one of the error messages is printed and INT13
5177 ; de-installs itself. It does this at DEVABORT_NOMES by changing
5178 ; the Device attributes to a BLOCK DEVICE and setting its size to NULL.
5179 ; All INT13 needs to do is make sure any INT vectors it changed
5180 ; (INT 9 and INT 19 and INT 13) get restored to what they were
5181 ; when INT13 first started. If an EMM_CTRL sector is being
5182 ; used (TYPE 1) and one of the EMM_REC structures has been
5183 ; marked EMM_ISDRIVER by this driver, it must turn that bit back off
5184 ; since the driver did not install. A TYPE 2 driver must make sure it
5185 ; ABOVE_DEALLOCs any memory it allocated from the EMM device. The duty
5186 ; of reclaiming EMM_CTRL or Above Board memory and re-setting vectors
5187 ; is done by the DISK_ABORT routine which may be called by either
5188 ; this COMMON INIT code, or the TYPE specific INIT code.
5190 ; Step 1 initializes the segment part of TERM_ADDR to the correct
5191 ; value for type 1, 2 drivers.
5193 ; Step 2 checks to make sure that we are running on a DOS in the
5194 ; 2.X or 3.X series which this driver is restricted to. If running
5195 ; on a 2.X series the device header attribute word and device command
5196 ; table are patched to exclude those device calls that don't exist
5199 ; Step 3 uses the "DEVICE = xxxxxxxxx" line pointer provided by
5200 ; DOS to look for the various device parameters. NOTE: This pointer
5201 ; IS NOT DOCUMENTED in the DOS 2.X tech ref material, but it does
5202 ; exist in the same way as 3.X. This code is simple even though
5203 ; it looks rather long. First it skips over the device name field
5204 ; to get to the arguments. In then parses the arguments as they are
5205 ; encountered. All parameter errors are detected here. NOTE THAT
5206 ; THIS ROUTINE IS NOT RESPONSIBLE FOR SETTING DEFAULT VALUES OF
5207 ; PARAMETER VARIABLES. This is accomplished by static initialization
5208 ; of the parameter variables.
5210 ; Step 4 calls a device TYPE specific initialization routine based
5211 ; on the parse in step 3 (presence or absense of /E and /A switches).
5212 ; NOTE that one of the prime jobs of these device TYPE specific
5213 ; routines is to set all of the variables that are needed by Step
5214 ; 5 and 6 that haven't been set by the COMMON init code:
5216 ; DEV_SIZE set to TRUE size of device
5217 ; BASE_ADDR set to TRUE start of device so BLKMOV
5219 ; BASE_RESET set so DISK_ABORT can be called
5220 ; TERM_ADDR set to correct end of device
5222 ; Step 5 makes the status report display of DEVICE SIZE and other info.
5224 ; Step 6 sets the INIT I/O packet return values for Break address.
5228 ; MS-DOS Technical Reference manual section on
5229 ; Installable Device Drivers
5231 ; ENTRY from INT13$IN
5232 ; EXIT Through DEVEXIT
5235 ; COMMON TO TYPE 1, 2 drivers
5239 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
5241 ; 1. Initialize various global values
5243 MOV WORD PTR [TERM_ADDR
+ 2],CS
5245 ; 2. Check for correct DOS version and do changes to the device
5246 ; based on the DOS version if needed.
5252 CMP AX,(2 SHL 8) + 00
5253 JB BADVER
; Below 2.00, BAD
5254 CMP AX,(3 SHL 8) + 00
5255 JB VER2X
; 2.X requires some patches
5256 CMP AX,(4 SHL 8) + 00
5257 JBE VER_OK
; 3.X or 4.0 OK
5259 MOV DX,OFFSET BADVERMES
5263 AND [DEVATS
],NOT DEVOPCL
; No such bit in 2.X
5264 MOV BYTE PTR [INT13TBL
],11 ; Fewer functions too
5268 ;; 2.5 Check here for 6300 PLUS machine. First look for Olivetti copyright,
5269 ;; and if found, check id byte at f000:fffd.
5272 push es ;; Olivetti Machine?
5273 mov ax,0fc00h ;; Look for 'OL' at fc00:50
5276 jnz notS5
;; not found
5279 cmp word ptr es:[0fffdh],0fc00h ;; look for 6300 plus
5281 mov [S5_FLAG
],S_OLIVETTI
;; yep, set flag
5284 ;; Check here for an HP Vectra machine. Look for HP id byte.
5290 mov [S5_FLAG
],S_VECTRA
5295 ; 3. Set OLD_13, OLD_1C and Parse the command line and set values accordingly
5297 MOV AX,(Get_Interrupt_Vector
SHL 8) OR 13H
5299 MOV WORD PTR [OLD_13
],BX
5300 MOV WORD PTR [OLD_13
+ 2],ES
5301 MOV AX,(Get_Interrupt_Vector
SHL 8) OR 1
CH
5303 MOV WORD PTR [OLD_1C
],BX
5304 MOV WORD PTR [OLD_1C
+ 2],ES
5307 MOV DX,OFFSET HEADERMES
5309 LDS SI,[SI.INIT_BPB
] ; DS:SI points to config.sys
5310 SKIPLP1: ; Skip leading delims to start of name
5326 SKIPLP2: ; Skip over device name
5339 CMP AL,0 ; Need this for 2.0 2.1
5341 SCAN_LOOP: ; PROCESS arguments
5344 OR AL,AL ; Need this for 2.0 2.1
5365 JA BAD_PARMJ
; Only 1 numeric argument
5373 mov [current_dev_size
],bx
5381 INC [NUM_ARG
] ; Next numeric argument
5386 MOV DX,OFFSET ERRMSG1
5390 ; INC [NULDEV] ;Indicate NUL device
5391 ; MOV WORD PTR [TERM_ADDR],OFFSET ERROR_END ;Minimul null device
5392 ; JMP SETBPB ;and return
5394 MOV WORD PTR [BX].INIT_NUM
,0
5395 MOV WORD PTR [BX].INIT_BREAK
[0],0
5396 MOV WORD PTR [BX].INIT_BREAK
[2],CS
5404 if WINDOWS_SWITCHES
eq 0
5408 TEST [GOTSWITCH
],SWITCH_E
+ SWITCH_A
5410 OR [GOTSWITCH
],SWITCH_E
5416 endif
; WINDOWS_SWITCHES eq 0
5418 ;; Added for /u switch
5419 cmp al,'u' ;; Look for U switch for PLUS
5421 cmp [S5_FLAG
],S_OLIVETTI
;; No good unless PLUS
5423 TEST [GOTSWITCH
],SWITCH_A
;; Already have switch A ?
5437 endif
;WINDOWS_SWITCHES
5440 TEST [GOTSWITCH
],SWITCH_A
; Was SWITCH_E + SWITCH_A
5442 ;; added for /u switch
5446 OR [GOTSWITCH
],SWITCH_A
5450 if WINDOWS_SWITCHES
eq 0
5456 TEST [GOTSWITCH
],SWITCH_D
5458 OR [GOTSWITCH
],SWITCH_D
5478 TEST [GOTSWITCH
],SWITCH_WC
5480 OR [GOTSWITCH
],SWITCH_WC
5492 TEST [GOTSWITCH
],SWITCH_WT
5494 OR [GOTSWITCH
],SWITCH_WT
5495 MOV [WRITE_THROUGH
],1
5514 TEST [GOTSWITCH
],SWITCH_T
5516 OR [GOTSWITCH
],SWITCH_T
5517 MOV [TICK_SETTING
],BX
5531 TEST [GOTSWITCH
],SWITCH_R
5533 OR [GOTSWITCH
],SWITCH_R
5534 MOV [REBOOT_FLUSH
],1
5548 TEST [GOTSWITCH
],SWITCH_C
5550 OR [GOTSWITCH
],SWITCH_C
5553 endif
;WINDOWS_SWITCHES eq 0
5557 ; 4. Call a TYPE specific INIT routine based on the Parse
5558 ; to set up a specific driver TYPE.
5563 MOV AL,[DRIVER_SEL
] ; Find out which init to call
5578 ; update the current device size
5581 mov [current_dev_size
],ax
5583 ; 6. Print out report of INT13 parameters
5585 MOV DX,OFFSET STATMES1
5589 MOV DX,OFFSET STATMES1E
5592 MOV DX,OFFSET STATMES1A
5595 MOV DX,OFFSET STATMES2
5599 MOV DX,OFFSET STATMES3
5603 MOV DX,OFFSET STATMES4
5607 mov dx,offset omti_msg
5611 MOV DX,OFFSET STATMES5
5615 MOV DX,OFFSET STATMES6
5619 ; Turn on the cache by chaining INT 13, and INT 1C
5621 MOV DX,OFFSET INT_13_HANDLER
5622 MOV AX,(Set_Interrupt_Vector
SHL 8) OR 13H
5624 ; MOV DX,OFFSET INT_1C_HANDLER
5625 ; MOV AX,(Set_Interrupt_Vector SHL 8) OR 1CH
5629 ;** DRIVEPARMS Initialize drive related cache parameters
5632 ; Stuff set so that BLKMOV can be used to access cache memory
5633 ; DEV_SIZE set to TRUE cache size in K
5636 ; Error, message already printed
5638 ; TRACK_BUFFER_PTR adjusted for DMA error prevention
5639 ; CACHE_CONTROL_PTR set
5644 ; HDARRAY set (SUNILP)
5648 ; COMMON TO TYPE 1, 2 drivers
5652 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
5653 MOV DX,OFFSET NOHARD
5660 MOV DX,OFFSET BIGTRACK
5664 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
5666 ; First figure out sec/track of any hardfiles
5675 MOV [SECTRKARRAY
],CL
5682 CMP CX,MAX_HARD_FILES
- 1
5684 MOV CX,MAX_HARD_FILES
- 1
5696 AND BX,0000000001111111B
5697 MOV [BX.SECTRKARRAY
],CL
5710 ; Figure out number of full tracks that fit in cache
5714 MUL CX ; DX:AX = Bytes per track
5717 MOV BX,AX ; BX is bytes per track
5720 MUL CX ; DX:AX = size of cache in bytes
5721 DIV BX ; AX is full tracks in cache
5724 ; Figure out if we have a DMA boundary problem
5726 mov DX,DS ; Check for 64k boundary error
5730 shl DX,1 ; Segment converted to absolute address
5731 add DX,[TRACK_BUFFER_PTR
] ; Combine with offset
5732 add DX,511 ; simulate a one sector transfer
5733 ; And set next divide for round up
5735 ; If carry is set, then we are within 512 bytes of the end of the DMA segment.
5736 ; Adjust TRACK_BUFFER_PTR UP by 512 bytes.
5739 add [TRACK_BUFFER_PTR
],512 ; adjust
5740 jmp short SetCachest
5744 ; DX is the physical low 16 bits of the proposed track buffer plus 511.
5745 ; See how many sectors fit up to boundary.
5747 shr DH,1 ; DH = number of sectors in DMA segment
5748 ; till start of buffer rounded up
5749 mov AH,128 ; AH = max number of sectors in DMA segment
5752 ; AH is now the number of sectors that we can successfully transfer using this
5753 ; address without a DMA boundary problem. If this number is above or equal to
5754 ; the track buffer size, then buffer is OK. Otherwise, we adjust buffer UP
5755 ; till it is after the boundary by adding ((AH+1)*512) to the buffer address.
5759 cmp AX,[SECTRACK
] ; can we fit it in?
5760 jae SetCachest
; yes, buffer is OK
5762 mov cl,9 ; Mult by 512
5764 add [TRACK_BUFFER_PTR
],ax ; Adjust
5767 ; Set pointer to cache control structures
5770 mov cl,9 ; Mult by 512
5771 shl bx,cl ; AX is bytes in Track buffer
5772 add bx,[TRACK_BUFFER_PTR
] ; First byte after track buffer
5773 mov [CACHE_CONTROL_PTR
],bx
5774 mov cx,SIZE CACHE_CONTROL
5781 mov word ptr [TERM_ADDR
],bx
5785 ;** GETNUM - Read an unsigned integer
5787 ; This routine looks at DS:SI for a decimal unsigned integer.
5788 ; It is up to the caller to make sure DS:SI points to the start
5789 ; of a number. If it is called without DS:SI pointing to a valid
5790 ; decimal digit the routine will return 0. Any non decimal digit
5791 ; defines the end of the number and SI is advanced over the
5792 ; digits which composed the number. Leading "0"s are OK.
5794 ; THIS ROUTINE DOES NOT CHECK FOR NUMBERS LARGER THAN WILL FIT
5795 ; IN 16 BITS. If it is passed a pointer to a number larger than
5796 ; 16 bits it will return the low 16 bits of the number.
5798 ; This routine uses the MUL instruction to multiply the running
5799 ; number by 10 (initial value is 0) and add the numeric value
5800 ; of the current digit. Any overflow on the MUL or ADD is ignored.
5803 ; DS:SI -> ASCII text of number
5805 ; BX is binary for number
5806 ; SI advanced to point to char after number
5810 ; COMMON TO TYPE 1, 2, 3, 4 drivers
5814 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
5834 BREAK <INITIAL EMM control sector
>
5836 ;** flag to signify valid emm control record
5840 ;** INITIAL EMM_CTRL sector
5842 ; This is a datum which represents a correct initial EMM_CTRL
5843 ; sector as discussed in the EMM_CTRL documentation. It is used
5844 ; to check for the presense of a valid EMM_CTRL by comparing
5845 ; the signature strings, and for correctly initializing the
5846 ; EMM_CTRL sector if needed.
5848 ; The DWORD at BASE_RESET, which is the EMM_BASE of the NULL
5849 ; 0th EMM_REC structure, is used as a storage location of
5850 ; the address of the EMM_CTRL sector (PLUS 1024!!!!!!).
5851 ; This value can be used if it is necessary to re-address the
5852 ; EMM_CTRL sector during initialization. See the DISK_ABORT routine.
5853 ; NOTE THAT BASE_RESET CAN NOT BE USED AT RUNTIME AS THIS DATUM
5854 ; IS NOT PART OF THE RESIDENT IMAGE.
5856 ; This data is appropriate to TYPE 1 drivers
5859 EMM_CONTROL
LABEL BYTE
5860 DB "MICROSOFT EMM CTRL VERSION 1.00 CONTROL BLOCK "
5864 DW EMM_ALLOC
+ EMM_ISDRIVER
5866 ;; Note: When using upper extended memory on the PLUS, the value
5867 ;; at BASE_RESET + 2 is patched to FA during initialization.
5869 BASE_RESET
LABEL DWORD ; RESMEM driver must patch this value
5870 DW EXTMEM_LOW
+ 1024
5878 BREAK <INT13
COMMON INIT ROUTINES
>
5880 ;** DISK_ABORT - De-install INT13 after init
5882 ; This routine MUST BE CALLED to de-install a INT13 cache
5883 ; if the de-installation takes place:
5885 ; AFTER INT 19/INT 9 vectors are replaced
5886 ; AFTER ABOVE_PID is valid for TYPE 2
5887 ; AFTER an EMM_REC structure in the EMM_CTRL sector
5888 ; has been marked EMM_ISDRIVER for TYPE 1.
5890 ; In all cases the INT 9 and INT 19 vectors are replaced if the
5891 ; value of both words of OLD_19 is NOT -1. This is why the initial value
5892 ; of this datum is -1. In the event that the INT 9 and INT 19
5893 ; vectors are replaced, this datum takes on some value other than -1.
5895 ; If this is a TYPE 1 driver the EMM_ISDRIVER bit is
5896 ; turned off in the EMM_REC pointed to by MY_EMM_REC.
5897 ; NOTE THAT A TYPE 1 DRIVER MAY USE THIS ROUTINE
5898 ; IF IT HAS NOT "TURNED ON" AN EMM_ISDRIVER BIT IN ONE OF THE EMM_REC
5899 ; STRUCTURES. This is OK because the initial 0 value of MY_EMM_REC
5900 ; is checked, and nothing is done if it is still 0.
5902 ; If this is a TYPE 2 driver, an ABOVE_DEALLOC call is made on
5906 ; BASE_RESET valid if TYPE 1
5907 ; ABOVE_PID valid if TYPE 2
5913 ; COMMON TO TYPE 1, 2 drivers
5917 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
5923 ; TYPE 2, De-alloc the Above Board memory
5926 MOV AH,ABOVE_DEALLOC
5928 CMP AH,ABOVE_ERROR_BUSY
5933 CMP [MY_EMM_REC
],0 ; Need to turn off bit?
5936 ; TYPE 1, turn off EMM_ISDRIVER at MY_EMM_REC
5938 MOV AX,WORD PTR [BASE_RESET
]
5939 MOV DX,WORD PTR [BASE_RESET
+ 2]
5940 SUB AX,1024 ; Backup to EMM_CTRL
5942 MOV WORD PTR [BASE_ADDR
],AX
5943 MOV WORD PTR [BASE_ADDR
+ 2],DX
5945 CALL CTRL_IO
; Get EMM_CTRL
5947 MOV DI,OFFSET TRACK_BUFFER
5949 AND [DI.EMM_FLAGS
],NOT EMM_ISDRIVER
; Undo install
5951 CALL CTRL_IO
; EMM_CTRL back out
5954 ; Reset INT 9, and/or INT 19 if OLD_19 is not -1
5965 MOV AX,(Set_Interrupt_Vector
SHL 8) OR 19H
5967 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5968 ; removed from smartdrv
5970 ; MOV AX,(Set_Interrupt_Vector SHL 8) OR 9H
5972 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5979 mov ax,(set_interrupt_vector
shl 8) or 15h
5985 ;** CTRL_IO - Read/Write the first 1024 bytes at BASE_ADDR
5987 ; This routine is used at INIT time to read the first 1024
5988 ; bytes at BASE_ADDR. If TYPE 1 and BASE_ADDR points
5989 ; to the EMM_CTRL address (initial value), the EMM_CTRL sector
5990 ; is read/written. If TYPE 1 and BASE_ADDR has been set
5991 ; to the start of the cache, the first 1024 bytes of the cache
5992 ; are read/written. If TYPE 2, the first 1024 bytes of
5993 ; the cache are read/written. All this routine does is
5994 ; set inputs to BLKMOV to transfer 1024 bytes at offset 0 to/from
5998 ; BH = 0 for READ, 1 for WRITE
6000 ; TRACK_BUFFER filled in with 1024 bytes at BASE_ADDR
6004 ; COMMON TO TYPE 1, 2 drivers
6008 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
6010 MOV AX,DX ; Offset 0
6011 MOV CX,512 ; 1024 bytes
6014 MOV DI,OFFSET TRACK_BUFFER
6016 CALL BLKMOV
; Read in EMM_CTRL
6020 ;** MM_SETDRIVE - Look for/Init EMM_CTRL and DOS volume
6022 ; This routine is used by TYPE 1 drivers to check for/initialize
6023 ; the EMM_CTRL sector.
6025 ; This routine reads the EMM_CTRL sector in to TRACK_BUFFER
6026 ; CALLS FIND_VDRIVE to check out and alloc or find an EMM_REC
6027 ; Sets BASE_ADDR to point to the start of the INT13 cache memory
6028 ; Writes the updated EMM_CTRL back out from TRACK_BUFFER
6031 ; BASE_ADDR initialized to point at START of extended memory
6032 ; so that the EMM_CTRL sector can be accessed by
6033 ; doing I/O at offset 0.
6034 ; EXT_K is set to size of extended memory
6035 ; DEV_SIZE is set to user requested device size
6037 ; CARRY SET - error, message already printed
6039 ; BASE_ADDR set for this drive
6040 ; DEV_SIZE set to TRUE size
6045 ; Used by TYPE 1 drivers
6049 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
6051 CALL CTRL_IO
; Get EMM_CTRL
6052 MOV DX,OFFSET INIT_IO_ERR
6054 CALL FIND_VDRIVE
; Snoop
6056 PUSH ES ; Save EMM_BASE from EMM_REC
6058 ; modification sunilp
6059 cmp [u_switch
],0 ; we shall use ol' microsoft standard for this
6060 je mm_s$1
; if we are using int15 scheme no need to write
6061 ; out emm control record
6062 ; end modification sp
6064 CALL CTRL_IO
; Write EMM_CTRL back out
6065 MOV DX,OFFSET INIT_IO_ERR
6068 POP WORD PTR [BASE_ADDR
] ; Set final correct BASE_ADDR
6069 POP WORD PTR [BASE_ADDR
+ 2]
6081 ;** FIND_VDRIVE - Check out EMM_CTRL and alloc
6083 ; This code checks for a valid EMM_CTRL and sets up
6084 ; an initial one if there isn't. It then performs the
6085 ; algorithm described in the EMM_CTRL documentation
6086 ; to either allocate a NEW EMM_REC of type EMM_APPLICATION,
6087 ; or find an existing EMM_REC which is EMM_APPLICATION and has
6088 ; its EMM_ISDRIVER bit clear. In the later case it
6089 ; checks to see if DEV_SIZE is consistent with EMM_KSIZE
6090 ; and tries to make adjustments to EMM_KSIZE or DEV_SIZE
6091 ; if they are not consistent.
6093 ; First the EMM_CTRL signature strings are checked.
6094 ; If they are not valid we go to SETCTRL to set up a new
6095 ; empty EMM_CTRL in SECTOR_BUFFER.
6096 ; If the signatures are valid, EMM_TOTALK is checked
6097 ; against EXT_K. If they are the same, the EMM_CTRL sector is
6098 ; valid and we skip to SCAN_DEV. Otherwise we initialize the
6099 ; EMM_CTRL sector at SETCTRL. All we need to do to set up the initial
6100 ; EMM_CTRL sector is transfer the record at EMM_CONTROL into
6101 ; TRACK_BUFFER and set EMM_TOTALK and EMM_AVAILK to EXT_K - 1.
6103 ; In either case, finding a valid EMM_CTRL or setting up a correct
6104 ; initial one, we end up at SCAN_DEV. This code performs the
6105 ; scan of the EMM_REC structures looking for a "free" one
6106 ; or an allocated one which is EMM_APPLICATION and has its EMM_ISDRIVER
6107 ; bit clear as described in the EMM_CTRL sector documentation.
6109 ; If we find a "free" EMM_REC structure we go to GOT_FREE_REC
6110 ; and try to allocate some memory. This attempt will fail if
6111 ; EMM_AVAILK is less than 16K. We then call SET_RESET to do
6112 ; the INT 9/INT 19 setup. We adjust DEV_SIZE to equal the
6113 ; available memory if DEV_SIZE is > EMM_AVAILK. Then all we do
6114 ; is set EMM_AVAILK and all of the fields in the EMM_REC structure
6115 ; as described in the EMM_CTRL sector documentation.
6117 ; Call SET_RESET to do INT 9/INT 19 setup.
6118 ; IF the EMM_REC structure we found is the LAST EMM_REC structure
6119 ; we cannot edit any sizes and whatever the EMM_KSIZE
6120 ; is we stuff it into DEV_SIZE and set the EMM_ISDRIVER
6121 ; bit, and we're done.
6122 ; NOTE: We DO NOT check that EMM_KSIZE is at least
6123 ; 16K as we know this EMM_REC was created
6124 ; by some PREVIOUS INT13 program who
6125 ; DID make sure it was at least 16K
6127 ; IF EMM_KSIZE == DEV_SIZE
6128 ; set EMM_ISDRIVER and we're done
6129 ; IF EMM_KSIZE < DEV_SIZE
6130 ; either the user has edited his DEVICE = line since
6131 ; the last time the system was re-booted, or at the
6132 ; time we initially allocated this region EMM_AVAILK
6133 ; was less than DEV_SIZE and we had to trim the device
6135 ; This case is handled at INSUFF_MEM.
6136 ; IF the next EMM_REC structure is not allocated
6137 ; IF EMM_AVAILK == 0
6138 ; We can't do anything, so set DEV_SIZE
6139 ; to EMM_KSIZE and we're done.
6141 ; allocate appropriate amount off of EMM_AVAILK
6142 ; and add it to EMM_KSIZE and we're done.
6144 ; We can't do anything, so set DEV_SIZE
6145 ; to EMM_KSIZE and we're done.
6147 ; This is the EMM_KSIZE > DEV_SIZE case, it means the
6148 ; user MUST have edited his DEVICE = line.
6149 ; IF next EMM_REC is NOT free
6150 ; We can't shrink the allocation block,
6151 ; but we'll leave DEV_SIZE set to the user
6152 ; specification and let him waste memory.
6154 ; SHRINK the allocation block by adding
6155 ; the extra memory back onto EMM_AVAILK
6156 ; and subtracting it from EMM_KSIZE and
6160 ; SECTOR_BUFFER containes POSSIBLE EMM_CTRL sector
6162 ; EXT_K is set to size of extended memory
6163 ; DEV_SIZE is set to user requested device size
6166 ; Error, message already printed
6168 ; ES:DI = BASE_ADDR for this drive from EMM_BASE of EMM_REC
6169 ; EMM_REC is marked EMM_ISDRIVER
6170 ; TRACK_BUFFER must be written out, it contains an updated
6172 ; DEV_SIZE set to TRUE size
6173 ; MY_EMM_REC is the offset in the 1k EMM_CTRL sector of the
6174 ; record we allocated.
6179 ; Specific to TYPE 1 drivers
6181 ; substancial modification to this routine, would have totally changed
6182 ; if it weren't for the olivetti memory
6184 ; we are going to be int15 guys from now except for the olivetti memory
6187 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
6190 MOV DI,OFFSET TRACK_BUFFER
6191 MOV SI,OFFSET EMM_CONTROL
6196 ; JNZ SETCTRL ; No EMM_CTRL
6197 ADD SI,EMM_TAIL_SIG
- 50
6198 ADD DI,EMM_TAIL_SIG
- 50
6202 ; JNZ SETCTRL ; No EMM_CTRL
6203 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6204 ; with int15 guys around this is not feasible
6205 ; MOV DI,OFFSET TRACK_BUFFER
6207 ; DEC AX ; Size in EMM_CTRL doesn't include EMM_CTRL
6208 ; CMP AX,[DI.EMM_TOTALK]
6209 ; JZ SCAN_DEV ; EMM_CTRL is valid
6211 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6213 ; modification sunilp
6215 dec [valid_emm
] ; signal prescence of emm record
6217 cmp [u_switch
],0h
; is it a u driver
6218 jne old_st
; if not go to install acc to new int15
6219 jmp new_st
; standard
6221 ; for olivetti u memory we still have to install according to ol' microsoft st
6224 cmp [valid_emm
],0h
; do we have a valid emm
6225 jne scan_dev
; if yes go to scan structures
6226 set_ctrl: ; else we have to install a new one
6227 MOV DI,OFFSET TRACK_BUFFER
6229 MOV SI,OFFSET EMM_CONTROL
6231 REP MOVSW ; Move in initial EMM_CTRL
6234 DEC AX ; Size in EMM_CTRL doesn't include EMM_CTRL
6235 MOV [DI.EMM_TOTALK
],AX
6236 MOV [DI.EMM_AVAILK
],AX
6238 MOV SI,OFFSET TRACK_BUFFER
; DS:SI points to EMM_CTRL
6240 ADD DI,EMM_RECORD
; DS:DI points to EMM records
6243 TEST [DI.EMM_FLAGS
],EMM_ALLOC
6245 JMP GOT_FREE_REC
; Must alloc new region
6248 CMP [DI.EMM_SYSTEM
],EMM_APPLICATION
6249 JNZ NEXTREC
; Not correct type
6250 TEST [DI.EMM_FLAGS
],EMM_ISDRIVER
6251 JNZ NEXTRECI
; Driver already in
6252 CALL SET_RESET
; Set up INT 19,9
6253 MOV AX,[DI.EMM_KSIZE
]
6255 JBE OK_SET_DEV
; If this is last record, must
6258 JZ OK_SET_DEV
; Exact match, Okay
6259 JB INSUFF_MEM
; User asked for more
6260 ; Size of found block is bigger than requested size.
6261 ; User MUST have edited CONFIG.SYS.
6264 TEST [DI.EMM_FLAGS
],EMM_ALLOC
6266 JZ SHRINK_BLOCK
; Next block is free, shrink
6271 SUB AX,[DEV_SIZE
] ; AX is amount to shrink
6272 ADD [SI.EMM_AVAILK
],AX
6274 MOV [DI.EMM_KSIZE
],AX
6277 INSUFF_MEM: ; Size of found block is smaller
6278 ; than requested size.
6281 TEST [DI.EMM_FLAGS
],EMM_ALLOC
6283 JNZ OK_SET_DEV
; Next block is NOT free, can't grow
6285 CMP [SI.EMM_AVAILK
],0
6286 JZ OK_SET_DEV
; Need SPECIAL check for this case
6288 NEG AX ; AX is amount we would like to grow
6289 SUB [SI.EMM_AVAILK
],AX
6291 ADD AX,[SI.EMM_AVAILK
] ; AX is MAX we can grow
6292 MOV [SI.EMM_AVAILK
],0 ; We take all that's left
6294 ADD [DI.EMM_KSIZE
],AX
6295 MOV AX,[DI.EMM_KSIZE
]
6299 OR [DI.EMM_FLAGS
],EMM_ISDRIVER
6301 SUB [MY_EMM_REC
],OFFSET TRACK_BUFFER
; Make start of EMM_CTRL relative
6302 LES DI,[DI.EMM_BASE
]
6303 XOR AX,AX ; Set zero, clear carry
6308 ADD DI,SIZE EMM_REC
; Next record
6311 MOV DX,OFFSET ERRMSG2
6320 MOV AX,[SI.EMM_AVAILK
]
6322 JB VERROR
; 16K is smallest device
6323 CALL SET_RESET
; Set INT 19,9
6325 JBE GOTSIZE
; Not enough for user spec
6326 MOV AX,[DEV_SIZE
] ; User size is OK
6329 SUB [SI.EMM_AVAILK
],AX
6330 MOV [DI.EMM_KSIZE
],AX
6331 MOV [DI.EMM_SYSTEM
],EMM_APPLICATION
6332 MOV [DI.EMM_FLAGS
],EMM_ALLOC
+ EMM_ISDRIVER
6334 SUB [MY_EMM_REC
],OFFSET TRACK_BUFFER
; Make start of EMM_CTRL relative
6336 SUB DI,SIZE EMM_REC
; Look at prev record to compute base
6337 MOV AX,[DI.EMM_KSIZE
]
6338 LES BX,[DI.EMM_BASE
]
6339 MOV DI,ES ; DI:BX is prev base
6341 MUL CX ; Mult size by 1024 to get # bytes
6342 ADD AX,BX ; Add size onto base to get next base
6345 MOV WORD PTR [DI.EMM_BASE
],AX
6346 MOV WORD PTR [DI.EMM_BASE
+ 2],DX
6347 LES DI,[DI.EMM_BASE
]
6348 XOR AX,AX ; Set zero, clear carry
6352 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6353 ; the new int15 standard
6356 mov bx,[ext_k
] ; contiguous memory reported by int15
6357 cmp [valid_emm
],0 ; is there a valid emm record
6358 je no_adjust
; if not there no need to adjust
6359 ; the memory available
6360 ; else we have to find how much memory is already allocated by the microsoft
6361 ; emm control block and subtract this from the amount that is available. the
6362 ; memory allocated is totalk - availk + 1
6364 sub bx,1 ; subtract the emm ctrl record size
6365 mov di,offset track_buffer
; set up to address the ctrl record
6367 mov ax,[di.emm_totalk
] ; ax <- totalk
6368 sub ax,[di.emm_availk
] ; ax <- totalk - availk
6369 sub bx,ax ; adjust memory available
6370 jc verror
; if no memory go to abort
6372 cmp bx,128 ; is it the minimum required
6373 jb verror
; if less go to abort
6375 ; the memory available has been found and is in bx. now compare it with
6376 ; requested device size and take the minimum of the two
6380 jb skip_adj_dev_size
; if enough space we don't need to adj
6382 mov [dev_size
],bx ; else we have compromise on dev size
6385 ; now that we have the correct dev size we should proceed with the installation
6386 ; of a new int 15 handler which will account for the memory grabbed by this guy
6388 mov bx,[ext_k
] ; get memory which was reported by int15
6389 add bx,[special_mem
] ; account for olivetti guys
6391 mov [int15_size
],bx ; this is the size thaat will be reported
6392 ; by the int 15 handler
6393 ; now install the int15 handler
6399 mov ax,(get_interrupt_vector
shl 8) or 15h
6401 mov word ptr [old_15
],bx
6402 mov word ptr [old_15
+2],es
6403 mov dx,offset int_15
6404 mov ax,(set_interrupt_vector
shl 8) or 15h
6411 ; set up int19 vector
6415 ; now fill device base address in es:di
6418 sub ax,[dev_size
] ; this now has memory left
6419 mov cx,1024 ; we are going to find size in bytes
6420 mul cx ; dx:ax = ax * 1024
6421 add ax,word ptr [base_addr
] ;
6422 adc dx,word ptr [base_addr
+2] ;
6427 ;** SET_RESET - Set up INT 19/INT 9 vectors
6429 ; This routine will install the INT 9 and INT 19
6430 ; code by saving the current INT 9 and INT 19
6431 ; vectors in OLD_9 and OLD_19 (NOTE: the change in the value of OLD_19
6432 ; to something other than -1 indicates that the vectors have been
6433 ; replaced), setting the vectors to point to INT_9 and INT_19.
6442 ; COMMON TO TYPE 1, 2 drivers
6446 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
6447 cmp U_SWITCH
,0 ;; don't do this for at&t 6300 plus
6453 MOV AX,(Get_Interrupt_Vector
SHL 8) OR 19H
6455 MOV WORD PTR [OLD_19
],BX
6456 MOV WORD PTR [OLD_19
+ 2],ES
6457 MOV DX,OFFSET INT_19
6458 MOV AX,(Set_Interrupt_Vector
SHL 8) OR 19H
6460 MOV AX,(Get_Interrupt_Vector
SHL 8) OR 9H
6462 MOV WORD PTR [OLD_9
],BX
6463 MOV WORD PTR [OLD_9
+ 2],ES
6464 ; MOV DX,OFFSET INT_9
6465 ; MOV AX,(Set_Interrupt_Vector SHL 8) OR 9H
6474 BREAK </E INIT
Code>
6476 ;** AT_EXT_INIT - Perform /E (TYPE 1) specific initialization
6478 ; This code does the drive TYPE specific initialization for TYPE 1
6481 ; Make sure running on 80286 IBM PC-AT compatible system by
6482 ; making sure the model byte at FFFF:000E is FC.
6483 ; Get the size of extended memory by using 8800H call to INT 15.
6484 ; and make sure it is big enough to accomodate thr driver.
6485 ; Limit DEV_SIZE to the available memory found in the previous step
6486 ; by making DEV_SIZE smaller if necessary.
6487 ; Initialize the GLOBAL parts of the LOADALL information which
6488 ; are not set by each call to BLKMOV.
6489 ; CALL MM_SETDRIVE to look for EMM_CTRL and perform all the
6490 ; other initialization tasks.
6491 ; Call DRIVEPARMS to set TERM_ADDR and other drive specific cache parms
6494 ; Invokation line parameter values set.
6497 ; Error, message already printed. Driver not installed.
6498 ; EMM_CTRL not marked (but MAY be initialized if
6499 ; a valid one was not found).
6501 ; BASE_ADDR set for this drive from EMM_BASE of EMM_REC
6502 ; BASE_RESET set from BASE_ADDR
6504 ; EMM_REC is marked EMM_ISDRIVER
6506 ; DEV_SIZE set to TRUE size
6507 ; RESET_SYSTEM code and INT 9/INT 19 code included,
6508 ; INT 19 and 9 vector patched.
6513 ; Code is specific to TYPE 1 driver
6517 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
6519 call sys_det
; new routine to do more comprehensive
6521 jnc at001
; checks than before
6522 MOV DX,OFFSET BAD_AT
6530 ;; If upper extended memory is used on the PLUS, it is necessary to
6531 ;; patch the values of base_reset and base_addr to get the addressing right.
6533 cmp [U_SWITCH
],0 ;; patch the code for /U option
6536 mov word ptr [emm_ctrl_addr
+2],ax ;; in resident part for reset code
6537 mov word ptr [base_reset
+2],ax ;; patching upper address
6538 mov word ptr [base_addr
+2],ax ;; to FA from 10
6541 INT 15H
; Get extended memory size
6542 MOV DX,OFFSET NO_MEM
6546 ;; If running on a 6300 PLUS, it is necessary to subtract any upper extended
6547 ;; memory from the value obtained by int 15 to determine the correct memory
6548 ;; available for a type /E RAMDrive. If loading a /U RAMDrive, it is necessary
6549 ;; to find out if there IS any upper extended memory.
6551 cmp [U_SWITCH
],0 ;; did we ask for upper extended memory
6553 call UpperMemCheck
;; yes, see if anything there
6554 jc ERR_RET
;; no, quit
6555 mov ax,384 ;; yes, but max allowed is 384K
6558 cmp [S5_FLAG
],S_OLIVETTI
;; if not 6300 PLUS, go on
6560 call UpperMemCheck
;; yes, see if 384K is there
6561 jc at001b
;; no, so int 15h is right
6562 sub ax,384 ;; yes, subtract 384K
6563 mov [special_mem
],384 ;; store special memory size
6566 MOV DX,OFFSET ERRMSG2
6567 CMP AX,128 ; 128k min cache
6571 ; DEC BX ; BX is MAX possible cache size
6573 JBE AT002
; DEV_SIZE OK
6574 MOV [DEV_SIZE
],BX ; Limit DEV_SIZE to available
6576 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6578 test [sys_flg
],M_386
6581 mov word ptr [cod_seg
],ax
6585 mov si,offset cs_des
6586 mov [si].bas_0_15
,ax
6587 mov [si].bas_16_23
,dl
6588 mov [si].bas_24_31
,dh
6590 mov si,offset emm_gdt
6591 add ax,offset start_gdt
6593 mov [si].gdt_base_0
,ax
6594 mov [si].gdt_base_2
,dx
6595 jmp short common_setup
6597 ; Init various pieces of LOADALL info
6600 ;;;; SIDT QWORD PTR [IDTDES]
6601 ;;;; SGDT QWORD PTR [GDTDES]
6603 ;;;; ; NOW The damn SXXX instructions store the desriptors in a
6604 ;;;; ; different order than LOADALL wants
6606 ;;;; MOV SI,OFFSET IDTDES
6607 ;;;; CALL FIX_DESCRIPTOR
6608 ;;;; MOV SI,OFFSET GDTDES
6609 ;;;; CALL FIX_DESCRIPTOR
6625 ;;* UpperMemCheck - Called by 6300 PLUS to verify existence of
6626 ;; upper extended memory of 384K at FA0000h
6628 ;; Returns carry set if no upper extended memory.
6630 ;; This routine is called only by a 6300 PLUS, and
6631 ;; it reads the hardware switch DSW2 to do the job.
6646 BREAK </A INIT
Code>
6648 ;** EMM device driver name
6650 ; The following datum defines the Above Board EMM 8 character
6651 ; device driver name that is looked for as part of TYPE 2
6652 ; specific initialization.
6654 ; This datum is specific to TYPE 2 drivers
6657 ABOVE_DEV_NAME
DB "EMMXXXX0"
6659 ;** ABOVE_INIT - Perform /A (TYPE 2) specific initialization
6661 ; This code performes the driver specific initialization for
6664 ; Swap ABOVE_BLKMOV code in for TYPE 1 code at BLKMOV
6665 ; Swap ABOVE_RESET code in for TYPE 1 code at RESET_SYSTEM
6666 ; Check to make sure EMM Above Board device driver is installed
6667 ; by looking for device name relative to INT 67H segment
6668 ; address. This is method 2 described on page 36 and 37
6669 ; of the Expanded Memory Manager Programming Specification.
6671 ; WARNING! If run on a version of DOS where all INT vectors
6672 ; are managed by the kernel, or on a system where some
6673 ; foreign program (not EMM.SYS) is also using INT 67H, this
6674 ; method will fail to find the EMM device driver.
6675 ; The reason this method was used rather than the more portable
6676 ; method 1 described on pages 33 and 34 of the EMM Programming
6677 ; Specification is that the DOS Installable Device Driver
6678 ; document makes a statement about which DOS system calls
6679 ; may be made in a device initialization routine, and
6680 ; OPEN, IOCTL, and CLOSE are not included in the allowed
6681 ; set. Adherance to the Installable Device Driver document,
6682 ; therefore, excludes the use of method 1.
6684 ; Check the EMM device status
6685 ; Get the EMM map window address and set BASE_ADDR
6686 ; Get the available Above Board memory
6687 ; Adjust DEV_SIZE to be consistent with the available memory if needed,
6688 ; and also round DEV_SIZE up so that it is a multiple of the 16K
6689 ; granularity of the Above Board memory.
6690 ; Allocate DEV_SIZE worth of Above Board memory and set ABOVE_PID.
6691 ; After this point we can use CTRL_IO and/or BLKMOV to
6692 ; read/write the memory we have allocated.
6693 ; Install the INT 9 and INT 19 code by calling SET_RESET.
6694 ; Call DRIVEPARMS to set TERM_ADDR and other drive specific cache parms
6697 ; INTEL Expanded Memory Manager Programming Specification
6700 ; Invokation line parameter values set.
6702 ; ABOVE_BLKMOV code swapped in at BLKMOV
6703 ; ABOVE_RESET code swapped in at RESET_SYSTEM
6705 ; Error, message already printed. Driver not installed.
6706 ; No Above Board memory allocated.
6708 ; BASE_ADDR set to segment address of Above Board map window
6709 ; ABOVE_PID contains PID of allocated above board memory
6710 ; DEV_SIZE set to TRUE size
6716 ; Code is specific to TYPE 2 driver
6720 ASSUME
DS:INT13CODE
,ES:NOTHING
,SS:NOTHING
6722 ; Swap above code into place
6726 MOV SI,OFFSET ABOVE_CODE
6727 MOV DI,OFFSET DRIVE_CODE
6728 MOV CX,OFFSET DRIVE_END
- OFFSET DRIVE_CODE
6730 MOV SI,OFFSET ABOVE_RESET
6731 MOV DI,OFFSET RESET_SYSTEM
6732 MOV CX,OFFSET RESET_INCLUDE
- OFFSET RESET_SYSTEM
6735 ; Check for presence of Above board memory manager
6737 MOV AX,(Get_Interrupt_Vector
SHL 8) OR 67H
6740 MOV SI,OFFSET ABOVE_DEV_NAME
6744 MOV DX,OFFSET NO_ABOVE
6752 ; Check memory status
6758 CMP AH,ABOVE_SUCCESSFUL
6760 CMP AH,ABOVE_ERROR_BUSY
6763 MOV DX,OFFSET BAD_ABOVE
6768 ; Get base address of map region and set BASE_ADDR
6770 MOV AH,ABOVE_GET_SEG
6772 CMP AH,ABOVE_ERROR_BUSY
6774 CMP AH,ABOVE_SUCCESSFUL
6776 MOV WORD PTR [BASE_ADDR
],0
6777 MOV WORD PTR [BASE_ADDR
+ 2],BX
6779 ; Allocate drive memory
6782 MOV AH,ABOVE_GET_FREE
6784 CMP AH,ABOVE_ERROR_BUSY
6786 CMP AH,ABOVE_SUCCESSFUL
6788 MOV AX,DX ; AX is total 16K pages
6789 ; BX is un-allocated 16K pages
6790 MOV DX,OFFSET NO_MEM
6793 MOV DX,OFFSET ERRMSG2
6795 ; change in allocation strategy new default is all of available pages
6798 ; algorithm: if (free_pages < 8) then error();
6800 ; if (free_pages > 200h) then free_pages = 200h;
6801 ; if (num_arg == 1) then dev_size = free_pages;
6802 ; else dev_size = min (dev_size,free_pages)
6806 CMP BX,8 ; 128K = 16K * 8 = Min cache size
6808 cmp bx,0200h ; 8192K = Max cache size
6809 jbe ab0$1
; if less or equal fine
6810 mov bx,0200h ; else limit it to 8192K
6812 mov cx,4 ; to convert number of pages into no of k
6814 cmp [num_arg
],1 ; is numeric argument 1 ( means none )
6815 jne ab0$2
; cache size has been requested
6816 mov [dev_size
],bx ; else use all of available cache
6819 cmp [dev_size
],bx ; minimum of dev size and bx
6826 mov [current_dev_size
],bx ; Initialize current device size
6828 ; BX is K we want to allocate (limited by available K)
6832 MOV CX,4 ; Convert back to # of 16K pages
6834 TEST AX,0FH ; Even????
6836 INC BX ; Gotta round up
6840 MOV [DEV_SIZE
],BX ; Correct dev size too by rounding it up to
6841 ; next multiple of 16K, no sense wasting
6844 mov [current_dev_size
],bx ; Correct current device size also
6850 CMP AH,ABOVE_ERROR_BUSY
6852 CMP AH,ABOVE_SUCCESSFUL
6854 CMP AH,ABOVE_ERROR_MAP_CNTXT
6856 CMP AH,ABOVE_ERROR_OUT_OF_PIDS
6858 MOV DX,OFFSET ERRMSG2
6867 ; INSTALL ABOVE RESET handler
6871 ; We are now in good shape.
6880 BREAK <Drive
code for
/A driver
. Swapped
in at BLKMOV
>
6883 ; This label defines the start of the code swapped in at DRIVE_CODE
6885 ABOVE_CODE
LABEL WORD
6888 ; WARNING DANGER!!!!!!!
6890 ; This code is tranfered over the /E driver code at DRIVE_CODE
6892 ; ALL jmps etc. must be IP relative.
6893 ; ALL data references must be to cells at the FINAL, TRUE location
6894 ; (no data cells may be named HERE, must be named up at BLKMOV).
6895 ; OFFSET of ABOVE_BLKMOV relative to ABOVE_CODE MUST be the same as
6896 ; the OFFSET of BLKMOV relative to DRIVE_CODE.
6897 ; SIZE of stuff between ABOVE_CODE and ABOVE_END MUST be less than
6898 ; or equal to size of stuff between DRIVE_CODE and DRIVE_END.
6901 IF((OFFSET ABOVE_BLKMOV
- OFFSET ABOVE_CODE
) NE
(OFFSET BLKMOV
- OFFSET DRIVE_CODE
))
6902 %
out ERROR BLKMOV
, ABOVE_BLKMOV
NOT ALIGNED
6904 IF((OFFSET ABOVE_END
- OFFSET ABOVE_CODE
) GT
(OFFSET DRIVE_END
- OFFSET DRIVE_CODE
))
6905 %
out ERROR ABOVE
CODE TOO BIG
6909 DD ?
; 24 bit address of start of this RAMDRV
6910 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6911 ;** ABOVE_BLKMOV - Perform transfer for TYPE 2 driver
6913 ; This routine is the transfer routine for moving bytes
6914 ; to and from the Above Board memory containing the cache.
6916 ; The Above Board is implemented as 4 16K windows into the Above
6917 ; Board memory, giving a total window of 64K wich starts on some
6918 ; 16K boundary of the Above Board memory. Given that a DOS I/O
6919 ; request is up to 64K bytes starting on some sector boundary,
6920 ; the most general I/O picture is:
6922 ; |------------|------------|------------|------------|------------|
6923 ; | Above Brd | Above Brd | Above Brd | Above Brd | Above Brd |
6924 ; |Log page n |Log page n+1|Log page n+2|log page n+3|Log page n+4|
6925 ; |------------|------------|------------|------------|------------|
6927 ; | | |---------------- 64K bytes of sectors -------------|
6929 ; offset|------------------|------------------------| |
6930 ; of first| Number of words in | |
6931 ; byte of | first part of I/O that |---|---|
6932 ; I/O in | can be performed once Number
6933 ; first | logical pages n - n+3 of words
6934 ; Log page| are mapped into physical in tail
6935 ; | pages 0 - 3 part of I/O
6936 ; Location of that have
6937 ; first byte to be done
6938 ; of sector M, once logical
6939 ; the start sector page n+4 is
6940 ; of the I/O mapped into
6944 ; One or both of "Byte offset of first byte of I/O in first page" and
6945 ; "Number of words in tail part of I/O" may be zero depending on the
6946 ; size of the I/O and its start offset in the first logical page it is
6949 ; WARNING: IF A PRE-EMPTIVE MULTITASKING SYSTEM SCHEDULES A TASK WHICH
6950 ; IS USING THE ABOVE BOARD DURING THE TIME THIS DRIVER IS IN THE
6951 ; MIDDLE OF PERFORMING AN I/O, THE SYSTEM HAD BETTER MANAGE THE A
6952 ; BOARD MAPPING CONTEXT CORRECTLY OR ALL SORTS OF STRANGE UNPLEASANT
6953 ; THINGS WILL OCCUR.
6956 ; INTEL Expanded Memory Manager Programming Specification
6959 ; ES:DI is packet transfer address.
6960 ; CX is number of words to transfer.
6961 ; DX:AX is 32 bit start byte offset (0 = start of cache)
6962 ; BH is 1 for WRITE, 0 for READ
6964 ; BASE_ADDR set to point to Above Board mapping window in main memory
6965 ; This "input" is not the responsibility of the caller. It
6966 ; is up to the initialization code to set it up when the
6967 ; device is installed
6971 ; OK, operation performed successfully
6973 ; Error during operation, AL is error number
6978 ; This routine is specific to TYPE 2 driver
6981 assume
ds:int13code
,es:nothing
,ss:nothing
6983 ; save mapping context and return with error if save fails
6985 save_mapping_context
6989 ; find logical page number, offset of i/o in first page
6993 mov cx,1024*16 ; 16k bytes / page
6994 div cx ; dx:ax / 16k --> log page numb in ax
6995 ; --> offset of i/o in dx
6996 mov si,dx ; transfer offset to si
6997 mov dx,ax ; store the page number in dx
7000 ; find case and dispatch accordingly
7002 ; case 0 : user buffer below page map, can use aaron's code
7003 ; case 1 : user buffer above page map, can use aaron's code
7004 ; case 2 : user buffer totally within page map, use pai's code
7005 ; case 3 : user buffer partly in page map partly below, error
7006 ; case 4 : user buffer partly in page map partly above, error
7011 ; if( final_user_off < pm_base_addr ) then case 0
7013 mov ax,di ; get user buffer initial offset into ax
7014 shr ax,1 ; convert to word offset
7015 dec cx ; convert word count to 0 based number
7016 add ax,cx ; user buffer final word offset
7017 shr ax,1 ; convert to segment
7020 mov bx,es ; get segment of buffer
7021 add ax,bx ; now we have the segment of the user buffer
7023 sub ax,word ptr [base_addr
+2] ; compare against page map
7024 jc aar_cd
; if below page map then execute old code
7026 ; if( initial_user_off < pm_base_addr ) then error
7029 mov bp,di ; get initial offset in bp
7032 sub bp,word ptr [base_addr
+2]
7035 ; if ( initial_user_off >= pm_end_addr ) then case1
7040 ; if ( final_addr >= pm_end_addr ) then error
7047 within_pm: jmp new_code
; user buffer in page map
7048 ; so we need to execute new code
7051 mov al,0bbh ; general failure
7053 jmp short REST_CONT
; RESTORE CONTEXT!!!
7058 ; Referring back to the diagram given above the following routine is
7059 ; to take care of transfer of the most general case.
7060 ; What this routine does is break every I/O down into the above parts.
7061 ; The first or main part of the I/O is performed by mapping 1 to 4
7062 ; sequential logical pages into the 4 physical pages and executing one
7063 ; REP MOVSW. If the tail word count is non-zero then the fith sequential
7064 ; logical page is mapped into physical page 0 and another REP MOVSW is
7068 ; Break I/O down as described above into main piece and tail piece
7069 ; Map the appropriate number of sequential pages (up to 4)
7070 ; into the page window at BASE_ADDR to set up the main piece
7072 ; Set appropriate seg and index registers and CX to perform the
7073 ; main piece of the I/O into the page window
7075 ; IF there is a tail piece
7076 ; Map the next logical page into physical page 0
7077 ; Reset the appropriate index register to point at phsical page 0
7078 ; Move tail piece word count into CX
7080 ; Restore Above Board page mapping context
7082 XOR BP,BP ; No tail page
7085 ; DX is first page #, SI is byte offset of start of I/O in first page
7089 SHR BX,1 ; # Words in first 16k page which are not part
7092 ADD BX,CX ; # of words we need to map to perform I/O
7094 AND DX,1FFFH
; DX is number of words to transfer last page
7095 ; remainder of div by words in 16K bytes
7096 MOV CL,13 ; Div by # words in 16K
7097 SHR BX,CL ; BX is number of pages to map (may need round up)
7098 OR DX,DX ; Remainder?
7100 INC BX ; Need one more page
7102 MOV CX,BX ; CX is total pages we need to map
7103 MOV BX,AX ; BX is first logical page
7104 CMP CX,4 ; We can map up to 4 pages
7106 MOV BP,DX ; Words to move in tail page saved in BP
7107 DEC CX ; Need second map for the 5th page
7109 SUB AX,DX ; Words to move in first 4 pages is input
7110 ; word count minus words in tail page
7111 PUSH AX ; Count for first mapping back on stack
7115 MOV AX,ABOVE_MAP
SHL 8 ; Physical page 0
7118 POP AX ; Recover correct AX register
7122 INT 67H
; Damn call ABOVE_MAP zaps BX,DX,AX
7126 JNZ MAP_ERR1
; error
7128 IF (ABOVE_SUCCESSFUL
)
7129 %
out ASSUMPTION
IN CODE THAT ABOVE_SUCCESSFUL
= 0 IS INVALID
7133 INC BX ; Next logical page
7135 INC AL ; Next physical page
7138 POP AX ; Clean stack
7139 POP CX ; Word count for first page mapping
7140 POP AX ; Operation in AH
7142 ; BX has # of next logical page (Tail page if BP is non-zero)
7143 ; BP has # of words to move in tail page (0 if no tail)
7144 ; CX has # of words to move in current mapping
7145 ; SI is offset into current mapping of start of I/O
7146 ; AH indicates READ or WRITE
7148 PUSH AX ; Save op for possible second I/O
7156 MOV DI,SI ; Start page offset to DI
7157 POP SI ; DS:SI is transfer addr
7160 MOV ES,WORD PTR [BASE_ADDR
+ 2] ; ES:DI -> start
7161 JMP SHORT FIRST_MOVE
7165 MOV DS,WORD PTR [BASE_ADDR
+ 2] ; DS:SI -> start
7175 ; Restore page mapping context
7176 PUSH AX ; Save possible error code
7177 PUSHF ; And carry state
7180 MOV AH,ABOVE_RESTORE_MAP_PID
7185 IF (ABOVE_SUCCESSFUL
)
7186 %
out ASSUMPTION
IN CODE THAT ABOVE_SUCCESSFUL
= 0 IS INVALID
7189 CMP AH,ABOVE_ERROR_BUSY
7191 CMP AH,ABOVE_ERROR_NO_CNTXT
7192 JZ ROK
; Ignore the invalid PID error
7194 POP DX ; Clean stack
7195 MOV AL,0BBH ; General failure
7200 POPF ; Recover carry state
7201 POP AX ; and possible error code
7207 MOV AX,ABOVE_MAP
SHL 8 ; map logical page BX to phys page 0
7210 INT 67H
; Damn call ABOVE_MAP zaps BX,DX,AX
7214 JNZ MAP_ERR2
; Error
7216 IF (ABOVE_SUCCESSFUL
)
7217 %
out ASSUMPTION
IN CODE THAT ABOVE_SUCCESSFUL
= 0 IS INVALID
7221 POP AX ; Recover Op type
7228 XOR DI,DI ; ES:DI -> start of tail
7232 XOR SI,SI ; DS:SI -> start of tail
7239 CMP AH,ABOVE_ERROR_BUSY
; Busy?
7240 JZ MAP_NEXT
; Yes, wait till not busy (INTs are ON)
7241 ADD SP,6 ; Clean stack
7245 CMP AH,ABOVE_ERROR_BUSY
7249 MOV AL,0AAH ; Drive not ready
7254 ; this code has been written to handle te cases of overlapping usage
7255 ; of the above board page frame segment by the cache and user buffer
7256 ; assumption: in dos tracks cannot be more than 64 sectors long so
7257 ; in the worst case we shall have the user buffer occupying three
7258 ; pages is the page frame. we attempt to find the page that is
7259 ; available for the cache and use it repeatedly to access the cache
7260 ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7262 ; ******************************************************
7263 ; [STEP1: determine the page we can use for the cache]
7265 ; if (initial_para_offset_user in page 1 or above ) then {
7266 ; physical_cache_page = 0;
7267 ; cache_segment = above board segment;
7270 ; physical_cache_page = 3;
7271 ; cache_segment = above_board_segment + 3*1024;
7274 ; ******************************************************
7275 ; [STEP2: initial setup]
7277 ; count = user_count_requested;
7278 ; number_to_be_transferred = min ( count, (16K - si) >> 2 );
7279 ; exchange source and destination if necessary;
7281 ; *******************************************************
7282 ; [STEP3: set up transfer and do it]
7284 ; count = count - number_to_be_transferred;
7285 ; map_page cache_handle,physical_cache_page,logical_cache_page
7288 ; *******************************************************
7289 ; [STEP4: determine if another transfer needed and setup if so]
7291 ; if ( count == 0 ) then exit;
7292 ; if ( operation == read ) then source_offset = 0;
7293 ; else dest_offset = 0;
7294 ; number_to_be_transferred = min ( count, 8*1024 );
7295 ; logical_page_number++ ;
7297 ; *******************************************************
7298 ; [STEP5: go to do next block]
7301 ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7304 assume
ds:int13code
,es:nothing
,ss:nothing
7308 ; bp : start para offset of user buffer in physical page frame
7309 ; ax : end para offset of user buffer in physical page frame
7310 ; di : transfer offset of user buffer
7311 ; es : transfer segment of user buffer
7312 ; dx : logical page number in cache
7313 ; si : offset from start in logical page number
7315 ; on stack { cx,bx } where cx = number of words, bx = read / write status
7317 ; [STEP1: finding physical cache page and page frame]
7320 ; assume is physical page 0
7322 xor al,al ; use page 0 for cache
7323 mov bx,word ptr [base_addr
+2]
7325 ; see if this assumption valid
7327 cmp bp,1024 ; is initial in page 1 or above
7328 jae ab$30
; if so or assumption is valid
7330 ; else we have to correct our assumption
7332 mov al,3 ; use page 3 for cache
7335 ; initialise page frame segment
7342 ; [STEP2: initialising transfer parameters]
7345 pop bp ; bp will have count of words left to be transferred
7346 pop bx ; read / write status
7347 push bx ; save it back again
7348 push dx ; save this too
7350 ; initially si offset into logical page, so we can only do 16*1024 - si
7355 shr cx,1 ; convert to word count
7357 ; number to be transferred is the minimum of this and the user requested
7366 ; see if write, then we have to switch source with destination
7369 je ab$32
; if read we don't have to do anything
7370 ; else we have to switch
7374 ; set direction flag so that we don't have to do it repeatedly
7378 ; [STEP3: set up transfer and do it]
7382 ; update count of words still left to be transferred after this
7386 ; map the logical page in cache to the physical page selected
7388 mov bx,dx ; get logical page into bx
7389 ; al already holds the physical page #
7391 jnc ab$34
; suceeded ?
7397 jmp short restore_mp
; and go to restore page map
7400 ; succeeded, do the transfer
7405 ; [STEP4: check if transfer done, if not set up for next block]
7406 ; [STEP5: go back to STEP3]
7411 je ab$40
; yes, go to finish up
7413 ; recover original dx and bx, increment dx and then save both again
7421 ; words to be transferred minimum of count and 8*1024 words
7423 mov cx,8*1024 ; 8k words in a page
7425 jbe ab$35
; if below or equal this is what we want
7427 mov cx,bp ; else we can transfer the whole count
7430 ; see whether cache src or dest and accordingly reset either si or di
7433 jne ab$36
; if write go to modify
7435 ; read, zero si and go back to step3
7438 jmp short ab$33
; to step 3
7441 ; write, zero di and go back to step3
7444 jmp short ab$33
; to step 3
7446 ; finishing up we have to restore the page map
7452 restore_mapping_context
7455 DW ?
; SPACE for ABOVE_PID
7458 ; This label defines the end of the code swapped in at DRIVE_CODE
7460 ABOVE_END
LABEL WORD
7462 BREAK <Drive
code for
/A driver
. Swapped
in at RESET_SYSTEM
>
7466 ; WARNING DANGER!!!!!!!
7468 ; This code is tranfered over the /E driver code at RESET_SYSTEM
7470 ; ALL jmps etc. must be IP relative.
7471 ; ALL data references must be to cells at the FINAL, TRUE location
7472 ; (no data cells may be named HERE, must be named up at RESET_SYSTEM).
7473 ; SIZE of stuff between ABOVE_RESET and ABOVE_RESET_END MUST be less than
7474 ; or equal to size of stuff between RESET_SYSTEM and RESET_INCLUDE.
7476 ; NOTE: EACH ABOVE BOARD driver has an INT 19 and 9 handler. This is
7477 ; different from /E and RESMEM in which only the first
7478 ; driver has an INT 19 and 9 handler.
7482 IF((OFFSET ABOVE_RESET_END
- OFFSET ABOVE_RESET
) GT
(OFFSET RESET_INCLUDE
- OFFSET RESET_SYSTEM
))
7483 %
out ERROR ABOVE_RESET
CODE TOO BIG
7487 ;** ABOVE_RESET perform TYPE 2 (/A) driver specific reboot code
7489 ; This code issues an ABOVE_DEALLOC call for the memory
7490 ; associated with this particular TYPE 2 cache since the
7491 ; system is being re-booted and the driver is "gone".
7500 ; This code is specific to TYPE 2 drivers
7504 ASSUME
DS:NOTHING
,ES:NOTHING
,SS:NOTHING
7509 MOV AH,ABOVE_DEALLOC
; Close PID
7511 CMP AH,ABOVE_ERROR_BUSY
7518 ; This label defines the end of the code swapped in at RESET_SYSTEM
7520 ABOVE_RESET_END
LABEL BYTE
7522 BREAK <messages
and common data>
7524 ;** Message texts and common data
7526 ; Init data. This data is disposed of after initialization.
7527 ; it is mostly texts of all of the messages
7529 ; COMMON to TYPE 1 and 2 drivers
7531 ; THIS IS THE START OF DATA SUBJECT TO TRANSLATION
7533 NO_ABOVE db "SMARTDrive : Expanded Memory Manager not present",13,10,"$"
7534 BAD_ABOVE db "SMARTDrive : Expanded Memory Status shows error",13,10,"$"
7535 BAD_AT db "SMARTDrive : Cannot run on this computer",13,10,"$"
7536 NO_MEM db "SMARTDrive : No extended memory available",13,10,"$"
7537 ERRMSG1 db "SMARTDrive : Invalid parameter",13,10,"$"
7538 ERRMSG2 db "SMARTDrive : Insufficient memory",13,10,"$"
7539 INIT_IO_ERR db "SMARTDrive : I/O error accessing cache memory",13,10,"$"
7540 NOHARD db "SMARTDrive : No hard drives on system",13,10,"$"
7541 BIGTRACK db "SMARTDrive : Too many bytes per track on hard drive",13,10,"$"
7542 BADVERMES db 13,10,"SMARTDrive : Incorrect DOS version",13,10,"$"
7545 ; This is the Int13 header message.
7547 HEADERMES db 13,10,"Microsoft SMARTDrive Disk Cache v2.10",13,10,"$"
7550 ; This is the status message used to display INT13 configuration
7553 ; STATMES1<size in K><STATMES1A|STATMES1E>STATMES2<# tracks in cache>STATMES3
7554 ; <sectors per track>STATMES4
7556 ; It is up to translator to move the message text around the numbers
7557 ; so that the message is printed correctly when translated
7559 STATMES1 db " Cache size: $"
7560 STATMES1A db "K in Expanded Memory$"
7561 STATMES1E db "K in Extended Memory$"
7562 STATMES2 db 13,10," Room for $"
7563 STATMES3 db " tracks of $"
7564 STATMES4 db " sectors each",13,10,13,10,"$"
7566 omti_msg db " OMTI controller release",13,10,"$"
7569 ;-----------------------------------------------------------------------
7571 ; END OF DATA SUBJECT TO TRANSLATION
7575 STATMES5 db "Device CS = $"
7576 STATMES6 db " decimal",13,10,"$"
7577 s5flagmsg db " = S5 flag",13,10,"$"
7578 U_msg db " = U Switch", 13,10,'$'
7581 db "This program is the property of Microsoft Corporation."
7583 INT13_END
LABEL BYTE