summaryrefslogtreecommitdiff
path: root/v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM
diff options
context:
space:
mode:
authorGravatar Mark Zbikowski2024-04-25 21:24:10 +0100
committerGravatar Microsoft Open Source2024-04-25 22:32:27 +0000
commit2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch)
tree80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM
parentMerge pull request #430 from jpbaltazar/typoptbr (diff)
downloadms-dos-main.tar.gz
ms-dos-main.tar.xz
ms-dos-main.zip
MZ is back!HEADmain
Diffstat (limited to 'v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM')
-rw-r--r--v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM7587
1 files changed, 7587 insertions, 0 deletions
diff --git a/v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM b/v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM
new file mode 100644
index 0000000..3efe7c1
--- /dev/null
+++ b/v4.0/src/DEV/SMARTDRV/SMARTDRV.ASM
@@ -0,0 +1,7587 @@
1 TITLE EXTENDED/EXPANDED MEMORY DISK CACHE
2
3PAGE 58,132
4
5;
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
9;
10;
11;; device = SMARTDRV.sys [bbbb] [/a] [/u]
12;
13; bbbb First numeric argument, if present, is memory size
14; in K bytes. Default value is 256. Min is 128. Max
15; is 8192 (8 Meg).
16;
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
28; with RAMDRive.
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.
32;
33; /a Specifies that Above Board memory is to be used. It
34; is an error if the above board device driver is not
35; present.
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
47;; for SMARTDRIVE.
48;; Note that it is an error to specify this switch
49;; if the machine is not a 6300 PLUS.
50;
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.
54;
55; MODIFICATION HISTORY
56;
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
85; problem with CEMM
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.
92;
93; SMARTDRV
94; ------
95;
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
101;
102;
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
105;; find.
106;
107; 8/31/87 SUNILP
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.
111; several bug fixes.
112;
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
116;
117; 1.02 10/22/87 SUNILP
118; Reduced statically allocated track buffer size to
119; minimum required
120;
121; 1.03 10/29/87 SUNILP
122; Changed name reported in messages to SMARTDrive
123;
124; 1/08/88 GREGH
125; Added support for OMTI controller. This code
126; is ifdef'd in with the OMTI keyword.
127;
128; 1.04 3/02/88 SUNILP
129; fix for recognition of 20MHz model 80.
130; fix for READ DASD int 13 dispatch.
131;
132; 1.05 5/13/88 SUNILP
133; fixed version checking to include dos 4.00
134;
135; 2.10 6/13/88 CHIPA Merged in these changes for HP Vectra
136; 11/20/87 RCP
137; Fixed a20 enabling/disabling problems on
138; Vectra machines.
139; 8/24/88 MRW Merged changes from Windows tree into DOS tree
140;
141
142BREAK MACRO subtitle
143 SUBTTL subtitle
144 PAGE
145ENDM
146
147.286p ; Use some 286 instructions in /E code
148
149DEBUG EQU 0
150S_OLIVETTI EQU 1 ; Flag for olivetti 6300 plus machine
151S_VECTRA EQU 2 ; Flag for HP Vectra machines
152WINDOWS_SWITCHES EQU 1 ; 1 = uses switches for windows, 0 = all switches
153;OMTI EQU 1 ; Used for code specific to the OMTI Controller
154
155MAX_HARD_FILES EQU 16 ; Max number of hardfiles our data structures support
156
157MIN_CACHE_SIZE_K EQU 128 ; Minimum size for cache in K (multiple of 16)
158
159IF1
160 IFDEF OMTI
161 %out OMTI Controller release
162 ENDIF
163ENDIF
164
165IF1
166 IF DEBUG
167 %out DEBUG VERSION!!!!!!
168 ENDIF
169ENDIF
170
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.
176;;
177BiosSeg segment at 40h ;; Used to locate 6300 PLUS reset address
178 org 00a2h
179RealLoc1 dd 0
180BiosSeg ends
181;
182R_Mode_IDT segment at 0h
183R_mode_IDT ends
184;
185.xlist
186 include devsym.asm
187 include syscall.asm
188 include mi.asm
189.list
190
191; The INT13 device driver has 2 basic configurations.
192;
193; TYPE 1 - /E configuration using PC-AT extended memory and the LOADALL
194; instruction.
195;; The /U configuration using upper extended memory on the
196;; 6300 PLUS is a special case of the type 1 configuration.
197;
198; TYPE 2 - /A configuration using Above Board memory and EMM device
199; driver.
200;
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.
203;
204; The TYPE 1 configuration uses the EMM control sector to
205; control access to the available memory
206;
207
208 include emm.asm
209
210 include loadall.asm
211
212 include above.asm
213
214 include ab_macro.asm
215
216BREAK <I/O Packet offset declarations>
217
218;
219; Define I/O packet offsets for useful values.
220;
221; SEE ALSO
222; MS-DOS Technical Reference manual section on Installable Device Drivers
223;
224
225; READ/WRITE PACKET OFFSETS
226RW_COUNT EQU WORD PTR (SIZE SRHEAD) + 5
227RW_TRANS EQU DWORD PTR (SIZE SRHEAD) + 1
228RW_START EQU WORD PTR (SIZE SRHEAD) + 7
229
230; MEDIA CHECK PACKET OFFSETS
231MCH_RETVAL EQU BYTE PTR (SIZE SRHEAD) + 1
232MCH_MEDIA EQU BYTE PTR (SIZE SRHEAD) + 0
233
234; BUILD BPB PACKET OFFSETS
235BPB_BUFFER EQU DWORD PTR (SIZE SRHEAD) + 1
236BPB_MEDIA EQU BYTE PTR (SIZE SRHEAD) + 0
237BPB_BPB EQU DWORD PTR (SIZE SRHEAD) + 5
238
239; INIT PACKET OFFSETS
240INIT_NUM EQU BYTE PTR (SIZE SRHEAD) + 0
241INIT_BREAK EQU DWORD PTR (SIZE SRHEAD) + 1
242INIT_BPB EQU DWORD PTR (SIZE SRHEAD) + 5
243INIT_DOSDEV EQU BYTE PTR (SIZE SRHEAD) + 9
244
245BREAK <Cache control structure>
246
247;
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".
266;
267; The original INT 13 caching algorithm (Aaronr) is sumarized as follows:
268;
269; If user read is in cache
270; Perform user read from cache
271; Else
272; If read is full track
273; Perform write through (if enabled)
274; Pass Read to old INT 13 handler (no cache operation)
275; Else
276; Read track into cache using old INT 13 handler
277; Perform user read out of cache
278;
279; If user write is in cache
280; If write buffering is enabled
281; Perform user write into cache
282; Else
283; Discard cache for this track
284; Pass write through to old INT 13 handler
285; Else
286; If write is full track
287; Perform write through (if enabled)
288; Pass Write to old INT 13 handler (no cache operation)
289; Else
290; If write buffering is enabled
291; Read track into cache using old INT 13 handler
292; Perform user write into cache
293; Else
294; Pass write through to old INT 13 handler
295;
296; SMARTDRV modifications:
297;
298; 1. Write through always.
299; 2. Multi track I/O capability support.
300; 3. Direct transfer between cache and user buffer address for
301; full tracks.
302;
303;
304;
305
306CACHE_CONTROL STRUC
307FWD_LRU_LNK DW ? ; Link to next CACHE_CONTROL, -1 if last
308BACK_LRU_LNK DW ? ; Link to previous CACHE_CONTROL, -1 if first
309BASE_OFFSET DD ? ; Offset releative to start of cache
310 ; memory of start of this track buffer
311TRACK_FLAGS DW ? ; Flags
312;
313; NOTE: The next two bytes are refed as a word.
314; MOV DX,WORD PTR STRC.TRACK_DRIVE
315; OR DH,80H
316; Puts the INT 13 drive in DL, and the head in DH which is correct
317; for an INT 13
318;
319TRACK_DRIVE DB ? ; INT 13 drive with high bit = 0
320TRACK_HEAD DB ? ; INT 13 head
321TRACK_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
327 ; track:
328 ; MOV CX,STRC.CACHE_CYLN
329 ; OR CL,1
330CACHE_CONTROL ENDS
331
332;
333; TRACK_FLAGS bits
334;
335TRACK_FREE EQU 0000000000000001B ; Track buffer is free
336TRACK_DIRTY EQU 0000000000000010B ; Track needs to be written
337TRACK_LOCKED EQU 0000000000000100B ; Track is locked
338
339BREAK <Device header>
340
341INT13CODE SEGMENT
342ASSUME CS:INT13CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
343
344 IF DEBUG
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
351 public Cache_hit
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
361 public region
362 public SECTRKARRAY
363 ENDIF
364
365;**
366;
367; INT13 DEVICE HEADER
368;
369; COMMON TO TYPE 1, 2 drivers
370;
371; SEE ALSO
372; MS-DOS Technical Reference manual section on
373; Installable Device Drivers
374;
375
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.*
378;;
379INT13DEV LABEL WORD
380 DW -1,-1
381DEVATS DW DEVOPCL + CharDev + DevIOCtl
382 DW STRATEGY
383 DW INT13$IN
384 DB "SMARTAAR" ;Name of device
385
386
387BREAK <Command dispatch table>
388
389;**
390;
391; This is the device driver command dispatch table.
392;
393; The first byte indicates the size of the table and therefore defines
394; which device function codes are valid.
395;
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
398; 3 is:
399; WORD at ((INT13TBL + 1) + (2 * 3))
400;
401; COMMON TO TYPE 1, 2 drivers
402;
403;
404
405INT13TBL LABEL WORD
406 DB 15 ; Max allowed command code
407 DW INT13$INIT ; Init
408 DW CMDERR ; Media check
409 DW CMDERR ; Build BPB
410 DW INT13$IOCTL_Read ; IOCTL input
411 DW CMDERR ; Read
412 DW CMDERR ; Non-des read no-wait
413 DW INT13$Read_St ; Read status
414 DW CMDERR ; Read flush
415 DW CMDERR ; Write
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
420 DW DEVEXIT ; Open
421 DW DEVEXIT ; Close
422 DW CMDERR ; Rem media?
423
424BREAK <Device Control data>
425
426STATISTICS_SIZE EQU 40
427
428DRIVER_SEL DB 0 ; 0 if /E (TYPE 1), 1 if /A (TYPE 2),
429
430DEV_SIZE DW 256 ; Size in K of the cache
431
432SECTRACK DW ? ; Sectors per track
433
434current_dev_size dw ? ; Current size in K of cache
435
436;; Data peculiar to AT&T 6300 PLUS.
437
438S5_FLAG DB 0 ;; = S_OLIVETTI if 6300 plus machine
439 ;; = S_VECTRA if HP Vectra machine
440
441 db ? ; Spacer
442
443A20On dw 0DF90h
444A20Off dw 0DD00h
445
446special_mem dw 0
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.
457;
458; The system flag bit assignments are:
459;
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 ?)
469;
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.
472;
473sys_flg db ?
474;
475; equates used for the system flag
476;
477M_286 equ 00000001B
478M_386 equ 00000010B
479M_PS2 equ 00000100B
480M_OLI equ 00001000B
481A20_ST equ 00010000B
482ifdef OMTI
483OMTI_EXT equ 00100000B
484endif
485
486;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
487; flag to indicate that reset code is being executed
488reboot_flg db 0
489;
490;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
491; emm ctrl address, needed for the reset code
492emm_ctrl_addr dw EXTMEM_LOW
493 dw EXTMEM_HIGH
494;
495;
496;
497;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
498; A20 address line state determination addresses
499;
500low_mem label dword
501 dw 20h*4
502 dw 0
503
504high_mem label dword
505 dw 20h*4 + 10h
506 dw 0ffffh
507
508;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
509; A20 PS2 equates
510;
511PS2_PORTA equ 0092h
512GATE_A20 equ 010b
513
514;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
515; 386 working areas
516start_gdt label byte
517nul_des desc <>
518cs_des desc <0FFFFh,0,0,09Fh,0,0>
519ss_des desc <0FFFFh,0,0,093h,0,0>
520ds_des desc <0FFFFh,0,0,093h,0,0>
521es_des desc <0FFFFh,0,0,093h,0,0>
522end_gdt label byte
523
524emm_gdt gdt_descriptor <end_gdt-start_gdt,0,0>
525;
526; int 15 gdt
527;
528int15_gdt label byte
529 desc <> ;dummy descriptor
530 desc <> ;descriptor for gdt itself
531src desc <0ffffh,,,93h,,>
532tgt desc <0ffffh,,,93h,,>
533 desc <> ;bios cs descriptor
534 desc <> ;stack segment descriptor
535
536;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
537; 0 if device is valid
538; Non-0 if device install failed (device non-functional)
539;
540; We need this state because there is no way to "un-install"
541; a character device as there is with block devices
542;
543NULDEV DB 0
544
545;
546; 0 if caching is off
547; Non-0 if caching is on
548;
549ENABLE_13 DB 1 ; 0 for debug
550
551;
552; 0 if no write through
553; Non-0 if write through
554;
555WRITE_THROUGH DB 0
556
557;
558; 0 if no write buffering
559; Non-0 if write buffering enabled
560;
561WRITE_BUFF DB 0
562
563;
564; 0 if cache is unlocked
565; Non-0 if cache is locked
566;
567LOCK_CACHE DB 0
568
569;
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
572;
573ALL_CACHE DB 1
574
575;
576; 0 if reboot flush is disabled
577; Non-0 if reboot flush is enabled
578;
579REBOOT_FLUSH DB 0
580
581;
582; An exclusion sem so that the INT 13 handler and the timer interact
583; without re-entrancy problems
584;
585INT_13_BUSY DB 0 ; Exclusion sem
586
587 EVEN ; Force word data to word align
588;
589; Statistics counters
590;
591; WARNING!!!! Do not disturb the order of these!!!! See IOCTL_READ code.
592;
593TOTAL_WRITES DD 0
594WRITE_HITS DD 0
595TOTAL_READS DD 0
596READ_HITS DD 0
597TTRACKS DW ? ; Total number of track buffers that fit
598 ; in DEV_SIZE K (number of cache elements)
599TOTAL_USED DW 0
600TOTAL_LOCKED DW 0
601TOTAL_DIRTY DW 0
602
603;
604; Tick counters
605;
606TICK_SETTING DW 1092 ; Approx 1 minute
607TICK_CNT DW 1092
608
609;
610; Non-zero if there are dirty buffers in the cache
611;
612DIRTY_CACHE DW 0 ; 0 if no dirty elements in cache
613
614BREAK <Common Device code>
615
616; INT13 DEVICE ENTRY POINTS - STRATEGY, INT13$IN
617;
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.
621;
622; INT13$IN uses INT13TBL to dispatch to the appropriate handler
623; for each device function. It also does standard packet
624; unpacking.
625;
626; SEE ALSO
627; MS-DOS Technical Reference manual section on
628; Installable Device Drivers
629;
630
631ASSUME CS:INT13CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
632
633PTRSAV DD 0 ; Storage location for packet addr
634
635;** STRATEGY - Device strategy routine
636;
637; Standard DOS 2.X 3.X device driver strategy routine. All it does
638; is save the packet address in PTRSAV.
639;
640; ENTRY ES:BX -> Device packet
641; EXIT NONE
642; USES NONE
643;
644; COMMON TO TYPE 1, 2 drivers
645;
646;
647
648STRATP PROC FAR
649
650STRATEGY:
651 MOV WORD PTR [PTRSAV],BX ; Save packet addr
652 MOV WORD PTR [PTRSAV+2],ES
653 RET
654
655STRATP ENDP
656
657;** INT13$IN - Device interrupt routine
658;
659; Standard DOS 2.X 3.X device driver interrupt routine.
660;
661;
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
667; DS = INT13CODE
668; STACK has saved values of all regs but FLAGS
669; All function handlers must return through one of
670; the standard exit points
671; USES FLAGS
672;
673; COMMON TO TYPE 1, 2 drivers
674;
675;
676
677INT13$IN:
678 PUSH SI
679 PUSH AX
680 PUSH CX
681 PUSH DX
682 PUSH DI
683 PUSH BP
684 PUSH DS
685 PUSH ES
686 PUSH BX
687
688 LDS BX,[PTRSAV] ;GET POINTER TO I/O PACKET
689 ;
690 ; Set up registers for READ or WRITE since this is the most common case
691 ;
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
696 CMP AL,AH
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.
702
703 LES DI,DS:[BX.RW_TRANS] ; ES:DI transfer address
704
705 PUSH CS
706 POP DS
707
708ASSUME DS:INT13CODE
709
710 JMP WORD PTR [SI] ; GO DO COMMAND
711
712;** EXIT - ALL ROUTINES RETURN THROUGH ONE OF THESE PATHS
713;
714; Exit code entry points:
715;
716; SEE ALSO
717; MS-DOS Technical Reference manual section on
718; Installable Device Drivers
719;
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
723; header.
724;
725; CMDERR - Used when an invalid device command is detected
726;
727; ENTRY Stack has frame set up by INT13$IN
728; EXIT Standard Device driver with error 3
729; USES FLAGS
730;
731; ERR$CNT - Used when READ or WRITE wants to return with error code.
732; The packet RW_COUNT field is zeroed
733;
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
737; USES FLAGS
738;
739; ERR$EXIT - Used when a function other that READ or WRITE wants to
740; return an error
741;
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
745; USES FLAGS
746;
747; DEVEXIT - Used when a function wants to return with no error
748;
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
756; USES FLAGS
757;
758; ERR1 - Used when a function wants to return with a value
759; for the whole status word
760;
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
764; USES FLAGS
765;
766; COMMON TO TYPE 1, 2 drivers
767;
768;
769
770ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
771
772CMDERR:
773 MOV AL,3 ;UNKNOWN COMMAND ERROR
774 JMP SHORT ERR$EXIT
775
776ERR$CNT:
777 LDS BX,[PTRSAV]
778 MOV [BX.RW_COUNT],0 ; NO sectors transferred
779ERR$EXIT: ; Error in AL
780 MOV AH,(STERR + STDON) SHR 8 ;MARK ERROR RETURN
781 JMP SHORT ERR1
782
783EXITP PROC FAR
784
785DEVEXIT:
786 MOV AH,STDON SHR 8
787ERR1:
788 LDS BX,[PTRSAV]
789 MOV [BX.REQSTAT],AX ; Set return status
790
791 POP BX
792 POP ES
793 POP DS
794 POP BP
795 POP DI
796 POP DX
797 POP CX
798 POP AX
799 POP SI
800 RET ;RESTORE REGS AND RETURN
801EXITP ENDP
802
803;
804; The following functions are not supported at this time.
805;
806INT13$Read_St:
807ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
808INT13$Write_St:
809ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
810 JMP CMDERR
811
812BREAK <IOCTL Read function (get device control parms)>
813
814SET_ZRJ3:
815 JMP SET_ZR
816
817INT13$IOCTL_Read:
818ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
819 CLD
820 CMP CX,STATISTICS_SIZE ; Must have room to transfer data.
821 JB SET_ZRJ3 ; Not enough room from user
822 MOV [TOTAL_USED],0
823 MOV [TOTAL_LOCKED],0
824 MOV [TOTAL_DIRTY],0
825 ;
826 ; Count all occupied, dirty and locked elements
827 ;
828 MOV [INT_13_BUSY],1
829 MOV SI,[CACHE_HEAD]
830 INC SI
831NEXTCC:
832 DEC SI
833 TEST [SI.TRACK_FLAGS],TRACK_FREE
834 JNZ SKIPCC
835 INC [TOTAL_USED]
836 TEST [SI.TRACK_FLAGS],TRACK_LOCKED
837 JZ TEST_DIRTY
838 INC [TOTAL_LOCKED]
839TEST_DIRTY:
840 TEST [SI.TRACK_FLAGS],TRACK_DIRTY
841 JZ SKIPCC
842 INC [TOTAL_DIRTY]
843SKIPCC:
844 MOV SI,[SI.FWD_LRU_LNK]
845 INC SI
846 JNZ NEXTCC
847 MOV [INT_13_BUSY],0
848 MOV AL,[WRITE_THROUGH]
849 MOV AH,[WRITE_BUFF]
850 STOSW
851 MOV AL,[ENABLE_13]
852 MOV AH,[NULDEV]
853 STOSW
854 MOV AX,[TICK_SETTING]
855 STOSW
856 MOV AL,[LOCK_CACHE]
857 MOV AH,[REBOOT_FLUSH]
858 STOSW
859 MOV AL,[ALL_CACHE]
860 XOR AH,AH ; Unused currently
861 STOSW
862 MOV SI,OFFSET TOTAL_WRITES
863 MOV CX,12
864 REP MOVSW
865;
866; Transfer Above Board Information
867;
868 xor dx,dx
869 mov es:[di][0],dx
870 mov es:[di][2],dx
871 mov es:[di][4],dx
872 cmp [driver_sel],dl ; is it expanded memory?
873 jz no_ems ; no, info already set
874 mov cx,16
875 mov ax,[current_dev_size]
876 div cx
877 or dx,dx
878 jz no_remain
879 inc ax
880 xor dx,dx
881no_remain:
882 stosw
883 mov ax,[dev_size]
884 div cx
885 or dx,dx
886 jz no_remaind
887 inc ax
888no_remaind:
889 stosw
890 mov ax,MIN_CACHE_SIZE_K / 16
891 stosw
892no_ems:
893 LDS BX,[PTRSAV]
894ASSUME DS:NOTHING
895 MOV [BX.RW_COUNT],STATISTICS_SIZE ; transfer amount
896 JMP DEVEXIT
897
898BREAK <IOCTL Write functions (set device control parms and do flushes)>
899
900;
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.
905;
906IOCTLTBL DB 0Ch
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
920
921SET_ZR:
922ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
923 LDS BX,[PTRSAV]
924ASSUME DS:NOTHING
925 MOV [BX.RW_COUNT],0 ; NO bytes transferred
926 JMP DEVEXIT
927
928SET_ERR_CNT:
929ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
930 MOV AL,3 ;UNKNOWN COMMAND ERROR
931 JMP ERR$CNT
932
933INT13$IOCTL_Write:
934ASSUME 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?
940 JA SET_ERR_CNT ; No
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
946
947;** IOCTL_FLUSH -- Flush the cache, but keep the data
948;
949; ENTRY:
950; ES:DI is transfer address
951; CX is transfer count
952; EXIT:
953; Through one of the device exit paths
954; USES:
955; ALL
956;
957IOCTL_FLUSH:
958ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
959 MOV [INT_13_BUSY],1
960 CALL FLUSH_WHOLE_CACHE
961 MOV [INT_13_BUSY],0
962 JMP DEVEXIT
963
964;** IOCTL_FLUSH_INVALIDATE -- Flush the cache, and discard the data
965;
966; ENTRY:
967; ES:DI is transfer address
968; CX is transfer count
969; EXIT:
970; Through one of the device exit paths
971; USES:
972; ALL
973;
974IOCTL_FLUSH_INVALID:
975ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
976 MOV [INT_13_BUSY],1
977 CALL FLUSH_WHOLE_CACHE
978 CALL INVALIDATE_CACHE
979 MOV [INT_13_BUSY],0
980 JMP DEVEXIT
981
982;** IOCTL_DISABLE -- Disable the caching for both reads and writes
983;
984; Also flush and invalidate the cache
985;
986; ENTRY:
987; ES:DI is transfer address
988; CX is transfer count
989; EXIT:
990; Through one of the device exit paths
991; USES:
992; ALL
993;
994IOCTL_DISABLE:
995ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
996 MOV [INT_13_BUSY],1
997 CALL FLUSH_WHOLE_CACHE
998 CALL INVALIDATE_CACHE
999 MOV [ENABLE_13],0
1000 MOV [INT_13_BUSY],0
1001 JMP DEVEXIT
1002
1003;** IOCTL_ENABLE -- Enable the caching for reads (and possibly writes)
1004;
1005; ENTRY:
1006; ES:DI is transfer address
1007; CX is transfer count
1008; EXIT:
1009; Through one of the device exit paths
1010; USES:
1011; ALL
1012;
1013IOCTL_ENABLE:
1014ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1015 MOV [ENABLE_13],1
1016 JMP DEVEXIT
1017
1018;** IOCTL_WRITE_MOD -- En/Disable Write through and write caching
1019;
1020; ENTRY:
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)
1028; EXIT:
1029; Through one of the device exit paths
1030; USES:
1031; ALL
1032;
1033IOCTL_WRITE_MOD:
1034ASSUME 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
1042 DEC AL
1043 DEC AL ; 2 = 0, 3 = 1
1044 MOV [INT_13_BUSY],1
1045 MOV [WRITE_BUFF],AL
1046 CALL FLUSH_WHOLE_CACHE
1047 MOV [INT_13_BUSY],0
1048 JMP DEVEXIT
1049
1050SET_WRITE_TH:
1051 MOV [WRITE_THROUGH],AL
1052 JMP DEVEXIT
1053
1054;** IOCTL_SET_TICK -- Set tick count for auto flush
1055;
1056; ENTRY:
1057; ES:DI is transfer address
1058; CX is transfer count
1059; Second byte and third byte of data is value to set
1060; EXIT:
1061; Through one of the device exit paths
1062; USES:
1063; ALL
1064;
1065IOCTL_SET_TICK:
1066ASSUME 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
1071 JMP DEVEXIT
1072
1073SET_ZRJ:
1074 JMP SET_ZR
1075
1076;** IOCTL_LOCK -- Lock the current cache
1077;
1078; Also flush the cache
1079;
1080; ENTRY:
1081; ES:DI is transfer address
1082; CX is transfer count
1083; EXIT:
1084; Through one of the device exit paths
1085; USES:
1086; ALL
1087;
1088IOCTL_LOCK:
1089ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1090 MOV [INT_13_BUSY],1
1091 CALL FLUSH_WHOLE_CACHE
1092 MOV [LOCK_CACHE],1
1093 ;
1094 ; Lock all cache elements that have something in them
1095 ;
1096 MOV SI,[CACHE_HEAD]
1097 INC SI
1098NEXTCS:
1099 DEC SI
1100 TEST [SI.TRACK_FLAGS],TRACK_FREE
1101 JNZ SKIPCS
1102 OR [SI.TRACK_FLAGS],TRACK_LOCKED
1103SKIPCS:
1104 MOV SI,[SI.FWD_LRU_LNK]
1105 INC SI
1106 JNZ NEXTCS
1107 MOV [INT_13_BUSY],0
1108 JMP DEVEXIT
1109
1110;** IOCTL_UNLOCK -- Unlock the cache
1111;
1112; ENTRY:
1113; ES:DI is transfer address
1114; CX is transfer count
1115; EXIT:
1116; Through one of the device exit paths
1117; USES:
1118; ALL
1119;
1120IOCTL_UNLOCK:
1121ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1122 MOV [INT_13_BUSY],1
1123 MOV [LOCK_CACHE],0
1124 ;
1125 ; UnLock all cache elements
1126 ;
1127 MOV SI,[CACHE_HEAD]
1128 INC SI
1129NEXTCX:
1130 DEC SI
1131 AND [SI.TRACK_FLAGS],NOT TRACK_LOCKED
1132 MOV SI,[SI.FWD_LRU_LNK]
1133 INC SI
1134 JNZ NEXTCX
1135 MOV [INT_13_BUSY],0
1136 JMP DEVEXIT
1137
1138;** IOCTL_REBOOT -- En/Disable Reboot flush
1139;
1140; ENTRY:
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
1146; EXIT:
1147; Through one of the device exit paths
1148; USES:
1149; ALL
1150;
1151IOCTL_REBOOT:
1152ASSUME 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
1159 JMP DEVEXIT
1160
1161
1162;** IOCTL_STAT_RESET -- Reset the INT 13 statistics counters
1163;
1164; ENTRY:
1165; ES:DI is transfer address
1166; CX is transfer count
1167; EXIT:
1168; Through one of the device exit paths
1169; USES:
1170; ALL
1171;
1172IOCTL_STAT_RESET:
1173ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1174 XOR AX,AX
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
1183 JMP DEVEXIT
1184
1185;** IOCTL_ALL_CACHE -- En/Disable All cache
1186;
1187; ENTRY:
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
1193; EXIT:
1194; Through one of the device exit paths
1195; USES:
1196; ALL
1197;
1198IOCTL_ALL_CACHE:
1199ASSUME 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
1205 MOV [ALL_CACHE],AL
1206 JMP DEVEXIT
1207
1208SET_ZRJ2:
1209 JMP SET_ZR
1210
1211;** ioctl_reduce_cache_size Dynamically reduce the size of the cache
1212;
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.
1219;
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
1222; updated to disk.
1223;
1224; ENTRY:
1225; ES:DI is transfer address
1226; EXIT:
1227; Through one of the device exit paths
1228; USES:
1229; ALL
1230;
1231error_reduce_exitj:
1232 jmp error_reduce_exit
1233
1234ioctl_reduce_cache_size:
1235 assume ds:Int13Code
1236 assume es:nothing
1237 assume ss:nothing
1238
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?
1243 jnz do_it
1244 jmp devexit
1245do_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
1255;
1256; Request Reallocation of Pages from EMM
1257;
1258 push ax
1259 mov dx,word ptr [above_pid]
1260 mov ah,ABOVE_REALLOCATE_PID
1261 int 67h
1262 or ah,ah
1263 jnz real_fail
1264;
1265; Determine how many cache tracks will be lost
1266;
1267 mov bx,word ptr [sectrack] ; Init code checked for too large track
1268 mov cl,9
1269 shl bx,cl ; BX has bytes/track
1270 mov ax,word ptr [ttracks]
1271 mul bx ; AX:DX has bytes used by tracks
1272 xchg ax,si
1273 mov di,dx
1274 mov cx,1024
1275 mul cx ; AX:DX has total bytes allocated
1276 sub ax,si ; Find difference in allocated and used
1277 sbb dx,di
1278 xchg ax,si
1279 xchg dx,di
1280 pop ax ; AX still has requested reduction in K
1281 mul cx ; DX:AX has requested reduction in byte
1282
1283;
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.
1286; ChipA 14-Mar-1988
1287;
1288 cmp dx,di
1289 ja free_tracks
1290 jb no_need_to_free_tracks
1291 cmp ax,si
1292 ja free_tracks
1293no_need_to_free_tracks:
1294 sub ax,ax
1295 mov dx,ax
1296 jmp all_freed
1297
1298free_tracks:
1299 sub ax,si ; Account for part not used
1300 sbb dx,di
1301 div bx ; AX has number of tracks being "lost"
1302 or dx,dx
1303 jz no_remainder
1304 inc ax ; Add one more track if remainder
1305;
1306; Determine which cache control structures we are to remove
1307;
1308no_remainder:
1309 mov si,ax
1310 mov cx,SIZE CACHE_CONTROL
1311 mul cx
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
1320;
1321; Loop through each cache control structure, removing from LRU list
1322;
1323remove_cache_entries:
1324 mov word ptr [si].track_flags,TRACK_FREE
1325 call unlink_cache
1326 add si,SIZE CACHE_CONTROL
1327 loop remove_cache_entries
1328
1329all_freed:
1330 mov byte ptr [int_13_busy],0
1331 jmp devexit
1332
1333real_fail:
1334 pop ax
1335 mov [current_dev_size],si ; Restore former dev size
1336 mov byte ptr [int_13_busy],0
1337error_reduce_exit:
1338 mov al,0Ch ; General failure
1339 jmp err$cnt
1340
1341
1342;** ioctl_increase_cache_size Dynamically increase the size of the cache
1343;
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.
1350;
1351; ENTRY:
1352; ES:DI is transfer address
1353; EXIT:
1354; Through one of the device exit paths
1355; USES:
1356; ALL
1357;
1358error_increase_exitj:
1359 jmp error_increase_exit
1360
1361ioctl_increase_cache_size:
1362 assume ds:Int13Code
1363 assume es:nothing
1364 assume ss:nothing
1365
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
1377 mov ax, bx
1378 sub ax, si ; Correct increase
1379increase_size_ok:
1380 mov word ptr [current_dev_size],bx
1381 shr bx,cl ; BX has new cache size in pages
1382 mov [int_13_busy],1
1383;
1384; Request Reallocation of Pages from EMM
1385;
1386 push ax
1387 mov dx,[above_pid]
1388 mov ah,ABOVE_REALLOCATE_PID
1389 int 67h
1390 or ah,ah
1391 jnz realloc_fail
1392;
1393; Determine how many cache tracks will be gained
1394;
1395 mov bx,word ptr [sectrack] ; Init code checked for too large track
1396 mov cl,9
1397 shl bx,cl ; BX has bytes/track
1398 mov ax,word ptr [ttracks]
1399 mul bx ; AX:DX has bytes used by tracks
1400 xchg ax,si
1401 mov di,dx
1402 mov cx,1024
1403 mul cx ; AX:DX has total bytes allocated
1404 sub ax,si ; Find difference in allocated and used
1405 sbb dx,di
1406 xchg ax,si
1407 xchg dx,di
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
1411 adc dx,di
1412 div bx ; AX has number of tracks being "gained"
1413 or ax, ax ; DIV trashes flags
1414 jz nothing_to_gain
1415;
1416; Determine which cache control structures we are to add
1417;
1418 mov si,ax
1419 mov cx,SIZE CACHE_CONTROL
1420 mov ax,[ttracks]
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
1426;
1427; Loop through each cache control structure, adding to LRU list
1428;
1429add_cache_entries:
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
1436
1437 add si,SIZE CACHE_CONTROL
1438 loop add_cache_entries
1439nothing_to_gain:
1440 mov [int_13_busy],0
1441 jmp devexit
1442
1443realloc_fail:
1444 pop ax
1445 mov [current_dev_size],si ; Restore to original size
1446 mov [int_13_busy],0
1447error_increase_exit:
1448 mov al,0Ch ; General failure
1449 jmp err$cnt
1450
1451
1452;
1453; If the device errors out during install, we set the break address here.
1454;
1455ERROR_END LABEL BYTE
1456
1457BREAK <INT 1C (timer) handler>
1458
1459 EVEN ; Force word align
1460;
1461; Storage for the INT 1C vector BEFORE cache installed
1462;
1463OLD_1C DD ?
1464
1465;** INT_1C_HANDLER - Handler for INT 1C timer ticks
1466;
1467; ENTRY
1468; None
1469;
1470; EXIT
1471; To next 1C handler, EOI may be sent
1472;
1473; USES
1474; None
1475;
1476; SEE ALSO
1477; IBM PC TECH REF MANUAL section on INT 1C
1478; DOS PRINT utility (most of this is stolen from there)
1479;
1480INT_1C_HANDLER PROC FAR
1481ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
1482 PUSH AX
1483 DEC [TICK_CNT]
1484 JNZ CHAIN_1C
1485 CMP [INT_13_BUSY],0
1486 JZ TRY_FLSH
1487RE_TRIGGER:
1488 INC [TICK_CNT] ; Set it back to 1 so next tick triggers
1489CHAIN_1C:
1490 POP AX
1491 JMP [OLD_1C]
1492
1493TRY_FLSH:
1494 mov al,00001011b ; Select ISR in 8259
1495 out 20H,al
1496 jmp short yyyy
1497yyyy:
1498 jmp short zzzz
1499zzzz:
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?
1505 JZ SKIP_FLSH ; No
1506 STI
1507 mov al,20H
1508 out 20H,al
1509 CALL FLUSH_WHOLE_CACHE_SAV
1510SKIP_FLSH:
1511 MOV AX,[TICK_SETTING]
1512 CLI
1513 MOV [TICK_CNT],AX
1514 MOV [INT_13_BUSY],0
1515 JMP CHAIN_1C
1516
1517INT_1C_HANDLER ENDP
1518
1519BREAK <INT 13 handler>
1520
1521
1522; INT 13 stack frame
1523;
1524STACK_FRAME STRUC
1525USER_OFF DW ? ; added for user transfer address
1526USER_ES DW ?
1527USER_DS DW ?
1528USER_DI DW ?
1529USER_SI DW ?
1530USER_BP DW ?
1531 DW ?
1532USER_BX DW ?
1533USER_DX DW ?
1534USER_CX DW ?
1535USER_AX DW ?
1536USER_IP DW ?
1537USER_CS DW ?
1538USER_FL DW ?
1539STACK_FRAME ENDS
1540
1541 EVEN ; Force word align
1542;
1543; Storage for the INT 13 vector BEFORE cache installed
1544;
1545OLD_13 DD ?
1546
1547;
1548; Array of sec/track for all hardfiles on system. First element cooresponds
1549; to first hard file.
1550; Value = 0 indicates no hardfile
1551;
1552SECTRKARRAY DB MAX_HARD_FILES DUP (0)
1553;
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)
1557;
1558HDARRAY DB MAX_HARD_FILES DUP (0FFH)
1559
1560ifdef OMTI
1561OMTI_SET_CYL EQU 0EEh
1562OMTI_GET_CYL EQU 0FEh
1563OMTI_GET_REV EQU 0F9h
1564endif ;OMTI
1565;
1566; INT 13 function dispatch
1567; Addresses of routines for AH INT 13 functions
1568;
1569INT13DISPATCH 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1593;
1594; MODIFICATIONS TO DATA STRUCT TO SUPPORT MULTI-TRACK I/O
1595;
1596; sunilp
1597;
1598; extra declarations needed
1599;
1600max_hd db ? ;maximum useable head number for curr. drive
1601sect_in_trk db ? ;maximum sector number in trk for curr drive
1602bytes_in_trk dw ? ;numb of bytes in trk for current drive
1603int13err db ?
1604;
1605TRUE = 0ffH
1606FALSE = NOT TRUE
1607REG1_P = 001B
1608REG2_P = 010B
1609REG3_P = 100B
1610;
1611REG1t struc
1612START_H db ? ;start head
1613START_T dw ? ;start track
1614COUNT dw ? ;number of words
1615START_S dw ? ;start sector
1616REG1t ends
1617;
1618REG2t struc
1619 db ? ;start head
1620 dw ? ;start track
1621TRACKS db ? ;number of tracks
1622REG2t ends
1623;
1624REG3t struc
1625 db ? ;start head
1626 dw ? ;start track
1627 dw ? ;number of words
1628REG3t ends
1629;
1630REGIONt struc
1631FLAG db 0
1632REG1 db size REG1t dup(?)
1633REG2 db size REG2t dup(?)
1634REG3 db size REG3t dup(?)
1635REGIONt ends
1636;
1637;
1638REGION db size REGIONt dup(?)
1639;
1640;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1641;
1642; INT_13_HANDLER - Handler for INT 13 requests
1643;
1644; ENTRY
1645; All regs as for INT 13
1646;
1647; EXIT
1648; To old INT 13 handler with regs unchanged if cache not involved
1649; Else return in AH and flags as per INT 13.
1650;
1651; USES
1652; AH and carry bit of FLAGS
1653;
1654; SEE ALSO
1655; IBM PC TECH REF MANUAL section on INT 13
1656;
1657INT_13_HANDLER PROC FAR
1658ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
1659 MOV [INT_13_BUSY],1 ; Exclude
1660 TEST DL,80H ; Hard file?
1661 JNZ IS_HARD ; Yes
1662NO_PROC:
1663 PUSHF
1664 CLI
1665 CALL [OLD_13] ; Let old handler do it
1666 MOV [INT_13_BUSY],0 ; Clear sem
1667 RET 2 ; "IRET" chucking flags
1668
1669;
1670; Pop off the stack frame and pass to old handler
1671;
1672POP_NO_PROC:
1673 POP BX
1674 POP ES
1675 POP DS
1676 ; Simulate POPA
1677 POP DI
1678 POP SI
1679 POP BP
1680 POP BX ; Dummy for pop sp
1681 POP BX
1682 POP DX
1683 POP CX
1684 POP AX
1685 ;
1686 JMP NO_PROC
1687
1688IS_HARD:
1689 STI ; INTs ok now
1690 ;
1691 ; Set up standard stack frame
1692 ;
1693 ; Simulate PUSHA
1694 PUSH AX
1695 PUSH CX
1696 PUSH DX
1697 PUSH BX
1698 PUSH BX ; dummy for push sp
1699 PUSH BP
1700 PUSH SI
1701 PUSH DI
1702 ;
1703 PUSH DS
1704 PUSH ES
1705 push bx
1706 ; Set frame pointer
1707 MOV BP,SP
1708 MOV BL,AH
1709 XOR BH,BH ; Command in BX
1710 PUSH CS
1711 POP DS
1712ASSUME DS:INT13CODE
1713 CMP [ENABLE_13],0 ; Are we enabled?
1714 JZ POP_NO_PROC ; No, ignore
1715
1716ifdef OMTI
1717;
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.
1722;
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
1727 or sys_flg,OMTI_EXT
1728 jmp pop_no_proc
1729in_extended_state:
1730;
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
1734; routine if so.
1735;
1736 and sys_flg,NOT OMTI_EXT ; Clear the extended flag
1737 cmp bx,2h ; READ
1738 jz pop_no_proc
1739 cmp bx,3h ; WRITE
1740 jz pop_no_proc
1741check_command_range:
1742;
1743; Allow the other OMTI functions to pass through
1744;
1745 cmp bx,OMTI_GET_CYL
1746 jz pop_no_proc
1747 cmp bx,OMTI_GET_REV
1748 jz pop_no_proc
1749endif ; OMTI
1750
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
1755
1756;** FLUSH_INVALID_PASS -- Discard cache and pass through
1757;
1758; ENTRY:
1759; INT 13 regs except for BP,BX and DS
1760; EXIT:
1761; To old INT13 handler
1762;
1763FLUSH_INVALID_PASS:
1764ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1765 CALL FLUSH_WHOLE_CACHE
1766 CALL INVALIDATE_CACHE
1767 JMP POP_NO_PROC
1768
1769;** INVALID_PASS -- DISCARD CACHE, NO NEED TO FLUSH, PASS THROUGH
1770;
1771;** SUNIL PAI
1772;
1773; ENTRY:
1774; INT 13 regs except for BP,BX and DS
1775; EXIT:
1776; To old INT 13 handler
1777;
1778INVALID_PASS:
1779ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1780 CALL INVALIDATE_CACHE
1781 JMP POP_NO_PROC
1782
1783;** FLUSH_PASS -- Flush cache (but retain data) and pass through
1784;
1785; ENTRY:
1786; INT 13 regs except for BP,BX and DS
1787; EXIT:
1788; To old INT13 handler
1789;
1790FLUSH_PASS:
1791ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1792 CALL FLUSH_WHOLE_CACHE
1793 JMP POP_NO_PROC
1794
1795BREAK <CACHE_READ -- Read via cache>
1796
1797ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
1798;
1799cache_read:
1800;
1801; inputs: int13 regs except for bp - user stack frame
1802; bx - function (read,write)
1803; ds - int13code segment
1804;
1805; exits: to user success routine
1806; to user error routine
1807; to old int13 handler call
1808;
1809; uses: all but bp
1810;
1811; written by: sunil pai
1812;
1813 call check_parameters ; check user params
1814 jc pop_no_proc ; if error go to old int 13 handler
1815 ;
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
1820;
1821; mov [int13err],FALSE ; clear int 13 error flag
1822;
1823 test [REGION.FLAG],REG1_P ; is region1 present
1824 je cr$2 ; if not present try region2
1825;
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
1832;
1833cr$2: test [REGION.FLAG],REG2_P ; is region2 present
1834 je cr$3 ; if not present try region3
1835;
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
1840 jc error ; fouled?
1841;
1842cr$3: test [REGION.FLAG],REG3_P ; is region3 present
1843 je suc ; if not we are done
1844;
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!
1851;
1852; exit points - suc and error.
1853;
1854suc: and [bp.USER_FL],NOT f_Carry ;
1855 mov byte ptr [bp.USER_AX.1],0 ;
1856;
1857operation_done:
1858 mov cs:[int_13_busy],0 ;clear semaphore
1859 pop bx ;user offset
1860;
1861; simulate popa
1862;
1863 pop es
1864 pop ds
1865 pop di
1866 pop si
1867 pop bp
1868 pop bx
1869 pop bx
1870 pop dx
1871 pop cx
1872 pop ax
1873;
1874 iret
1875;
1876error:
1877;
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
1884;
1885; test [int13err],TRUE
1886; jne er$1 ;if not int13 error go to call
1887 ;int13
1888 jmp pop_no_proc
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
1892;
1893pop_no_procw:
1894 call invalidate_cache
1895 jmp pop_no_proc
1896;
1897cache_write:
1898;
1899; inputs: int13 regs except for bp - user stack frame
1900; bx - function (read,write)
1901; ds - int13code segment
1902;
1903; uses: all but bp
1904;
1905; written by: sunil pai
1906;
1907 call check_parameters ;check user params
1908 jc pop_no_procw ;error
1909 ;
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
1914;
1915; writes always update disk, i.e., write through always operational
1916;
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
1923 cli
1924 call [old_13] ; call old int 13 handler
1925 jnc cw$1 ; no error in writing to disk, continue
1926;
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
1930;
1931; int 13 was successful, now we have to update cache as well
1932;
1933cw$1: and [bp.user_fl],not f_Carry ; int 13 success
1934 mov byte ptr [bp.user_ax.1],0 ;
1935;
1936 and dl,not 80h
1937;
1938 test [REGION.FLAG],REG1_P ; is region1 present
1939 je cw$2 ; if not present try region2
1940;
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
1947;
1948cw$2: test [REGION.FLAG],REG2_P ; is region2 present
1949 je cw$3 ; if not present try region3
1950;
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 ;
1955 jc errorw ; fouled?
1956;
1957cw$3: test [REGION.FLAG],REG3_P ; is region3 present
1958 je operation_donew ; if not we are done
1959;
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
1966;
1967errorw: call invalidate_cache ; we are not sure of the
1968operation_donew:
1969 jmp operation_done
1970;
1971;
1972INT_13_HANDLER endp
1973;
1974process_read_partial proc near
1975;
1976; is used to read a partial track
1977;
1978; inputs: dx: head and drive (8th bit stripped)
1979; cx: track
1980; bx: start sector
1981; ax: number of words
1982;
1983; outputs:cy set if error
1984; clear if success
1985;
1986; strategy:
1987; if (track in cache) then {
1988; if (track in track_buffer) then
1989; perform user read from track buffer;
1990; else
1991; perform user read from cache;
1992; }
1993; else {
1994; read track into track buffer;
1995; read track buffer into cache;
1996; perform user read from track buffer;
1997; }
1998;
1999; cache transfers handled by freeing cache element if possible
2000; and making it lru
2001;
2002; uses: only dx assumed unchanged
2003;
2004 call track_in_cache ; is track in cache
2005 jc read_disk ; if not we have to read from disk
2006;
2007 cmp cx,[track_buffer_cyln] ; is it in the track buffer
2008 jnz not_in_mem ; no
2009 cmp dx,[track_buffer_hddr] ;
2010 jz read_bufferj ; yes
2011;
2012; read buffer from cache
2013;
2014not_in_mem:
2015 push dx
2016 xchg ax,bx ;get number of words in bx and start sect in ax
2017 dec ax ;0 based number
2018 mov cl,9 ;
2019 shl ax,cl ;byte offset
2020 xor dx,dx ;
2021;
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
2027;
2028 add ax,word ptr [si.base_offset]
2029 adc dx,word ptr [si.base_offset.2]
2030 xor bh,bh
2031 push si
2032 push ds
2033 push bp
2034 call blkmov
2035 pop bp
2036 pop ds
2037 pop si
2038 pop dx
2039 jc err_cache
2040 call cache_is_mru
2041 clc
2042 ret
2043;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2044;
2045; track not in cache. see if free cache available to initiate transfer
2046;
2047read_disk:
2048 cmp di,-1 ;free cache
2049 jnz rd_part ;if present we can initiate read
2050 stc ; else error exit
2051 ret
2052;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2053;
2054read_bufferj:
2055 jmp short read_buffer
2056;
2057;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2058;
2059; cache element available. we can start by transferring track from disk
2060; to track buffer.
2061;
2062rd_part:
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
2067 push dx
2068 push cx
2069 push bx
2070 push ax
2071 or cl,1
2072 or dl,80h
2073 mov al,[sect_in_trk]
2074 mov ah,2 ; read
2075 push cs
2076 pop es
2077 mov bx,[track_buffer_ptr]
2078 pushf
2079 cli
2080 call [old_13]
2081 jnc rd$1
2082 cmp ah,11h ;the int13 error which is not an error
2083 je rd$1
2084 mov [int13err],TRUE
2085 add sp,8
2086 mov al,ah
2087 stc
2088 ret
2089;
2090; transfer track buffer to cache
2091;
2092rd$1:
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
2096 mov bh,1 ;write
2097 mov ax,word ptr [si.base_offset] ;address of cache
2098 mov dx,word ptr [si.base_offset.2] ;
2099 push ds
2100 push si
2101 push bp
2102 call blkmov
2103 pop bp
2104 pop si
2105 pop ds
2106 pop ax
2107 pop bx
2108 pop cx
2109 pop dx
2110 jnc rd$2
2111;
2112; error in transferring info to cache. invalidate cache element and
2113; make it lru
2114;
2115err_cache:
2116 mov [si.track_flags],track_free
2117 call cache_is_lru ; make cache element lru
2118 stc
2119 ret
2120;
2121; info transfer to cache successful. fill in track, head and drive
2122; info into cache control element and track buffer control element
2123;
2124if debug
2125rd_2:
2126endif
2127rd$2:
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
2133;
2134; perform user i/o from track buffer
2135;
2136read_buffer:
2137 call cache_is_mru
2138;
2139 mov si,bx ; start sector
2140 dec si ; 0 based number
2141 mov cl,9 ;
2142 shl si,cl ; byte offset in si
2143 add si,[track_buffer_ptr]
2144 mov cx,ax
2145 mov es,[bp.user_es]
2146 mov di,[bp.user_off]
2147 cld
2148rep movsw
2149 mov [bp.user_off],di
2150 clc
2151 ret
2152;
2153process_read_partial endp
2154;
2155process_block_read proc near
2156;
2157; inputs: ax = number of tracks
2158; dx = drive and head (8th bit stripped)
2159; cx = start track
2160; bp = user stack frame
2161;
2162; outputs: cy set if error
2163; cy clear if okay
2164;
2165; algorithm:
2166;
2167; repeat
2168; if (cur_trk in cache) then
2169; transfer track from cache to user buffer;
2170; no_of_trks = no_of_trks - 1
2171; else
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
2177;
2178pbr$1:
2179;
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
2182;
2183 push cx ; save start head
2184 push dx ; save start track
2185 xor ah,ah ;number of accumulated tracks
2186pbr$2: call track_in_cache ;
2187 jnc pbr$4 ; if current track in cache start processing
2188 inc dh ; go to next track
2189 call adj_hd_trk ;
2190 inc ah ;accumulate the tracks
2191 dec al ;are we done
2192 jne pbr$2 ;go to see next track
2193 pop dx ;restore start head and track
2194 pop cx ;
2195 call pr_acc_trks ;process the accumulated tracks
2196 ret ;we are done
2197pbr$4:
2198 pop dx ;restore start head and track
2199 pop cx
2200 or ah,ah ;are there any accumulated
2201 je pbr$5
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
2205 call adj_hd_trk
2206 jmp pbr$1
2207;
2208pbr$5: call pr_cur_trk ;process current track which is in cache
2209 jc pbr$7 ;if carry set finish with error
2210 inc dh
2211 call adj_hd_trk
2212pbr$6:
2213 dec al ;are we done?
2214 jne pbr$1 ;
2215 clc
2216pbr$7: ret
2217;
2218process_block_read endp
2219;
2220pr_acc_trks proc near
2221;
2222; inputs: cx = start track
2223; dh = start head
2224; ah = number of accumulated tracks
2225;
2226; outputs: if success :- cy clear, [bp.user_off] modified
2227; if failure :- cy set
2228;
2229; regs to be preserved: al,cx,si
2230;
2231; algorithm:
2232;
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;
2238; else
2239; exit with error;
2240;
2241;
2242 push si
2243 push dx
2244 push cx
2245 push ax ; stack:- {ax,cx,dx,si}
2246;
2247; initialise int13 registers for reading the accumulated tracks into
2248; user memory.
2249;
2250 mov al,ah ;number of tracks
2251 xor ah,ah ;in ax
2252;
2253 mul [sect_in_trk] ;get number of sectors in ax
2254;
2255 mov ah,2
2256 or cx,1 ;start sector
2257;
2258 or dl,80h ;set hard disk bit
2259 mov bx,[bp.user_off];
2260 mov es,[bp.user_es] ;
2261;
2262 pushf
2263 cli
2264 call [old_13] ;perform multi track read
2265;
2266; check for int 13 error
2267;
2268 jnc pat$1 ;if okay proceed
2269;
2270 add sp,8 ;clear stack
2271 mov al,ah
2272 mov [int13err],TRUE ;
2273 stc
2274 ret ;error exit
2275;
2276; we have succesfully read al tracks into user memory. now these have
2277; to transfer these to cache.
2278;
2279pat$1: and dl,not 80h ;clear off 8th bit
2280 pop ax ;restore ax
2281 pop cx ;
2282 push cx ;
2283 push ax
2284;
2285 mov di,[bp.user_off];initialise transfer offset
2286;
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
2291;
2292pat$2:
2293 call get_cache ;get free cache element
2294;
2295; si = cache element
2296;
2297 cmp si,-1 ;was there an element
2298 je pat$5 ;cache saturated exit
2299;
2300; check if track is in track buffer
2301;
2302 cmp cx,[track_buffer_cyln]
2303 jne pat$21
2304 cmp dx,[track_buffer_hddr]
2305 jne pat$21
2306;
2307; track is in track buffer. to avoid two transfers invalidate
2308; track buffer
2309;
2310 mov [track_buffer_cyln],-1
2311 mov [track_buffer_hddr],-1
2312;
2313; transfer track from user buffer to cache
2314;
2315pat$21: mov [si.track_flags],track_free ;if write fails
2316 push cx
2317 push ax
2318 push ds
2319 push si
2320 push bp
2321 push di
2322 push dx
2323;
2324 mov es,[bp.user_es]
2325 mov cx,[bytes_in_trk]
2326 shr cx,1 ;words
2327 mov ax,word ptr [si.base_offset]
2328 mov dx,word ptr [si.base_offset.2]
2329 mov bh,1 ;write
2330 call blkmov
2331;
2332 pop dx
2333 pop di
2334 pop bp
2335 pop si
2336 pop ds
2337 pop ax
2338 pop cx
2339;
2340 jnc pat$3
2341;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2342; error in transferring information to cache. make it lru
2343;
2344;
2345 call cache_is_lru
2346pat$5:
2347 add sp,8
2348 stc
2349 ret ;cache error exit
2350;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2351;
2352; successfully transferred information to cache. fill in track and
2353; drive and head information into cache control element
2354;
2355pat$3: mov word ptr [si.track_cyln],cx
2356 mov word ptr [si.track_drive],dx
2357 and [si.track_flags],not track_free
2358 call cache_is_mru
2359
2360;
2361 add di,[bytes_in_trk] ;advance pointer in user transfer buffer to
2362 ;next track
2363;
2364 inc dh ;advance track
2365 call adj_hd_trk
2366;
2367pat$4: dec ah ;have we processed all the accumulated trks
2368 jne pat$2
2369 mov [bp.user_off],di ;update user transfer address to beyond
2370 ;this accumulated block
2371 pop ax
2372 pop cx
2373 pop dx
2374 pop si
2375 clc
2376 ret ;success exit
2377;
2378pr_acc_trks endp
2379;
2380pr_cur_trk proc near
2381;
2382; inputs:
2383; si = cache element
2384; cx = current track
2385; dx = drive and head
2386;
2387; outputs:
2388; cy = set if error
2389; = clear if success
2390;
2391; algorithm:
2392; transfer track from cache to user memory
2393;
2394 push ax
2395 push cx
2396 push dx
2397 push si
2398 push ds
2399 push bp
2400;
2401 mov cx,[bytes_in_trk]
2402 shr cx,1
2403 mov ax,word ptr [si.base_offset]
2404 mov dx,word ptr [si.base_offset.2]
2405 mov es,[bp.user_es]
2406 mov di,[bp.user_off]
2407 xor bh,bh ;read
2408 call blkmov
2409;
2410 pop bp
2411 pop ds
2412 pop si
2413 pop dx
2414 pop cx
2415 pop ax
2416;
2417 jnc pct$1
2418;
2419 mov [si.track_flags],track_free
2420 call cache_is_lru
2421 stc
2422 ret ;error exit
2423;
2424pct$1: call cache_is_mru
2425 mov di,[bytes_in_trk]
2426 add [bp.user_off],di
2427 clc
2428 ret
2429;
2430pr_cur_trk endp
2431;
2432process_write_partial proc near
2433;
2434; is used to write a partial track
2435;
2436; inputs: dx: int13 dx with high bit of dl set off
2437; cx: track
2438; bx: start sector
2439; ax: number of words
2440;
2441; outputs:cy set if error
2442; clear if success
2443;
2444; strategy:
2445; if (track in cache) then {
2446; if (track in track_buffer) then
2447; invalidate track buffer;
2448; perform user write into cache;
2449; }
2450; else {
2451; read track into track buffer;
2452; write track buffer into cache;
2453; }
2454;
2455;
2456 call track_in_cache ;is track in cache
2457 jc read_diskw ;if not we have to read from disk
2458;
2459 cmp cx,[track_buffer_cyln] ;is it in the track buffer
2460 jnz not_in_memw ;no
2461 cmp dx,[track_buffer_hddr] ;
2462 jnz not_in_memw ;no
2463 ;yes
2464;
2465 mov [track_buffer_cyln],-1 ;invalidate trk buf
2466 mov [track_buffer_hddr],-1 ;to avoid two transfers
2467;
2468; update cache element from user buffer
2469;
2470not_in_memw:
2471 push dx
2472 xchg ax,bx ;get number of words in bx and start sect in ax
2473 dec ax ;0 based number
2474 mov cl,9 ;
2475 shl ax,cl ;byte offset
2476 xor dx,dx ;
2477;
2478 mov cx,bx ;number of words
2479 mov es,[bp.user_es] ;
2480 mov di,[bp.user_off];
2481 shl bx,1
2482 add [bp.user_off],bx
2483;
2484 add ax,word ptr [si.base_offset]
2485 adc dx,word ptr [si.base_offset.2]
2486 mov bh,1 ;write
2487 push si
2488 push ds
2489 push bp
2490 call blkmov
2491 pop bp
2492 pop ds
2493 pop si
2494 pop dx
2495 jc err_cw
2496 call cache_is_mru
2497 clc
2498 ret
2499;
2500; cache error
2501;
2502err_cw: mov [si.track_flags],track_free
2503 call cache_is_lru
2504 stc
2505 ret
2506;
2507; track not in cache. see if free cache element available
2508;
2509read_diskw:
2510 cmp di,-1 ;free cache
2511 jnz rd_partw ;if present we can initiate read
2512 stc
2513 ret
2514;
2515; free cache element available. read track from disk into track buffer
2516;
2517rd_partw:
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
2522 push dx
2523 push cx
2524 push bx
2525 push ax
2526 or cl,1
2527 or dl,80h
2528 mov al,[sect_in_trk]
2529 mov ah,2
2530 push cs
2531 pop es
2532 mov bx,[track_buffer_ptr]
2533 pushf
2534 cli
2535 call [old_13]
2536 jnc wr$1
2537 cmp ah,11h ;the int13 error which is not an error
2538 je wr$1
2539 mov [int13err],TRUE
2540 add sp,8
2541 mov al,ah
2542 stc
2543 ret
2544;
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
2547; into cache
2548;
2549wr$1: mov di,[track_buffer_ptr] ;es:di is transfer address
2550 mov al,[sect_in_trk]
2551 xor ah,ah
2552 mov cl,8
2553 shl ax,cl
2554 mov cx,ax
2555 mov bh,1 ;write
2556 mov ax,word ptr [si.base_offset]
2557 mov dx,word ptr [si.base_offset.2]
2558 push ds
2559 push si
2560 push bp
2561 call blkmov
2562 pop bp
2563 pop si
2564 pop ds
2565 pop ax
2566 pop bx
2567 pop cx
2568 pop dx
2569 jnc wr$2
2570 ret
2571;
2572; information successfully transferred to cache. fill in cache
2573; control element with the info on head, drive and track
2574;
2575wr$2:
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
2581;
2582 call cache_is_mru
2583;
2584 shl ax,1
2585 add [bp.user_off],ax
2586 clc
2587 ret
2588;
2589process_write_partial endp
2590;
2591;
2592process_block_write proc near
2593;
2594; al = number of tracks
2595; cx = start track
2596; dx = drive and start head
2597; bp = user stack frame
2598;
2599; cy set if error
2600; cy clear if success
2601;
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;
2605; else {
2606; get a free cache element;
2607; write cur_trk from user buffer into this cache elem
2608; }
2609;
2610 mov di,[bp.user_off]
2611;
2612pbw$1: push di ;save it because next call destroys di
2613 call track_in_cache ;is the track in cache
2614 jnc pbw$2
2615;
2616 cmp di,-1 ;no free cache
2617 je pbw$4 ;error exit
2618;
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
2623;
2624pbw$2: pop di
2625;
2626; check if it is in track buffer also in which case invalidate trk buffer
2627;
2628 cmp cx,[track_buffer_cyln]
2629 jne pbw$21
2630 cmp dx,[track_buffer_hddr]
2631 jne pbw$21
2632;
2633; invalidate track buffer to avoid two transfers
2634;
2635 mov [track_buffer_cyln],-1
2636 mov [track_buffer_hddr],-1
2637;
2638; update cache from user buffer
2639pbw$21:
2640 push cx ;
2641 push ax
2642 push ds
2643 push si
2644 push bp
2645 push di
2646 push dx
2647;
2648 mov es,[bp.user_es]
2649 mov cx,[bytes_in_trk]
2650 shr cx,1
2651 mov ax,word ptr [si.base_offset]
2652 mov dx,word ptr [si.base_offset.2]
2653 mov bh,1
2654 call blkmov
2655;
2656 pop dx
2657 pop di
2658 pop bp
2659 pop si
2660 pop ds
2661 pop ax
2662 pop cx
2663;
2664 jnc pbw$22
2665 jmp err_cw
2666;
2667pbw$22: and [si.track_flags],not track_free
2668;
2669 inc dh ;go to next track
2670 call adj_hd_trk
2671pbw$3: add di,[bytes_in_trk]
2672 call cache_is_mru
2673;
2674 dec al ; are we done
2675 jne pbw$1
2676;
2677; success exit
2678 mov [bp.user_off],di
2679 clc
2680 ret
2681;
2682; error exit
2683;
2684pbw$4: pop di
2685 stc
2686 ret
2687;
2688process_block_write endp
2689;
2690check_parameters proc near
2691;
2692; inputs: same as int13 registers except for
2693; BP - user_stack_frame
2694; BX - function (either read or write)
2695; DS - int13code segment
2696;
2697; outputs: carry set if params invalid
2698; carry clear if params okay
2699;
2700; if parameters okay :
2701; dl - drive with high bit off
2702; bx - sector number
2703; cl - cleared of the sector number
2704;
2705;
2706; check for number of drives
2707;
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.
2711;
2712; check for number of sectors
2713;
2714 or al,al ; zero sectors ?
2715 je bad_parmx ; error.
2716;
2717 cmp al,80h ; more than 80h sectors ?
2718 ja bad_parmx ; error.
2719;
2720; check for wrap in user transfer address
2721;
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
2725 push cx ;
2726 mov cl,9
2727 shl si,cl ; convert number of sectors into
2728 ; number of bytes
2729 dec si ; convert into zero based number
2730 pop cx ;
2731 add di,si ; add to transfer address offset
2732 jc bad_parmx ; if exceeds 64k offset then wrap
2733;
2734; get drive number into di to use as offset into sect / trk table
2735;
2736 mov di,dx ; drive number
2737 and di,0000000011111111B ; just get the relevant bits
2738;
2739; form sector number in bx and compare against allowed number of sectors
2740; in track on the drive indicated
2741;
2742 mov bx,cx ;
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
2747 ; allowed
2748 ja bad_parmx ;
2749;
2750; check head parameter
2751;
2752 cmp dh,[di.hdarray] ; is it more than max useable val for drive
2753 ja bad_parmx ;
2754;
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 ?
2758;
2759;
2760; clear off sector number from cl to leave just trk number in cx
2761;
2762 and cl,11000000B ; clear off sector number
2763;
2764; store sectors in track for current drive and bytes in track for
2765; current drive in memory
2766;
2767 push bx
2768 push cx
2769 mov bl,[di.hdarray]
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
2773 mov cl,9
2774 shl bx,cl ; bytes in track
2775 mov [bytes_in_trk],bx ; store this
2776 pop cx
2777 pop bx
2778 clc
2779 ret ;return with no error
2780;
2781bad_parmx:
2782 stc
2783 ret
2784;
2785check_parameters endp
2786;
2787process_regions proc near
2788;
2789; inputs: bx - start sector
2790; al - number of sectors
2791; cx - start track
2792;
2793; outputs: none
2794;
2795; action: initialise regions struct
2796;
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
2800;
2801; process region1
2802;
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
2807 cmp ah,al
2808 jbe pr$1 ; if below or equal ah has number of sectors
2809 ; in region1
2810 mov ah,al ; else all the sectors are in region1
2811pr$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
2816 push ax ;
2817 mov al,ah ;
2818 xor ah,ah ; ax has number of sectors now
2819 mov cl,8 ;
2820 shl ax,cl ; multiply by 256 to get number of words
2821 mov [REGION.REG1.COUNT],ax ; store this count
2822 pop ax ;
2823 pop cx
2824 inc dh
2825 call adj_hd_trk
2826;
2827; process region2
2828;
2829pr$2: or al,al ; are we done
2830 je pr$end ;
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
2836;
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
2842 call adj_hd_trk
2843;
2844; process region3
2845;
2846pr$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
2851 mov cl,8
2852 mov al,ah
2853 xor ah,ah ; convert number of sectors into number of
2854 shl ax,cl ; words
2855 mov [REGION.REG3.COUNT],ax ; store this
2856pr$end:
2857;
2858 ret
2859;
2860process_regions endp
2861;
2862; Support Routines:
2863;
2864;
2865track_in_cache proc near
2866;
2867; input: dl = drive number with bit 8 set off
2868; cx = cylinder number
2869;
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
2874nexte:
2875 dec si ; to counter last instruction in the loop
2876 test [si.track_flags],track_locked ; is the track locked?
2877 jnz no_set ;
2878 mov di,si ; if not locked update di to this element
2879no_set:
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
2883 jnz skipe ;
2884 cmp cx,[si.track_cyln] ; and cylinder number
2885 jz tic$1 ; if found exit routine
2886skipe:
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
2890 stc
2891 ret
2892tic$1: clc
2893 ret
2894;
2895track_in_cache endp
2896;
2897get_cache proc near
2898;
2899; inputs: none
2900; outputs: si = lru cache element not locked
2901; = -1 if all elems locked
2902;
2903 mov si,[cache_tail] ;start with lru element
2904 inc si ;to counter next instruction
2905gc$1:
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
2909;
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
2914;
2915 dec si ; no element found, si = -1
2916;
2917gc$2: ret
2918;
2919get_cache endp
2920;
2921adj_hd_trk proc near
2922;
2923; inputs: ch,cl track number
2924; dh changed head number to be checked and adjusted
2925;
2926; outputs: cx and dh updated
2927;
2928 pushf
2929aht$1: cmp dh,[max_hd] ;is the head number > heads on drive
2930 jbe aht$2
2931 sub dh,[max_hd] ;if so decrease head number by number of
2932 ;heads on drive
2933 dec dh ;by one more
2934 add ch,1 ;and step to next track
2935 jnc aht$1
2936 add cl,40h
2937 jmp aht$1
2938aht$2: popf
2939 ret
2940;
2941adj_hd_trk endp
2942
2943CACHE_HIT PROC NEAR
2944ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
2945CACHE_HIT ENDP
2946
2947
2948;** INVALIDATE_CACHE -- Discard all cache info
2949;
2950; ENTRY
2951; Cache is flushed (If it is not, all dirty info will simply be chucked)
2952; EXIT
2953; All elements of cache are marked free
2954; USES
2955; BX,FLAGS
2956;
2957INVALIDATE_CACHE PROC NEAR
2958ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
2959 MOV BX,[CACHE_HEAD]
2960 INC BX ; Counter next instruction
2961NEXTC:
2962 DEC BX
2963 MOV [BX.TRACK_FLAGS],TRACK_FREE
2964 MOV BX,[BX.FWD_LRU_LNK]
2965 INC BX
2966 JNZ NEXTC
2967 ;
2968 ; Track buffer invalid too
2969 ;
2970 MOV [TRACK_BUFFER_CYLN],-1
2971 MOV [TRACK_BUFFER_HDDR],-1
2972 MOV [DIRTY_CACHE],0
2973 ret
2974
2975INVALIDATE_CACHE ENDP
2976
2977;** CACHE_IS_MRU -- Put cache element in LRU chain at MRU position
2978;
2979; ENTRY
2980; SI points cache element to place at MRU position
2981; EXIT
2982; SI is at MRU position (head)
2983; USES
2984; DI,FLAGS
2985;
2986CACHE_IS_MRU PROC NEAR
2987ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
2988 push di
2989 CMP SI,[CACHE_HEAD]
2990 JZ RET444
2991 CALL UNLINK_CACHE
2992 MOV DI,SI
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
2997RET444: pop di
2998 RET
2999
3000CACHE_IS_MRU ENDP
3001
3002;** CACHE_IS_LRU -- Put cache element in LRU chain at LRU position
3003;
3004; ENTRY
3005; SI points to cache element to place at LRU position
3006; EXIT
3007; SI is at LRU position (tail)
3008; USES
3009; DI,FLAGS
3010;
3011CACHE_IS_LRU PROC NEAR
3012ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
3013 push di
3014 CMP SI,[CACHE_TAIL]
3015 JZ RET555
3016 CALL UNLINK_CACHE
3017 MOV DI,SI
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
3022RET555: pop di
3023 RET
3024
3025CACHE_IS_LRU ENDP
3026
3027;** UNLINK_CACHE -- Unlink cache element from LRU chain
3028;
3029; ENTRY
3030; SI points to element to unlink
3031; EXIT
3032; SI is unlinked
3033; USES
3034; DI,FLAGS
3035;
3036UNLINK_CACHE PROC NEAR
3037ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
3038 PUSH BX
3039 MOV DI,[SI.BACK_LRU_LNK] ; Get prev guy
3040 INC DI ; Guy First?
3041 JZ NEW_HEAD ; Yes
3042 DEC DI
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?
3046 JZ NEW_TAIL ; Yes
3047 DEC BX
3048 MOV [BX.BACK_LRU_LNK],DI ; Next back is my back
3049NULL_CACHE:
3050 POP BX
3051 RET
3052
3053NEW_HEAD:
3054 MOV DI,[SI.FWD_LRU_LNK] ; Is head also tail?
3055 INC DI
3056 JZ NULL_CACHE ; Yes
3057 DEC DI
3058 MOV [CACHE_HEAD],DI ; New head
3059 MOV [DI.BACK_LRU_LNK],-1 ; New head has no back link
3060 POP BX
3061 RET
3062
3063NEW_TAIL:
3064 MOV [CACHE_TAIL],DI ; New tail
3065 POP BX
3066 RET
3067UNLINK_CACHE ENDP
3068
3069RETRY_CNT DB ?
3070
3071;** WRITE_FROM_CACHE -- Write out cache element to disk
3072;
3073; ENTRY
3074; SI -> cache element to write
3075; EXIT
3076; Carry Clear
3077; Written OK
3078; Track buffer is set to this track
3079; Carry Set
3080; Error, AL is error code
3081; Track buffer is set to empty
3082; USES
3083; AX,BX,CX,DX,ES,DI,FLAGS
3084;
3085WRITE_FROM_CACHE PROC NEAR
3086ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
3087 ;
3088 ; First transfer track down to track buffer because we can't write
3089 ; direct to extended or expanded mem with INT 13
3090 ;
3091 PUSH CS
3092 POP ES
3093 MOV DI,[TRACK_BUFFER_PTR] ; ES:DI is transfer addr for BLKMOV
3094 MOV BX,WORD PTR [SI.TRACK_DRIVE]
3095 XOR BH,BH
3096 MOV AL,[BX.SECTRKARRAY]
3097 XOR AH,AH
3098 PUSH AX
3099 MOV CL,8
3100 SHL AX,CL ; AX is words in track
3101 MOV CX,AX
3102 XOR BH,BH ; Read
3103 MOV AX,WORD PTR [SI.BASE_OFFSET]
3104 MOV DX,WORD PTR [SI.BASE_OFFSET.2] ; DX:AX is address in mem
3105 PUSH DS
3106 PUSH SI
3107 PUSH BP
3108 CALL BLKMOV ; Track buffer contents to track buffer
3109 POP BP
3110 POP SI
3111 POP DS
3112 JC ERR_FLP
3113 POP AX ; AL is sec/trk
3114 ;
3115 ; Now write it out to drive from the track buffer
3116 ;
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
3121 OR CL,1
3122 OR DL,80H
3123 MOV AH,3
3124 PUSH CS
3125 POP ES
3126 MOV BX,[TRACK_BUFFER_PTR]
3127 PUSH AX
3128 MOV [RETRY_CNT],5
3129RETRY_WRITE:
3130 PUSHF
3131 CALL [OLD_13] ; Write it out
3132 JC ERR_RETRY
3133NO_ERR1:
3134 POP AX
3135ERR_FL:
3136 ret
3137
3138ERR_RETRY:
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
3142 MOV AH,0 ; Reset
3143 INT 13H
3144 POP AX ; Get error back
3145 DEC [RETRY_CNT]
3146 JZ SET_ERR ; Return error
3147 POP AX ; Recover correct AX for INT 13
3148 PUSH AX
3149 JMP RETRY_WRITE
3150
3151SET_ERR:
3152 MOV AL,AH ; INT 13 error to AL
3153ERR_FLP:
3154 ADD SP,2
3155 STC
3156 MOV [TRACK_BUFFER_CYLN],-1 ; Zap the track buffer
3157 MOV [TRACK_BUFFER_HDDR],-1
3158 JMP ERR_FL
3159
3160WRITE_FROM_CACHE ENDP
3161
3162;** FLUSH_CACHE -- Flush specific cache element if it's dirty
3163;
3164; ENTRY
3165; SI points to element to flush
3166; EXIT
3167; Carry Clear
3168; SI is flushed if it was dirty
3169; SI.TRACK_FLAGS dirty bit clear
3170; Track buffer set to this track
3171; Carry Set
3172; SI could not be flushed
3173; SI.TRACK_FLAGS = free
3174; AL is error code
3175; Track buffer set to empty
3176; USES
3177; AX,BX,CX,DX,ES,DI,FLAGS
3178;
3179FLUSH_CACHE PROC NEAR
3180ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
3181 TEST [SI.TRACK_FLAGS],TRACK_FREE ; Clears carry
3182 JNZ IGNORE_TRK
3183 TEST [SI.TRACK_FLAGS],TRACK_DIRTY ; Clears carry
3184 JZ IGNORE_TRK
3185 CALL WRITE_FROM_CACHE
3186 DEC [DIRTY_CACHE] ; Doesn't effect carry
3187 JC FLUSH_ERRX
3188 AND [SI.TRACK_FLAGS],NOT TRACK_DIRTY ; Clears carry
3189IGNORE_TRK:
3190 ret
3191
3192FLUSH_ERRX:
3193 MOV [SI.TRACK_FLAGS],TRACK_FREE ; Track gone, unlocked
3194 RET
3195
3196FLUSH_CACHE ENDP
3197
3198;** FLUSH_WHOLE_CACHE_SAV -- Flush all dirty cache elements saving regs
3199;
3200; ENTRY
3201; None
3202; EXIT
3203; Cache flushed
3204; USES
3205; FLAGS
3206;
3207FLUSH_WHOLE_CACHE_SAV PROC NEAR
3208ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
3209 ; Simulate PUSHA
3210 PUSH AX
3211 PUSH CX
3212 PUSH DX
3213 PUSH BX
3214 PUSH BX ; dummy for push sp
3215 PUSH BP
3216 PUSH SI
3217 PUSH DI
3218 ;
3219 PUSH DS
3220 PUSH ES
3221 PUSH CS
3222 POP DS
3223ASSUME DS:INT13CODE
3224 CALL FLUSH_WHOLE_CACHE
3225 POP ES
3226 POP DS
3227 ; Simulate POPA
3228 POP DI
3229 POP SI
3230 POP BP
3231 POP BX ; Dummy for pop sp
3232 POP BX
3233 POP DX
3234 POP CX
3235 POP AX
3236 ;
3237 ret
3238FLUSH_WHOLE_CACHE_SAV ENDP
3239
3240;** FLUSH_WHOLE_CACHE -- Flush all dirty cache elements
3241;
3242; ENTRY
3243; None
3244; EXIT
3245; Cache flushed
3246; USES
3247; AX,BX,CX,DX,ES,SI,DI,FLAGS
3248;
3249FLUSH_WHOLE_CACHE PROC NEAR
3250ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
3251 MOV SI,[CACHE_HEAD]
3252 INC SI ; Counter next instruction
3253FLSH_LP:
3254 DEC SI
3255 CALL FLUSH_CACHE
3256 MOV SI,[SI.FWD_LRU_LNK]
3257 INC SI
3258 JNZ FLSH_LP
3259FLUSH_DONE:
3260 MOV [DIRTY_CACHE],0 ; No dirty guys in cache
3261 ret
3262
3263FLUSH_WHOLE_CACHE ENDP
3264
3265BREAK <Drive code for /E driver>
3266
3267;
3268; The following label defines the start of the I/O code which is driver type
3269; specific.
3270;
3271; THE TYPE 2 driver must REPLACE this code with code appropriate
3272; to the driver type.
3273;
3274 EVEN ; Force start of drive code to word boundary
3275
3276DRIVE_CODE LABEL WORD
3277
3278EXTMEM_LOW EQU 0000H ; 24 bit addr of start of extended memory
3279EXTMEM_HIGH EQU 0010H
3280
3281;** BASE_ADDR data element
3282;
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.
3286;
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.
3290;
3291; NOTE: This data element is shared by TYPE 1, 2 drivers, but
3292; its meaning and correct initial value are driver type specific.
3293;
3294
3295;; NOTE: The value at BASE_ADDR is patched during initialization when
3296;; loading a RAMDrive into upper extended memory on a PLUS
3297;;
3298BASE_ADDR LABEL DWORD ; 24 bit address of start of this RAMDRV
3299 DW EXTMEM_LOW
3300 DW EXTMEM_HIGH
3301;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3302;** BLKMOV - Perform transfer for TYPE 1 driver
3303;
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".
3314;
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.
3318;
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
3323; range.
3324;
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.
3332;
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.
3338;
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
3345; INT13 MEMORY.
3346;
3347; METHOD:
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
3353; CLI
3354; copy 80:0 to INT13 buffer
3355; copy LOADALL info to 80:0
3356; LOADALL
3357; do 256 word transfer
3358; copy INT13 80:0 buffer back to 80:0
3359; STI
3360; Disable address line 20
3361;
3362; SEE ALSO
3363; INTEL special documentation of LOADALL instruction
3364;
3365; ENTRY:
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
3370;
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
3375;
3376; EXIT:
3377; Carry Clear
3378; OK, operation performed successfully
3379; Carry Set
3380; Error during operation, AL is error number (INT 13 error)
3381;
3382; USES:
3383; ALL
3384;
3385; This routine is specific to TYPE 1 driver
3386;
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
3394
3395BLKMOV:
3396ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
3397 test [sys_flg],M_386
3398 je blkmov_286
3399 jmp blkmov_386
3400 ;
3401 ; Compute 32 bit address of start of I/O
3402 ;
3403blkmov_286:
3404 ADD AX,WORD PTR [BASE_ADDR]
3405 ADC DX,WORD PTR [BASE_ADDR + 2]
3406 ;
3407 ; Dispatch on function
3408 ;
3409 OR BH,BH
3410 JZ READ_IT
3411 ;
3412 ; Write
3413 ;
3414 MOV WORD PTR [ESDES.SEG_BASE],AX
3415 MOV BYTE PTR [ESDES.SEG_BASE + 2],DL
3416; MOV [LSI],DI
3417 mov [lbx],di ;sp
3418 MOV [LDI],0
3419 MOV SI,OFFSET DSDES
3420 JMP SHORT SET_TRANS
3421
3422READ_IT:
3423 MOV WORD PTR [DSDES.SEG_BASE],AX
3424 MOV BYTE PTR [DSDES.SEG_BASE + 2],DL
3425 MOV [LDI],DI
3426; MOV [LSI],0 ;sp
3427 mov [lbx],0
3428 MOV SI,OFFSET ESDES
3429SET_TRANS:
3430 MOV AX,ES
3431 CALL SEG_SET ; Set ES or DS segreg
3432 ;
3433 ; Set stack descriptor
3434 ;
3435 MOV AX,SS
3436 MOV [LSSS],AX
3437 MOV SI,OFFSET SSDES
3438 CALL SEG_SET
3439 MOV [LSP],SP
3440; SUB [LSP],2 ; CX is on stack at LOADALL
3441;
3442; the loadall kludge
3443;
3444 mov ax,cs ;sp
3445 inc ax ;sp
3446 mov [lcss],ax ;sp
3447 mov si,offset CSDES ;sp
3448 mov ax,cs ;sp
3449 call seg_set ;sp
3450 ;
3451 ; Set Other LOADALL stuff
3452 ;
3453 SMSW [LDSW]
3454 SIDT FWORD PTR [IDTDES]
3455 SGDT FWORD PTR [GDTDES]
3456 ;
3457 ; NOW The damn SXXX instructions store the desriptors in a
3458 ; different order than LOADALL wants
3459 ;
3460 MOV SI,OFFSET IDTDES
3461 CALL FIX_DESCRIPTOR
3462 MOV SI,OFFSET GDTDES
3463 CALL FIX_DESCRIPTOR
3464 ;
3465 ; Enable address line 20
3466 ;
3467
3468;;
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.
3474;;
3475
3476;; MOV AH,0DFH
3477 mov ax,cs:[A20On] ;; set up for PLUS or AT
3478 CALL A20
3479 Jc NR_ERR
3480; JMP SHORT IO_START ;sp
3481 jmp short move_main_loop ;sp
3482
3483NR_ERR:
3484 MOV AL,0AAH ; Drive not ready error
3485 STC
3486 RET
3487
3488;IOLOOP: ;sp
3489; PUSH CX ;sp
3490
3491move_main_loop: ;sp
3492assume ds:nothing ;sp
3493 jcxz io_done ;sp
3494 mov cs:[ldx],cx ;sp
3495 MOV AX,80H
3496 MOV DS,AX
3497 PUSH CS
3498 POP ES
3499 XOR SI,SI
3500 MOV DI,OFFSET cs:[SWAP_80]
3501 MOV CX,102/2
3502 mov cs:[ssSave],ss
3503 CLD
3504 CLI ; Un interruptable
3505 REP MOVSW ; Save contents of 80:0
3506 PUSH DS
3507 PUSH ES
3508 POP DS
3509 POP ES
3510 XOR DI,DI
3511 MOV SI,OFFSET cs:LOADALL_TBL
3512 MOV CX,102/2
3513 REP MOVSW ; Transfer in LOADALL info
3514 DW 050FH ; LOADALL INSTRUCTION
3515AFTER_LOADALL:
3516; set up stack for moving 80:0 information back again
3517;
3518 xor bp,bp
3519 mov ss,ax
3520 mov si,offset cs:[swap_80]
3521 mov cx,102/2
3522move_loop:
3523 lods word ptr cs:[si]
3524 mov ss:[bp],ax
3525 inc bp
3526 inc bp
3527 loop move_loop
3528 mov ss,cs:[ssSave]
3529 mov cx,dx
3530 mov si,bx
3531;critical code
3532 sti
3533 rep movsw
3534 cli ; bugfix sunilp
3535 mov ax,cs
3536 dec ax
3537 push ax
3538 mov ax,offset io_done
3539 push ax
3540 db 0cbh
3541;
3542 db 16 dup (0fah) ; bugfix sunilp
3543 mov ax,cs
3544 dec ax
3545 push ax
3546 mov ax,offset resume_int
3547 push ax
3548 db 0cbh
3549;
3550resume_int:
3551 mov cs:[ldi],di
3552 mov cs:[lbx],si
3553 jmp move_main_loop
3554
3555; REP MOVSW ; Move data
3556;IO_START:
3557; JCXZ IODN
3558; MOV WORD PTR [LCX],256 ; ASSUME full block
3559; SUB CX,256
3560; JNC IOLOOP ; OK
3561; ADD [LCX],CX ; OOPs, partial block
3562; XOR CX,CX ; This is the last block
3563; JMP IOLOOP
3564
3565;IODN:
3566io_done:
3567 sti ; bugfix sunilp
3568 MOV CX,800H ; Retry this many times
3569OFFLP:
3570
3571;;
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.
3574;;
3575
3576;; MOV AH,0DDH
3577 mov ax,cs:[A20Off] ;; setup for PLUS or AT. ah for IBM, al for PLUS
3578 CALL A20 ; Disable address line 20
3579 jnc dis_done
3580 LOOP OFFLP
3581dis_done:
3582 CLC
3583 RET
3584
3585;** A20 - ENABLE/DISABLE ADDRESS LINE 20 ON IBM PC-AT
3586;
3587; This routine enables/disables address line 20 by twiddling bits
3588; in one of the keyboard controller registers.
3589;
3590; SEE ALSO
3591; IBM Technical Reference Personal Computer AT Manual #1502243
3592; Page 5-155
3593;
3594; ENTRY
3595; AH = 0DDH to disable A20
3596; AH = 0DFH to enable A20
3597; EXIT
3598; CY Failed
3599; NC Succeeded
3600; USES
3601; AL, FLAGS
3602;
3603; WARNING If this routine is called in a CLI state this routine has
3604; the side effect of enabling interrupts.
3605;
3606; This routine is specific to TYPE 1 driver
3607;
3608
3609A20:
3610ASSUME 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
3615 jmp a20s5
3616test_vec:
3617 cmp cs:[S5_FLAG],S_VECTRA
3618 jne test_ps2
3619 jmp VecA20
3620test_ps2:
3621 test cs:[sys_flg],M_PS2 ; is it a ps2 machine
3622 jne a20ps2 ; if yes it has separate a20 routine
3623old_a20:
3624 CLI
3625 call check_a20 ; check to see if it can be enb /disb
3626 jc a20suc ; no it may not be toggled
3627 CALL E_8042
3628 JNZ a20err
3629 MOV AL,0D1H
3630 OUT 64H,AL
3631 CALL E_8042
3632 JNZ a20err
3633 MOV AL,AH
3634 OUT 60H,AL
3635 CALL E_8042
3636 JNZ a20err
3637 ;
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!)
3645 ;
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
3649 jnz A20Err
3650
3651A20Suc: sti
3652 clc
3653 RET
3654A20Err: sti
3655 stc
3656 ret
3657;
3658; Helper routine for A20. It waits for the keyboard controller to be "ready".
3659;
3660E_8042:
3661ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
3662 PUSH CX
3663 XOR CX,CX
3664E_LOOP:
3665 IN AL,64H
3666 AND AL,2
3667 LOOPNZ E_LOOP
3668 POP CX
3669 RET
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
3674; is allowed
3675;
3676check_a20:
3677assume ds:nothing,es:nothing,ss:nothing
3678 cmp ah,0ddh ; is it a disable operation
3679 jne check_a20_enable
3680;
3681; check if a20 disabling allowed
3682;
3683 test cs:[sys_flg],a20_st
3684 jne no_toggle
3685toggle: clc
3686 ret
3687;
3688; a20 enabling, check if allowed
3689;
3690check_a20_enable:
3691 and cs:[sys_flg], not A20_ST
3692 push cx
3693 push ds
3694 push si
3695 push es
3696 push di
3697 lds si,cs:low_mem
3698 les di,cs:high_mem
3699 mov cx,3
3700 cld
3701repe cmpsw
3702 pop di
3703 pop es
3704 pop si
3705 pop ds
3706 jcxz not_enabled
3707 pop cx
3708 or cs:[sys_flg],A20_ST
3709no_toggle:
3710 stc
3711 ret
3712not_enabled:
3713 pop cx
3714 clc
3715 ret
3716
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.
3720A20PS2:
3721assume ds:nothing,es:nothing,ss:nothing
3722 cli
3723;
3724; first separate disable operation from enable operation
3725;
3726 cmp ah,0ddh
3727 je disbl_PS2
3728;
3729; enabling the a20
3730;
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
3734 je set_it ;
3735 or cs:[sys_flg],A20_ST ; indicate that it was already set
3736ps2a20suc:
3737 clc
3738 sti
3739 ret
3740
3741set_it: push cx
3742 xor cx,cx
3743 or al,GATE_A20
3744 out PS2_PORTA,al ; set it
3745see_agn:
3746 in al,PS2_PORTA ; read status again
3747 test al,GATE_A20
3748 loopz see_agn
3749 pop cx
3750 jz ps2err
3751 clc
3752 sti
3753 ret
3754;
3755; disabling the ps2
3756;
3757disbl_PS2:
3758 test cs:[sys_flg],A20_ST
3759 jne ps2a20suc
3760;
3761 push cx
3762 xor cx,cx
3763 in al,PS2_PORTA
3764 and al,not GATE_A20
3765 out PS2_PORTA,al
3766see_agn1:
3767 in al,PS2_PORTA
3768 test al,GATE_A20
3769 loopnz see_agn1
3770 pop cx
3771 jnz ps2err
3772 clc
3773 sti
3774 ret
3775;
3776ps2err:
3777 stc
3778 sti
3779 ret
3780
3781
3782;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3783;;* VECA20 - Address enable/disable routine for Vectra family computers
3784;;
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.
3791;;
3792;; Uses al, flags
3793;; Has same results as A20
3794;;
3795VecA20:
3796 CLI
3797 call check_a20
3798 jc VecA20Suc
3799 call E_8042
3800 jnz VecA20Err
3801 mov al,ah ;sigle byte command is code passed
3802 out 64H,al
3803 call E_8042
3804 jnz VecA20Err
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.
3810 mov al,ah
3811 out 64H,al
3812 call E_8042
3813 jnz VecA20Err
3814VecA20Suc:
3815 sti
3816 clc
3817 ret
3818VecA20Err:
3819 sti
3820 stc
3821 ret
3822
3823
3824;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3825;;* A20S5 - Address enable/disable routine for the 6300 PLUS.
3826;;
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
3835;; is to continue.
3836;;
3837;; Uses ax, flags.
3838;; Returns with zero flag set.
3839;;
3840A20S5: test [reboot_flg],0ffh ;; sunilp
3841 jne a20s5boot ;; sunilp
3842 cli
3843 or al,al ;; if zero, then resetting processor
3844 jnz A20S5Next
3845 call RSet ;; must return with entry value of ax
3846A20S5Next:
3847 push dx ;; set/reset port
3848 mov dx,3f20h
3849 out dx,al
3850 pop dx
3851 clc ;; sunilp modification cy flag now important
3852 STI
3853 RET
3854
3855;;* a20S5BOOT - This code bypasses the processor reset on a reboot
3856;; of the 6300 PLUS. Otherwise the machine hangs.
3857a20s5BOOT: ;; use this code before reboot
3858 cli
3859 jmp short a20s5next
3860
3861OldStackSeg dw 0 ;; used during PLUS processor reset
3862 ;; to save the stack segment
3863
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
3868;;
3869;; Uses flags.
3870;;
3871RSet:
3872 pusha ;; save world
3873 push ds ;; save segments
3874 push es
3875 mov ax,BiosSeg ;; point to the bios segment
3876 mov ds,ax ;; ds -> 40h
3877assume ds:BiosSeg
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
3882assume ds:nothing
3883 mov [OldStackSeg],ss ;; save the stack segment, too
3884 mov dx,03f00h ;; reset the processor
3885 in ax,dx
3886 nop
3887 nop
3888 nop
3889 cli
3890 hlt ;; should never get here
3891ReturnBack:
3892 mov ss,[OldStackSeg] ;; start the recovery
3893assume ds:BiosSeg
3894 pop word ptr [RealLoc1+2]
3895 pop word ptr [RealLoc1]
3896 pop es
3897 pop ds
3898 popa
3899 ret
3900;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3901blkmov_386: ;_protect:
3902assume ds:int13code,es:nothing,ss:nothing
3903;
3904; Compute 32 bit address of start of I/O
3905;
3906 add ax,word ptr [base_addr]
3907 adc dx,word ptr [base_addr + 2]
3908;
3909 push cx
3910;
3911; Are we in virtual mode
3912;
3913 smsw cx
3914 test cx,01B ; is the pe bit set
3915 je pr_mode_tran
3916
3917 jmp int_15_tran
3918;
3919; Dispatch on function
3920;
3921pr_mode_tran:
3922 or bh,bh
3923 jz read_it_1
3924;
3925; Write
3926;
3927; Update ES descriptor with address of track in cache
3928;
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
3933;
3934; Update DS descriptor with transfer address
3935;
3936 mov ax,es
3937 mov cx,16
3938 mul cx
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
3943
3944
3945; Switch SI and DI for write transfer
3946
3947 mov si,di
3948 xor di,di
3949
3950 jmp short set_trans_1
3951
3952read_it_1:
3953;
3954; Update DS descriptor with address of track in cache
3955;
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
3960;
3961; Update ES descriptor with transfer address
3962;
3963 mov ax,es
3964 mov cx,16
3965 mul cx
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
3970;
3971; Keep SI and DI the same for read transfer
3972;
3973 xor si,si
3974
3975set_trans_1:
3976;
3977; Restore Transfer Count
3978;
3979 pop cx
3980
3981;
3982 mov ax,cs:[A20On]
3983 call A20
3984 jc nr_err_1
3985;
3986; we shall do the transfer 1024 words at a time
3987;
3988 db 66h
3989 push ax
3990 mov bx,cx
3991assume ds:nothing
3992pr_io_agn_1:
3993 mov cx,1024
3994 cmp bx,cx
3995 ja pr_strt_1
3996 mov cx,bx
3997pr_strt_1:
3998 sub bx,cx
3999 cli ; Un interruptable
4000 cld
4001 lgdt fword ptr emm_gdt
4002
4003
4004;
4005; Switch to protected mode
4006;
4007 db 66h,0Fh, 20h, 0 ;mov eax,cr0
4008 or ax,1
4009 db 66h,0Fh,22h, 0 ;mov cr0,eax
4010;
4011; Clear prefetch queue
4012;
4013 db 0eah ; far jump
4014 dw offset flush_prefetch
4015 dw cs_des - start_gdt
4016;
4017flush_prefetch:
4018 assume cs:nothing
4019;
4020; Initialize segment registers
4021;
4022 mov ax,ds_des - start_gdt
4023 mov ds,ax
4024 assume ds:nothing
4025 mov ax,es_des - start_gdt
4026 mov es,ax
4027 assume es:nothing
4028 shr cx,1 ; convert word count to dword count
4029 db 0f3h,066h,0a5h ; rep movsd
4030; rep movsw ; Move data
4031;
4032;
4033; Return to Real Mode
4034;
4035;
4036 db 66h,0Fh, 20h, 0 ; mov eax,cr0
4037 and ax,0FFFEh
4038 db 66h,0Fh, 22h, 0 ; mov cr0,eax
4039;
4040; Flush Prefetch Queue
4041;
4042 db 0EAh ; Far jump
4043 dw offset flushcs
4044cod_seg dw ? ; Fixed up at initialization time
4045 assume cs:Int13Code
4046flushcs:
4047;
4048 sti
4049; see if transfer done else go to do next block
4050;
4051 or bx,bx
4052 jne pr_io_agn_1
4053;
4054 db 66h
4055 pop ax
4056 mov ax,cs
4057 mov es,ax
4058 assume es:nothing
4059 mov ds,ax
4060 assume ds:Int13Code
4061
4062 mov cx,800h ; Retry this many times
4063offlp_1:
4064 mov ax,cs:[A20Off]
4065 call A20 ; Disable address line 20
4066 jnc offlp1_out
4067 loop offlp_1
4068offlp1_out:
4069 clc
4070 ret
4071
4072nr_err_1:
4073
4074 mov al,0AAh ; Drive not ready error
4075 stc
4076 ret
4077;
4078int_15_tran:
4079assume ds:int13code,es:nothing,ss:nothing
4080 or bh,bh
4081 jz read_it_2
4082;
4083; Write
4084;
4085; Update tgt descriptor with address of track in cache
4086;
4087 mov si,offset tgt
4088 mov [si].bas_0_15,ax
4089 mov [si].bas_16_23,dl
4090 mov [si].bas_24_31,dh
4091;
4092; Update src descriptor with transfer address
4093;
4094 mov ax,es
4095 mov cx,16
4096 mul cx
4097 add ax,di
4098 adc dx,0
4099 mov si,offset src
4100 mov [si].bas_0_15,ax
4101 mov [si].bas_16_23,dl
4102 mov [si].bas_24_31,dh
4103;
4104 jmp short set_trans_2
4105
4106read_it_2:
4107;
4108; Update src descriptor with address of track in cache
4109;
4110 mov si,offset src
4111 mov [si].bas_0_15,ax
4112 mov [si].bas_16_23,dl
4113 mov [si].bas_24_31,dh
4114;
4115; Update tgt descriptor with transfer address
4116;
4117 mov ax,es
4118 mov cx,16
4119 mul cx
4120 add ax,di
4121 adc dx,0
4122 mov si,offset tgt
4123 mov [si].bas_0_15,ax
4124 mov [si].bas_16_23,dl
4125 mov [si].bas_24_31,dh
4126;
4127set_trans_2:
4128;
4129; Restore Transfer Count
4130;
4131 pop bx
4132
4133;
4134; we shall do the transfer 1024 words at a time
4135;
4136pr_io_agn_2:
4137 mov cx,1024
4138 cmp bx,cx
4139 ja pr_strt_2
4140 mov cx,bx
4141pr_strt_2:
4142 sub bx,cx
4143 push cs
4144 pop es
4145 mov si,offset int15_gdt
4146 mov ax,emm_blkm shl 8
4147 int emm_int
4148 jc nr_err_1
4149;
4150;
4151; see if transfer done else fo to do next block
4152;
4153 or bx,bx
4154 je io_exit
4155;
4156 add [src.bas_0_15],2048
4157 adc [src.bas_16_23],0
4158 adc [src.bas_24_31],0
4159;
4160 add [tgt.bas_0_15],2048
4161 adc [tgt.bas_16_23],0
4162 adc [tgt.bas_24_31],0
4163;
4164 jmp pr_io_agn_2
4165io_exit:
4166 clc
4167 ret
4168;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4169;** SEG_SET - Set up a LOADALL segment descriptor as in REAL mode
4170;
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.
4175;
4176; SEE ALSO
4177; INTEL special documentation of LOADALL instruction
4178;
4179; ENTRY:
4180; DS:SI -> Seg register descriptor
4181; AX is seg register value
4182; EXIT:
4183; NONE
4184; USES:
4185; AX
4186;
4187; This routine is specific to TYPE 1 driver
4188;
4189
4190SEG_SET:
4191ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
4192 PUSH DX
4193 PUSH CX
4194 MOV CX,16
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
4198 POP CX
4199 POP DX
4200 RET
4201
4202;** FIX_DESCRIPTOR - Shuffle GTD IDT descriptors
4203;
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
4209; order.
4210;
4211; SEE ALSO
4212; INTEL special documentation of LOADALL instruction
4213; INTEL 80286 processor handbook description of SIDT instruction
4214;
4215; ENTRY:
4216; DS:SI points to IDT or GDT descriptor in SIDT form
4217; EXIT:
4218; DS:SI points to IDT or GDT descriptor in LOADALL form
4219; USES:
4220; 6 words of stack
4221;
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.
4225;
4226; Specific to TYPE 1 driver
4227;
4228
4229FIX_DESCRIPTOR:
4230ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
4231 PUSH WORD PTR [SI + 4]
4232 PUSH WORD PTR [SI + 2]
4233 PUSH WORD PTR [SI]
4234 POP WORD PTR [SI + 4]
4235 POP WORD PTR [SI]
4236 POP WORD PTR [SI + 2]
4237 RET
4238
4239;** DATA SPECIFIC TO THE LOADALL INSTRUCTION USAGE
4240;
4241; SWAP_80 and LOADALL_TBL are data elements specific to the use
4242; of the LOADALL instruction by TYPE 1 drivers.
4243;
4244
4245;
4246; Swap buffer for contents of 80:0
4247;
4248 EVEN ; Force word alignment of SWAP_80 and LOADALL_TBL
4249
4250SWAP_80 DB 102 DUP(?)
4251ssSave dw ?
4252
4253;
4254; LOADALL data buffer placed at 80:0
4255;
4256LOADALL_TBL LABEL BYTE
4257 DB 6 DUP(0)
4258LDSW DW ?
4259 DB 14 DUP (0)
4260TR DW 0
4261FLAGS DW 0 ; High 4 bits 0, Int off, Direction clear
4262 ; Trace clear. Rest don't care.
4263LIP DW OFFSET AFTER_LOADALL
4264LDT DW 0
4265LDSS DW 8000h
4266LSSS DW ?
4267LCSS DW ?
4268LESS DW ?
4269LDI DW ?
4270LSI DW ?
4271LBP DW ?
4272LSP DW ?
4273LBX DW ?
4274LDX DW ?
4275LCX DW ?
4276LAX DW 80H
4277ESDES SEGREG_DESCRIPTOR <>
4278CSDES SEGREG_DESCRIPTOR <>
4279SSDES SEGREG_DESCRIPTOR <>
4280DSDES SEGREG_DESCRIPTOR <>
4281GDTDES DTR_DESCRIPTOR <>
4282LDTDES DTR_DESCRIPTOR <0D000H,0,0FFH,0088H>
4283IDTDES DTR_DESCRIPTOR <>
4284TSSDES DTR_DESCRIPTOR <0C000H,0,0FFH,0800H>
4285
4286;** TRUE LOCATION OF ABOVE_PID
4287;
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:
4294;
4295; MOV DX,[ABOVE_PID]
4296;
4297; Would have to be "fixed up" when we moved the ABOVE_BLKMOV
4298; code into its final location.
4299;
4300
4301ABOVE_PID EQU WORD PTR $ - 2 ; TRUE location of ABOVE_PID
4302
4303;
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
4307;
4308DRIVE_END LABEL WORD
4309
4310
4311BREAK <INT 19/9 Handlers>
4312
4313;
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.
4317;
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.
4324;
4325; The drive TYPE dependant piece of code works as follows:
4326;
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
4330; code.
4331;
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
4346; code.
4347;
4348
4349;
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.
4353;
4354OLD_19 LABEL DWORD
4355 DW -1
4356 DW -1
4357
4358OLD_9 LABEL DWORD
4359 DW -1
4360 DW -1
4361;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4362; modification to meet new memory allocation standard
4363OLD_15 LABEL DWORD
4364 DW -1
4365 DW -1
4366int15_size dw 0
4367;
4368;
4369INT_15:
4370ASSUME DS:NOTHING,SS:NOTHING,ES:NOTHING
4371;
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
4375;
4376; inputs: ah = 88h is of interest
4377; outputs: ax = size of extended memory allocated by all before and
4378; including us
4379; regs used: flags
4380;
4381 pushf
4382 cmp ah,88h
4383 je mem_det
4384 popf
4385 jmp [old_15]
4386mem_det:
4387 mov ax,[int15_size]
4388 popf
4389 clc
4390 sti
4391 iret
4392;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4393;** INT 9 Keyboard handler
4394;
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.
4401;
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.
4405;
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.
4412;
4413;
4414; SEE ALSO
4415; INT 9 IBM ROM code in ROM BIOS listing of
4416; IBM PC Technical Reference manual for any PC family member
4417;
4418; ENTRY
4419; NONE
4420; EXIT
4421; NONE, via OLD_9
4422; USES
4423; FLAGS
4424;
4425; THIS CODE IS USED BY TYPE 1,2 drivers.
4426;
4427
4428INT_9:
4429ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
4430 PUSH AX
4431 PUSH DS
4432 IN AL,60H
4433 CMP AL,83 ; DEL key?
4434 JNZ CHAIN ; No
4435 XOR AX,AX
4436 MOV DS,AX
4437 MOV AL,BYTE PTR DS:[417H] ; Get KB flag
4438 NOT AL
4439 TEST AL,0CH ; Ctrl Alt?
4440 JNZ CHAIN ; No
4441 MOV [INT_13_BUSY],1 ; Exclude
4442;
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.
4449;
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
4454 MOV AL,20H
4455 OUT 20H,AL ; EOI the keyboard int
4456 CALL FLUSH_WHOLE_CACHE_SAV ; Flush cache
4457NO_REBOOT_FLUSH:
4458;
4459 CALL RESET_SYSTEM ; Ctrl Alt DEL
4460 ;
4461 ; Reset INT 13 vector to turn cache off
4462 ;
4463 MOV AX,WORD PTR [OLD_13]
4464 CLI
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
4468 ;
4469 ; Reset INT 1C vector to turn cache off
4470 ;
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
4475 MOV [INT_13_BUSY],0
4476CHAIN:
4477 POP DS
4478 POP AX
4479 JMP [OLD_9]
4480
4481;** INT 19 Software re-boot handler
4482;
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.
4489;
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.
4495;
4496; SEE ALSO
4497; INT 19 IBM ROM code in ROM BIOS listing of
4498; IBM PC Technical Reference manual for any PC family member
4499;
4500; ENTRY
4501; NONE
4502; EXIT
4503; NONE, via OLD_19
4504; USES
4505; FLAGS
4506;
4507; THIS CODE IS USED BY TYPE 1,2 drivers.
4508;
4509
4510INT_19:
4511ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
4512 MOV [INT_13_BUSY],1 ; Exclude
4513 cmp [reboot_flush],0 ;
4514 je no_flush
4515 CALL FLUSH_WHOLE_CACHE_SAV ; Flush out the cache
4516no_flush:
4517 CALL RESET_SYSTEM
4518 PUSH AX
4519 PUSH DS
4520 XOR AX,AX
4521 MOV DS,AX
4522 MOV AX,WORD PTR [OLD_13]
4523 CLI
4524 ;
4525 ; Reset INT 13 vector to trun cache off
4526 ;
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
4530 ;
4531 ; Reset INT 1C vector to turn cache off
4532 ;
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
4537 ;
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.
4540 ;
4541 ; NOTE THAT WE RESET VECTORS DIRECTLY!!!!!!!!!!!!!!!!!!
4542 ; We are not sure that DOS is reliable enough to call.
4543 ;
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]
4557 jne res_15
4558 cmp ax,-1
4559 je skip_res
4560res_15:
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
4564;
4565skip_res:
4566 POP DS
4567 POP AX
4568 MOV [INT_13_BUSY],0
4569 JMP [OLD_19]
4570
4571;** RESET_SYSTEM perform TYPE 1 (/E) driver specific reboot code
4572;
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
4578; place.
4579;
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.
4583;
4584; ENTRY
4585; NONE
4586; EXIT
4587; NONE
4588; USES
4589; NONE
4590;
4591; This code is specific to TYPE 1 drivers
4592;
4593
4594RESET_SYSTEM:
4595ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
4596 JMP SHORT TRUE_START
4597
4598MY_EMM_REC DW 0 ; Offset into 1K EMM_CTRL of my record
4599
4600TRUE_START:
4601 PUSHA
4602 mov cs:[reboot_flg],0ffh ; set the reboot flag
4603 cmp cs:[my_emm_rec],0 ; was an emm record allocated?
4604 je reset_skip
4605 PUSH DS
4606 PUSH ES
4607;
4608; reset base_addr to be address of emm ctrl sector
4609;
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
4614 ;
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
4618 ;
4619 mov bx,cs
4620 mov es,bx ;
4621 mov di,cs:[track_buffer_ptr]
4622 mov cx,512
4623 xor ax,ax
4624 xor dx,dx
4625 mov bh,0 ; read
4626 push es
4627 push di
4628 call blkmov
4629 pop di
4630 pop es
4631 jc finish_reset
4632;
4633; fix the flags for my emm record so that it is no longer in use
4634;
4635 mov bx,cs:[my_emm_rec]
4636 add bx,di
4637 and es:[bx.emm_flags],not emm_isdriver
4638;
4639; write out the modified emm record out to memory
4640;
4641 xor ax,ax
4642 xor dx,dx
4643 mov bh,1 ; write
4644 call blkmov
4645finish_reset:
4646 POP ES
4647 POP DS
4648reset_skip:
4649 POPA
4650 RET
4651
4652;
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
4657; RESET_INCLUDE.
4658;
4659RESET_INCLUDE LABEL BYTE
4660
4661;
4662; This data is only used at INIT, but it must be protected from overwrite
4663; by the DO_INIT code.
4664;
4665TERM_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
4668
4669;
4670; THIS CODE MUST BE IN RESIDENT PORTION BECAUSE IT WRITES IN THE AREA
4671; OCCUPIED BY THE DISPOSABLE INIT CODE.
4672;
4673
4674;** DO_INIT - Initialize cache structures to "empty"
4675;
4676DO_INIT:
4677ASSUME DS:INT13CODE
4678 MOV AX,[SECTRACK]
4679 MOV CL,9
4680 SHL AX,CL ; AX is bytes per track buffer
4681 MOV BX,[CACHE_CONTROL_PTR]
4682 MOV CX,[TTRACKS]
4683 MOV [CACHE_HEAD],BX
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
4688 MOV DI,BX
4689 ADD BX,SIZE CACHE_CONTROL ; Next structure
4690 DEC CX ; One less to do
4691 JCXZ SETDONE ; one buffer in cache
4692SETLOOP:
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]
4697 ADD DX,AX
4698 MOV WORD PTR [BX.BASE_OFFSET],DX
4699 MOV DX,WORD PTR [DI.BASE_OFFSET+2]
4700 ADC DX,0
4701 MOV WORD PTR [BX.BASE_OFFSET+2],DX
4702 MOV DI,BX
4703 ADD BX,SIZE CACHE_CONTROL ; Next structure
4704 LOOP SETLOOP
4705SETDONE:
4706 MOV [DI.FWD_LRU_LNK],-1
4707 MOV [CACHE_TAIL],DI ; That is the tail
4708;
4709; NOTE FALL THROUGH!!!!!!!
4710;
4711
4712;** SETBPB - Set INIT packet I/O return values
4713;
4714; This entry is used to set the INIT packet Break address
4715;
4716; ENTRY
4717; TERM_ADDR set to device end
4718; EXIT
4719; through DEVEXIT
4720; USES
4721; DS, BX, CX
4722;
4723; COMMON TO TYPE 1, 2 drivers
4724;
4725
4726SETBPB:
4727ASSUME DS:NOTHING
4728 ;
4729 ; 7. Set the return INIT I/O packet values
4730 ;
4731 LDS BX,[PTRSAV]
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
4736 JMP DEVEXIT
4737
4738
4739 EVEN ; Make sure we get word alignment of the track
4740 ; buffer.
4741
4742;
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
4758; down quite a bit.
4759;
4760
4761;
4762; Cylinder and hd/drv of track in track buffer
4763;
4764TRACK_BUFFER_CYLN DW -1
4765TRACK_BUFFER_HDDR DW -1
4766
4767;
4768; Pointer to track buffer. May be adjusted for DMA boundary error prevention.
4769;
4770TRACK_BUFFER_PTR DW OFFSET TRACK_BUFFER
4771
4772;
4773; Cache structure pointers
4774;
4775
4776;
4777; Pointer to cache structures. This ends up being right after TRACK_BUFFER.
4778;
4779CACHE_CONTROL_PTR DW ?
4780
4781;
4782; Cache head and tail pointers
4783;
4784CACHE_HEAD DW ?
4785CACHE_TAIL DW ?
4786
4787;
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).
4793;
4794TRACK_BUFFER DB (512 * 2 + 16) DUP(0)
4795 ; we really don't need those 16 bytes, i
4796 ; am just paranoid
4797;
4798; The TERM_ADDR for the device will be set somewhere "around" here
4799; by the init code
4800;
4801
4802BREAK <COMMON INIT CODE>
4803
4804;** DISPOSABLE INIT DATA
4805;
4806; INIT data which need not be part of resident image
4807;
4808
4809U_SWITCH db 0 ;; upper extended memory requested on 6300 PLUS
4810
4811EXT_K DW ? ; Size in K of Extended memory.
4812
4813NUM_ARG DB 1 ; Counter for numeric
4814 ; arguments bbbb.
4815
4816GOTSWITCH DB 0 ; Bit map of switches seen
4817
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4827sys_det proc near
4828;
4829; author: sunilp, august 1, 1987. thanks to rickha for most of this
4830; routine.
4831;
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.
4835;
4836; inputs: none
4837;
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.
4841;
4842; registers used: ax,es,flags
4843;----------------------------------
4844; Clear the state of the system flag
4845;
4846assume 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.
4851;
4852 push ax ; ax has 0
4853 popf ; try to put that in the flags
4854 pushf
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.
4861;
4862 mov ax,0F000h ; N: try to set the high bits
4863 push ax
4864 popf ; ... in the flags
4865 pushf
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.
4872;
4873cpu_386:
4874 pushf ; clear
4875 pop ax ; NT
4876 and ax,not 0F000h ; and
4877 push ax ; IOPL
4878 popf ; bits
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.
4882;
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.
4892;
4893 or [sys_flg],M_386 ; set 386 bit
4894 jmp short PS2Test
4895
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
4899;
4900cpu_286:
4901 mov ax,0ffffh
4902 mov es,ax
4903 cmp byte ptr es:[0eh],0fch ; AT model byte
4904 jne cpu_err ; if not abort
4905;
4906 or [sys_flg],M_286 ; set 286 flag bit
4907;
4908;
4909; Determine if this is a PS/2 system
4910;
4911PS2Test:
4912 call IsPS2Machine
4913 or ax,ax
4914 jz NCRTest
4915 or [sys_flg],M_PS2
4916
4917NCRTest:
4918 call IsNCRMachine
4919 or ax,ax
4920 jz cpu_suc
4921
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.
4924 ; ChipA 06-16-88
4925 mov cs:[A20On],0D790h
4926 mov cs:[A20Off],0D500h
4927
4928;----------------------------------
4929; success exit:--
4930cpu_suc:
4931 clc
4932 ret
4933
4934;----------------------------------
4935; error exit:--
4936cpu_err:
4937 stc
4938 ret
4939;
4940sys_det endp
4941
4942
4943;*--------------------------------------------------------------------------*
4944;* *
4945;* IsPS2Machine HARDWARE DEP. *
4946;* *
4947;* Check for PS/2 machine *
4948;* *
4949;* ARGS: None *
4950;* RETS: AX = 1 if we're on a valid PS/2 machine, 0 otherwise *
4951;* REGS: AX and Flags clobbered *
4952;* *
4953;*--------------------------------------------------------------------------*
4954
4955IsPS2Machine proc near
4956
4957 mov ah,0C0h ; Get System Description Vector
4958 stc
4959 int 15h
4960 jc short IPMNoPS2 ; Error? Not a PS/2.
4961
4962 ; Are we on a PS/2 Model 35?
4963 cmp es:[bx+2],09FCh
4964 je short IPMFoundIt ; Yup, use the PS/2 method
4965
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
4969 jz short IPMNoPS2
4970
4971IPMFoundIt: mov ax,1
4972 ret
4973
4974IPMNoPS2: xor ax,ax
4975 ret
4976
4977IsPS2Machine endp
4978
4979
4980;*--------------------------------------------------------------------------*
4981;* *
4982;* IsNCRMachine HARDWARE DEP. *
4983;* *
4984;* Check for NCR machine *
4985;* *
4986;* ARGS: None *
4987;* RETS: AX = 1 if we're on a valid NCR machine, 0 otherwise *
4988;* REGS: AX and Flags clobbered *
4989;* *
4990;*--------------------------------------------------------------------------*
4991
4992; Look for 'NC' at F000:FFEA
4993
4994IsNCRMachine proc near
4995
4996 mov ax,0F000h
4997 mov es,ax
4998 mov ax,word ptr es:[0FFEAh]
4999 cmp ax,'CN'
5000 je INMFoundIt
5001 xor ax,ax
5002 ret
5003
5004INMFoundIt: mov ax,1
5005 ret
5006
5007IsNCRMachine endp
5008
5009
5010;******************************************************************************
5011; IS_B0 - check for 386-B0
5012;
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.
5021;
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
5025;
5026; ENTRY:
5027; EXIT:
5028; USED: AX, flags
5029; STACK:
5030;------------------------------------------------------------------------------
5031is_b0 proc near
5032 push bx
5033 push cx
5034 push dx
5035 push ds
5036
5037 xor bx,bx
5038 mov ds,bx ; DS = 0000 (real mode IDT)
5039assume ds:R_Mode_IDT
5040 push [bx+(6*4)]
5041 pop cs:[int6_save] ; save old INT 6 offset
5042 push [bx+(6*4)+2]
5043 pop cs:[int6_save+2] ; save old INT 6 segment
5044
5045 mov word ptr [bx+(6*4)],offset int6
5046 mov [bx+(6*4)+2],cs ; set vector to new INT 6 handler
5047;
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.
5053;
5054 xor ax,ax
5055 mov dx,ax
5056 mov cx,0FF00h ; Extract length (CL)=0, CX=non-zero
5057 db 0Fh,0A6h,0CAh ; XBTS CX,DX,AX,CL
5058
5059 xor bx,bx
5060 mov ds,bx ; DS = 0000 (real mode IDT)
5061 push cs:[int6_save] ; restore original INT 6 offset
5062 pop [bx+(6*4)] ;
5063 push cs:[int6_save+2] ; restore original INT 6 segment
5064 pop [bx+(6*4)+2]
5065
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
5069ib_exit:
5070 cmc ; flip carry tense
5071 pop ds
5072 pop dx
5073 pop cx
5074 pop bx
5075 ret ; *** RETURN ***
5076is_b0 endp
5077;
5078; Temporary INT 6 handler - assumes the cause of the exception was the
5079; attempted execution of an XTBS instruction.
5080;
5081int6 proc near
5082 push bp
5083 mov bp,sp
5084 add word ptr [bp+2],3 ; bump IP past faulting instruction
5085 pop bp
5086 iret ; *** RETURN ***
5087int6_save dw 0000,0000
5088int6 endp
5089;***************************************************************************
5090
5091;** PRINT - Print a "$" terminated message on stdout
5092;
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.
5097;
5098; ENTRY:
5099; DX pointer to "$" terminated message (INT13CODE relative)
5100; EXIT:
5101; NONE
5102; USES:
5103; AX
5104;
5105; COMMON TO TYPE 1, 2, 3, 4 drivers
5106;
5107
5108PRINT:
5109ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
5110 PUSH DS
5111 PUSH CS
5112 POP DS
5113 MOV AH,Std_Con_String_Output
5114 INT 21H
5115 POP DS
5116 RET
5117
5118;** ITOA - Print Decimal Integer on stdout
5119;
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
5122; 0 prints as "0".
5123;
5124; Routine uses divide instruction and a recursive call. Maximum
5125; recursion is four (five digit number) plus one word on stack
5126; for each level.
5127;
5128; ENTRY AX has binary value to be printed
5129; EXIT NONE
5130; USES AX,CX,DX,FLAGS
5131;
5132; COMMON TO TYPE 1, 2, 3, 4 drivers
5133;
5134
5135ITOA:
5136ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
5137
5138 MOV CX,10
5139 XOR DX,DX
5140 DIV CX ; DX is low digit, AX is higher digits
5141 OR AX,AX
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
5146PRINT_THIS_DIGIT:
5147 ADD DL,"0" ; Convert to ASCII
5148 MOV AH,Std_CON_Output
5149 INT 21H
5150 RET
5151
5152
5153;** INT13$INIT - Device Driver Initialization routine
5154;
5155; INT13 Initialization routine. This is the COMMON initialization
5156; code used by ALL driver TYPEs. Its jobs are to:
5157;
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
5167;
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.
5174;
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.
5189;
5190; Step 1 initializes the segment part of TERM_ADDR to the correct
5191; value for type 1, 2 drivers.
5192;
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
5197; on DOS 2.X.
5198;
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.
5209;
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:
5215;
5216; DEV_SIZE set to TRUE size of device
5217; BASE_ADDR set to TRUE start of device so BLKMOV
5218; can be called
5219; BASE_RESET set so DISK_ABORT can be called
5220; TERM_ADDR set to correct end of device
5221;
5222; Step 5 makes the status report display of DEVICE SIZE and other info.
5223;
5224; Step 6 sets the INIT I/O packet return values for Break address.
5225;
5226;
5227; SEE ALSO
5228; MS-DOS Technical Reference manual section on
5229; Installable Device Drivers
5230;
5231; ENTRY from INT13$IN
5232; EXIT Through DEVEXIT
5233; USES ALL
5234;
5235; COMMON TO TYPE 1, 2 drivers
5236;
5237
5238INT13$INIT:
5239ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
5240 ;
5241 ; 1. Initialize various global values
5242 ;
5243 MOV WORD PTR [TERM_ADDR + 2],CS
5244 ;
5245 ; 2. Check for correct DOS version and do changes to the device
5246 ; based on the DOS version if needed.
5247 ;
5248 CLD
5249 MOV AH,GET_VERSION
5250 INT 21H
5251 XCHG AH,AL
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
5258BADVER:
5259 MOV DX,OFFSET BADVERMES
5260 JMP DEVABORT
5261
5262VER2X:
5263 AND [DEVATS],NOT DEVOPCL ; No such bit in 2.X
5264 MOV BYTE PTR [INT13TBL],11 ; Fewer functions too
5265VER_OK:
5266
5267;;
5268;; 2.5 Check here for 6300 PLUS machine. First look for Olivetti copyright,
5269;; and if found, check id byte at f000:fffd.
5270;;
5271
5272 push es ;; Olivetti Machine?
5273 mov ax,0fc00h ;; Look for 'OL' at fc00:50
5274 mov es,ax
5275 cmp es:[0050h],'LO'
5276 jnz notS5 ;; not found
5277 mov ax,0f000h
5278 mov es,ax
5279 cmp word ptr es:[0fffdh],0fc00h ;; look for 6300 plus
5280 jnz notS5
5281 mov [S5_FLAG],S_OLIVETTI ;; yep, set flag
5282notS5:
5283
5284;; Check here for an HP Vectra machine. Look for HP id byte.
5285;;
5286 mov ax,0f000H
5287 mov es,ax
5288 cmp es:[0f8H],'PH'
5289 jnz notHP
5290 mov [S5_FLAG],S_VECTRA
5291notHP:
5292 pop es
5293
5294;
5295; 3. Set OLD_13, OLD_1C and Parse the command line and set values accordingly
5296;
5297 MOV AX,(Get_Interrupt_Vector SHL 8) OR 13H
5298 INT 21H
5299 MOV WORD PTR [OLD_13],BX
5300 MOV WORD PTR [OLD_13 + 2],ES
5301 MOV AX,(Get_Interrupt_Vector SHL 8) OR 1CH
5302 INT 21H
5303 MOV WORD PTR [OLD_1C],BX
5304 MOV WORD PTR [OLD_1C + 2],ES
5305 LDS SI,[PTRSAV]
5306ASSUME DS:NOTHING
5307 MOV DX,OFFSET HEADERMES
5308 CALL PRINT
5309 LDS SI,[SI.INIT_BPB] ; DS:SI points to config.sys
5310SKIPLP1: ; Skip leading delims to start of name
5311 LODSB
5312 CMP AL," "
5313 JZ SKIPLP1
5314 CMP AL,9
5315 JZ SKIPLP1
5316 CMP AL,","
5317 JZ SKIPLP1
5318 JMP SHORT SKIPNM
5319
5320ARGS_DONEJ:
5321 JMP ARGS_DONE
5322
5323SWITCHJ:
5324 JMP SWITCH
5325
5326SKIPLP2: ; Skip over device name
5327 LODSB
5328SKIPNM:
5329 CMP AL,13
5330 JZ ARGS_DONEJ
5331 CMP AL,10
5332 JZ ARGS_DONEJ
5333 CMP AL," "
5334 JZ FIRST_ARG
5335 CMP AL,9
5336 JZ FIRST_ARG
5337 CMP AL,","
5338 JZ FIRST_ARG
5339 CMP AL,0 ; Need this for 2.0 2.1
5340 JNZ SKIPLP2
5341SCAN_LOOP: ; PROCESS arguments
5342 LODSB
5343FIRST_ARG:
5344 OR AL,AL ; Need this for 2.0 2.1
5345 JZ ARGS_DONEJ
5346 CMP AL,13
5347 JZ ARGS_DONEJ
5348 CMP AL,10
5349 JZ ARGS_DONEJ
5350 CMP AL," "
5351 JZ SCAN_LOOP
5352 CMP AL,9
5353 JZ SCAN_LOOP
5354 CMP AL,","
5355 JZ SCAN_LOOP
5356 CMP AL,"/"
5357 JZ SWITCHJ
5358 CMP AL,"0"
5359 JB BAD_PARMJ
5360 CMP AL,"9"
5361 JA BAD_PARMJ
5362 DEC SI
5363 CALL GETNUM
5364 CMP [NUM_ARG],1
5365 JA BAD_PARMJ ; Only 1 numeric argument
5366SET_SIZE:
5367 CMP BX,128
5368 JB BAD_PARMJ
5369 CMP BX,8192
5370 JA BAD_PARMJ
5371 MOV [DEV_SIZE],BX
5372;A
5373 mov [current_dev_size],bx
5374;A
5375 JMP SHORT NUM_DONE
5376
5377BAD_PARMJ:
5378 JMP SHORT BAD_PARM
5379
5380NUM_DONE:
5381 INC [NUM_ARG] ; Next numeric argument
5382SCAN_LOOPJ:
5383 JMP SCAN_LOOP
5384
5385BAD_PARM:
5386 MOV DX,OFFSET ERRMSG1
5387DEVABORT:
5388 CALL PRINT
5389DEVABORT_NOMES:
5390; INC [NULDEV] ;Indicate NUL device
5391; MOV WORD PTR [TERM_ADDR],OFFSET ERROR_END ;Minimul null device
5392; JMP SETBPB ;and return
5393 LDS BX,[PTRSAV]
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
5397 MOV [DEVATS],0
5398 JMP DEVEXIT
5399
5400SWITCH:
5401 LODSB
5402 OR AL,20H
5403
5404if WINDOWS_SWITCHES eq 0
5405 CMP AL,"e"
5406 JNZ ABOVE_TEST
5407EXT_SET:
5408 TEST [GOTSWITCH],SWITCH_E + SWITCH_A
5409 JNZ BAD_PARM
5410 OR [GOTSWITCH],SWITCH_E
5411 MOV [DRIVER_SEL],0
5412 JMP SCAN_LOOP
5413
5414ABOVE_TEST:
5415
5416endif ; WINDOWS_SWITCHES eq 0
5417
5418;; Added for /u switch
5419 cmp al,'u' ;; Look for U switch for PLUS
5420 jnz A_TEST
5421 cmp [S5_FLAG],S_OLIVETTI ;; No good unless PLUS
5422 jne bad_parm
5423 TEST [GOTSWITCH],SWITCH_A ;; Already have switch A ?
5424 JNZ BAD_PARM
5425 cmp [U_SWITCH],0
5426 jne bad_parm
5427 dec [U_SWITCH]
5428 jmp scan_loop
5429A_TEST:
5430;;
5431 CMP AL,"a"
5432
5433if WINDOWS_SWITCHES
5434 jnz bad_parm
5435else
5436 JNZ DIS_TEST
5437endif ;WINDOWS_SWITCHES
5438
5439ABOVE_SET:
5440 TEST [GOTSWITCH],SWITCH_A ; Was SWITCH_E + SWITCH_A
5441 JNZ BAD_PARM
5442;; added for /u switch
5443 cmp [U_SWITCH],0
5444 jne bad_parm
5445;;
5446 OR [GOTSWITCH],SWITCH_A
5447 MOV [DRIVER_SEL],1
5448 JMP SCAN_LOOP
5449
5450if WINDOWS_SWITCHES eq 0
5451
5452DIS_TEST:
5453 CMP AL,"d"
5454 JNZ W_TEST
5455DIS_SET:
5456 TEST [GOTSWITCH],SWITCH_D
5457 JNZ BAD_PARM
5458 OR [GOTSWITCH],SWITCH_D
5459 MOV [ENABLE_13],0
5460 JMP SCAN_LOOP
5461
5462W_TEST:
5463 CMP AL,"w"
5464 JNZ T_TEST
5465 LODSW
5466 OR AL,20H
5467 CMP AX,":c"
5468 JNZ WT_TEST
5469 LODSW
5470 OR AX,2020H
5471 CMP AX,"fo"
5472 JNZ BAD_PARM
5473 LODSB
5474 OR AL,20H
5475 CMP AL,"f"
5476 JNZ BAD_PARMJX
5477WC_SET:
5478 TEST [GOTSWITCH],SWITCH_WC
5479 JNZ BAD_PARMJX
5480 OR [GOTSWITCH],SWITCH_WC
5481 MOV [WRITE_BUFF],0
5482 JMP SCAN_LOOP
5483
5484WT_TEST:
5485 CMP AX,":t"
5486 JNZ BAD_PARMJX
5487 LODSW
5488 OR AX,2020H
5489 CMP AX,"no"
5490 JNZ BAD_PARMJX
5491WT_SET:
5492 TEST [GOTSWITCH],SWITCH_WT
5493 JNZ BAD_PARMJX
5494 OR [GOTSWITCH],SWITCH_WT
5495 MOV [WRITE_THROUGH],1
5496 JMP SCAN_LOOP
5497
5498BAD_PARMJX:
5499 JMP BAD_PARM
5500
5501T_TEST:
5502 CMP AL,"t"
5503 JNZ R_TEST
5504 LODSW
5505 CMP AL,":"
5506 JNZ BAD_PARMJX
5507 CMP AH,"0"
5508 JB BAD_PARMJX
5509 CMP AH,"9"
5510 JA BAD_PARMJX
5511 DEC SI
5512 CALL GETNUM
5513T_SET:
5514 TEST [GOTSWITCH],SWITCH_T
5515 JNZ BAD_PARMJX
5516 OR [GOTSWITCH],SWITCH_T
5517 MOV [TICK_SETTING],BX
5518 JMP SCAN_LOOP
5519
5520R_TEST:
5521 CMP AL,"r"
5522 JNZ C_TEST
5523 LODSW
5524 OR AH,20H
5525 CMP AX,"o:"
5526 JNZ BAD_PARMJX
5527 LODSB
5528 OR AL,20H
5529 CMP AL,"n"
5530 JNZ BAD_PARMJX
5531 TEST [GOTSWITCH],SWITCH_R
5532 JNZ BAD_PARMJX
5533 OR [GOTSWITCH],SWITCH_R
5534 MOV [REBOOT_FLUSH],1
5535 JMP SCAN_LOOP
5536
5537C_TEST:
5538 CMP AL,"c"
5539 JNZ BAD_PARMJX
5540 LODSW
5541 OR AH,20H
5542 CMP AX,"o:"
5543 JNZ BAD_PARMJX
5544 LODSB
5545 OR AL,20H
5546 CMP AL,"n"
5547 JNZ BAD_PARMJX
5548 TEST [GOTSWITCH],SWITCH_C
5549 JNZ BAD_PARMJX
5550 OR [GOTSWITCH],SWITCH_C
5551 MOV [ALL_CACHE],1
5552 JMP SCAN_LOOP
5553endif ;WINDOWS_SWITCHES eq 0
5554
5555ARGS_DONE:
5556;
5557; 4. Call a TYPE specific INIT routine based on the Parse
5558; to set up a specific driver TYPE.
5559;
5560 PUSH CS
5561 POP DS
5562ASSUME DS:INT13CODE
5563 MOV AL,[DRIVER_SEL] ; Find out which init to call
5564 OR AL,AL
5565 JNZ NEXTV
5566 CALL AT_EXT_INIT
5567 JMP SHORT INI_RET
5568
5569NEXTV:
5570 CALL ABOVE_INIT
5571INI_RET:
5572 JNC I001
5573 JMP DEVABORT_NOMES
5574
5575I001:
5576DRIVE_SET:
5577 ;
5578 ; update the current device size
5579 ;
5580 mov ax,[dev_size]
5581 mov [current_dev_size],ax
5582 ;
5583 ; 6. Print out report of INT13 parameters
5584 ;
5585 MOV DX,OFFSET STATMES1
5586 CALL PRINT
5587 MOV AX,[DEV_SIZE]
5588 CALL ITOA
5589 MOV DX,OFFSET STATMES1E
5590 CMP [DRIVER_SEL],0
5591 JZ PTYPX
5592 MOV DX,OFFSET STATMES1A
5593PTYPX:
5594 CALL PRINT
5595 MOV DX,OFFSET STATMES2
5596 CALL PRINT
5597 MOV AX,[TTRACKS]
5598 CALL ITOA
5599 MOV DX,OFFSET STATMES3
5600 CALL PRINT
5601 MOV AX,[SECTRACK]
5602 CALL ITOA
5603 MOV DX,OFFSET STATMES4
5604 CALL PRINT
5605
5606ifdef OMTI
5607 mov dx,offset omti_msg
5608 call print
5609endif
5610IF DEBUG
5611 MOV DX,OFFSET STATMES5
5612 CALL PRINT
5613 MOV AX,CS
5614 CALL ITOA
5615 MOV DX,OFFSET STATMES6
5616 CALL PRINT
5617ENDIF
5618 ;
5619 ; Turn on the cache by chaining INT 13, and INT 1C
5620 ;
5621 MOV DX,OFFSET INT_13_HANDLER
5622 MOV AX,(Set_Interrupt_Vector SHL 8) OR 13H
5623 INT 21H
5624; MOV DX,OFFSET INT_1C_HANDLER
5625; MOV AX,(Set_Interrupt_Vector SHL 8) OR 1CH
5626; INT 21H
5627 JMP DO_INIT
5628
5629;** DRIVEPARMS Initialize drive related cache parameters
5630;
5631; ENTRY
5632; Stuff set so that BLKMOV can be used to access cache memory
5633; DEV_SIZE set to TRUE cache size in K
5634; EXIT
5635; Carry Set
5636; Error, message already printed
5637; Carry clear
5638; TRACK_BUFFER_PTR adjusted for DMA error prevention
5639; CACHE_CONTROL_PTR set
5640; TERM_ADDR set
5641; TTRACKS set
5642; SECTRACK set
5643; SECTRKARRAY set
5644; HDARRAY set (SUNILP)
5645; USES
5646; ALL but DS
5647;
5648; COMMON TO TYPE 1, 2 drivers
5649;
5650;
5651NO_HARDFILES:
5652ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
5653 MOV DX,OFFSET NOHARD
5654PEER:
5655 CALL PRINT
5656 STC
5657 RET
5658
5659TRACK_TOO_BIG:
5660 MOV DX,OFFSET BIGTRACK
5661 JMP PEER
5662
5663DRIVEPARMS:
5664ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
5665 ;
5666 ; First figure out sec/track of any hardfiles
5667 ;
5668 MOV DL,80H
5669 MOV AH,8
5670 INT 13H
5671 JC NO_HARDFILES
5672 OR DL,DL
5673 JZ NO_HARDFILES
5674 AND CL,00111111B
5675 MOV [SECTRKARRAY],CL
5676 mov [hdarray],dh
5677 XOR CH,CH
5678 MOV [SECTRACK],CX
5679 MOV CL,DL
5680 DEC CX
5681 JCXZ FINISHED
5682 CMP CX,MAX_HARD_FILES - 1
5683 JBE DNUM_OK
5684 MOV CX,MAX_HARD_FILES - 1
5685DNUM_OK:
5686 MOV DL,81H
5687PARMLOOP:
5688 PUSH CX
5689 PUSH DX
5690 MOV AH,8
5691 INT 13H
5692 JC IGNORE_DRIVE
5693 AND CL,00111111B
5694 POP BX
5695 PUSH BX
5696 AND BX,0000000001111111B
5697 MOV [BX.SECTRKARRAY],CL
5698 mov [bx.hdarray],dh
5699 XOR CH,CH
5700 CMP CX,[SECTRACK]
5701 JBE IGNORE_DRIVE
5702 MOV [SECTRACK],CX
5703IGNORE_DRIVE:
5704 POP DX
5705 INC DL
5706 POP CX
5707 LOOP PARMLOOP
5708FINISHED:
5709 ;
5710 ; Figure out number of full tracks that fit in cache
5711 ;
5712 MOV AX,[SECTRACK]
5713 MOV CX,512
5714 MUL CX ; DX:AX = Bytes per track
5715 OR DX,DX
5716 JNZ TRACK_TOO_BIG
5717 MOV BX,AX ; BX is bytes per track
5718 MOV AX,[DEV_SIZE]
5719 MOV CX,1024
5720 MUL CX ; DX:AX = size of cache in bytes
5721 DIV BX ; AX is full tracks in cache
5722 MOV [TTRACKS],AX
5723 ;
5724 ; Figure out if we have a DMA boundary problem
5725 ;
5726 mov DX,DS ; Check for 64k boundary error
5727 shl DX,1
5728 shl DX,1
5729 shl DX,1
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
5734;
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.
5737;
5738 jnc NotWithin512
5739 add [TRACK_BUFFER_PTR],512 ; adjust
5740 jmp short SetCachest
5741
5742NotWithin512:
5743;
5744; DX is the physical low 16 bits of the proposed track buffer plus 511.
5745; See how many sectors fit up to boundary.
5746;
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
5750 sub AH,DH
5751;
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.
5756;
5757 mov al,ah
5758 xor ah,ah
5759 cmp AX,[SECTRACK] ; can we fit it in?
5760 jae SetCachest ; yes, buffer is OK
5761 inc ax ; Add 1
5762 mov cl,9 ; Mult by 512
5763 shl ax,cl
5764 add [TRACK_BUFFER_PTR],ax ; Adjust
5765SetCachest:
5766 ;
5767 ; Set pointer to cache control structures
5768 ;
5769 mov bx,[SECTRACK]
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
5775 mov ax,[TTRACKS]
5776 MUL cx
5777 add bx,ax
5778 ;
5779 ; Set TERM_ADDR
5780 ;
5781 mov word ptr [TERM_ADDR],bx
5782 CLC
5783 RET
5784
5785;** GETNUM - Read an unsigned integer
5786;
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.
5793;
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.
5797;
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.
5801;
5802; ENTRY:
5803; DS:SI -> ASCII text of number
5804; EXIT:
5805; BX is binary for number
5806; SI advanced to point to char after number
5807; USES:
5808; AX,BX,DX,SI
5809;
5810; COMMON TO TYPE 1, 2, 3, 4 drivers
5811;
5812
5813GETNUM:
5814ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
5815
5816 XOR BX,BX
5817GETNUM1:
5818 LODSB
5819 SUB AL,"0"
5820 JB NUMRET
5821 CMP AL,9
5822 JA NUMRET
5823 CBW
5824 XCHG AX,BX
5825 MOV DX,10
5826 MUL DX
5827 ADD BX,AX
5828 JMP GETNUM1
5829
5830NUMRET:
5831 DEC SI
5832 RET
5833
5834BREAK <INITIAL EMM control sector>
5835
5836;** flag to signify valid emm control record
5837;
5838valid_emm db 0
5839
5840;** INITIAL EMM_CTRL sector
5841;
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.
5847;
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.
5855;
5856; This data is appropriate to TYPE 1 drivers
5857;
5858
5859EMM_CONTROL LABEL BYTE
5860 DB "MICROSOFT EMM CTRL VERSION 1.00 CONTROL BLOCK "
5861 DW 0
5862 DW 0
5863 ; NULL 0th record
5864 DW EMM_ALLOC + EMM_ISDRIVER
5865 DW EMM_EMM
5866;; Note: When using upper extended memory on the PLUS, the value
5867;; at BASE_RESET + 2 is patched to FA during initialization.
5868;;
5869BASE_RESET LABEL DWORD ; RESMEM driver must patch this value
5870 DW EXTMEM_LOW + 1024
5871 DW EXTMEM_HIGH
5872 DW 0
5873
5874 DB 950 DUP(0)
5875 DB "ARRARRARRA"
5876
5877
5878BREAK <INT13 COMMON INIT ROUTINES>
5879
5880;** DISK_ABORT - De-install INT13 after init
5881;
5882; This routine MUST BE CALLED to de-install a INT13 cache
5883; if the de-installation takes place:
5884;
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.
5889;
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.
5894;
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.
5901;
5902; If this is a TYPE 2 driver, an ABOVE_DEALLOC call is made on
5903; ABOVE_PID.
5904;
5905; ENTRY:
5906; BASE_RESET valid if TYPE 1
5907; ABOVE_PID valid if TYPE 2
5908; EXIT:
5909; NONE
5910; USES:
5911; ALL but DS
5912;
5913; COMMON TO TYPE 1, 2 drivers
5914;
5915
5916DISK_ABORT:
5917ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
5918
5919 CMP [DRIVER_SEL],1
5920 JNZ NOT_ABOVE
5921AGAIN:
5922 ;
5923 ; TYPE 2, De-alloc the Above Board memory
5924 ;
5925 MOV DX,[ABOVE_PID]
5926 MOV AH,ABOVE_DEALLOC
5927 INT 67H
5928 CMP AH,ABOVE_ERROR_BUSY
5929 JZ AGAIN
5930 JMP SHORT RET002
5931
5932NOT_ABOVE:
5933 CMP [MY_EMM_REC],0 ; Need to turn off bit?
5934 JZ RET002 ; No
5935 ;
5936 ; TYPE 1, turn off EMM_ISDRIVER at MY_EMM_REC
5937 ;
5938 MOV AX,WORD PTR [BASE_RESET]
5939 MOV DX,WORD PTR [BASE_RESET + 2]
5940 SUB AX,1024 ; Backup to EMM_CTRL
5941 SBB DX,0
5942 MOV WORD PTR [BASE_ADDR],AX
5943 MOV WORD PTR [BASE_ADDR + 2],DX
5944 XOR BH,BH ; READ
5945 CALL CTRL_IO ; Get EMM_CTRL
5946 JC RET002
5947 MOV DI,OFFSET TRACK_BUFFER
5948 ADD DI,[MY_EMM_REC]
5949 AND [DI.EMM_FLAGS],NOT EMM_ISDRIVER ; Undo install
5950 MOV BH,1 ; WRITE
5951 CALL CTRL_IO ; EMM_CTRL back out
5952RET002:
5953 ;
5954 ; Reset INT 9, and/or INT 19 if OLD_19 is not -1
5955 ;
5956 PUSH DS
5957 LDS DX,[OLD_19]
5958ASSUME DS:NOTHING
5959 MOV AX,DS
5960 CMP AX,-1
5961 JNZ RESET_VECS
5962 CMP AX,DX
5963 JZ NO_VECS
5964RESET_VECS:
5965 MOV AX,(Set_Interrupt_Vector SHL 8) OR 19H
5966 INT 21H
5967;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5968; removed from smartdrv
5969; LDS DX,[OLD_9]
5970; MOV AX,(Set_Interrupt_Vector SHL 8) OR 9H
5971; INT 21H
5972;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5973 lds dx,[old_15]
5974 cmp ax,-1
5975 jne reset_15
5976 cmp ax,dx
5977 je no_vecs
5978reset_15:
5979 mov ax,(set_interrupt_vector shl 8) or 15h
5980 int 21h
5981NO_VECS:
5982 POP DS
5983 RET
5984
5985;** CTRL_IO - Read/Write the first 1024 bytes at BASE_ADDR
5986;
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
5995; TRACK_BUFFER.
5996;
5997; ENTRY:
5998; BH = 0 for READ, 1 for WRITE
5999; EXIT:
6000; TRACK_BUFFER filled in with 1024 bytes at BASE_ADDR
6001; USES:
6002; ALL but DS
6003;
6004; COMMON TO TYPE 1, 2 drivers
6005;
6006
6007CTRL_IO:
6008ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
6009 XOR DX,DX
6010 MOV AX,DX ; Offset 0
6011 MOV CX,512 ; 1024 bytes
6012 PUSH CS
6013 POP ES
6014 MOV DI,OFFSET TRACK_BUFFER
6015 PUSH DS
6016 CALL BLKMOV ; Read in EMM_CTRL
6017 POP DS
6018 RET
6019
6020;** MM_SETDRIVE - Look for/Init EMM_CTRL and DOS volume
6021;
6022; This routine is used by TYPE 1 drivers to check for/initialize
6023; the EMM_CTRL sector.
6024;
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
6029;
6030; ENTRY:
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
6036; EXIT:
6037; CARRY SET - error, message already printed
6038; CARRY CLEAR
6039; BASE_ADDR set for this drive
6040; DEV_SIZE set to TRUE size
6041;
6042; USES
6043; ALL but DS
6044;
6045; Used by TYPE 1 drivers
6046;
6047
6048MM_SETDRIVE:
6049ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
6050 XOR BH,BH ; READ
6051 CALL CTRL_IO ; Get EMM_CTRL
6052 MOV DX,OFFSET INIT_IO_ERR
6053 JC ERR_RET2
6054 CALL FIND_VDRIVE ; Snoop
6055 JC RET001
6056 PUSH ES ; Save EMM_BASE from EMM_REC
6057 PUSH DI
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
6063 MOV BH,1 ; WRITE
6064 CALL CTRL_IO ; Write EMM_CTRL back out
6065 MOV DX,OFFSET INIT_IO_ERR
6066 JC ERR_RET2P
6067mm_s$1:
6068 POP WORD PTR [BASE_ADDR] ; Set final correct BASE_ADDR
6069 POP WORD PTR [BASE_ADDR + 2]
6070 CLC
6071RET001:
6072 RET
6073
6074ERR_RET2P:
6075 ADD SP,4
6076ERR_RET2:
6077 CALL PRINT
6078 STC
6079 RET
6080
6081;** FIND_VDRIVE - Check out EMM_CTRL and alloc
6082;
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.
6092;
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.
6102;
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.
6108;
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.
6116;
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
6126; ELSE
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
6134; size back.
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.
6140; ELSE
6141; allocate appropriate amount off of EMM_AVAILK
6142; and add it to EMM_KSIZE and we're done.
6143; ELSE
6144; We can't do anything, so set DEV_SIZE
6145; to EMM_KSIZE and we're done.
6146; ELSE
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.
6153; ELSE
6154; SHRINK the allocation block by adding
6155; the extra memory back onto EMM_AVAILK
6156; and subtracting it from EMM_KSIZE and
6157; we're done
6158;
6159; ENTRY:
6160; SECTOR_BUFFER containes POSSIBLE EMM_CTRL sector
6161; MUST BE CHECKED
6162; EXT_K is set to size of extended memory
6163; DEV_SIZE is set to user requested device size
6164; EXIT:
6165; CARRY SET
6166; Error, message already printed
6167; CARRY CLEAR
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
6171; EMM_CTRL sector
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.
6175;
6176; USES:
6177; ALL but DS
6178;
6179; Specific to TYPE 1 drivers
6180;
6181; substancial modification to this routine, would have totally changed
6182; if it weren't for the olivetti memory
6183;
6184; we are going to be int15 guys from now except for the olivetti memory
6185;
6186FIND_VDRIVE:
6187ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
6188 PUSH CS
6189 POP ES
6190 MOV DI,OFFSET TRACK_BUFFER
6191 MOV SI,OFFSET EMM_CONTROL
6192 MOV CX,50
6193 CLD
6194 REPE CMPSB
6195 jnz no_emm_rec
6196; JNZ SETCTRL ; No EMM_CTRL
6197 ADD SI,EMM_TAIL_SIG - 50
6198 ADD DI,EMM_TAIL_SIG - 50
6199 MOV CX,10
6200 REPE CMPSB
6201 jnz no_emm_rec
6202; JNZ SETCTRL ; No EMM_CTRL
6203;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6204; with int15 guys around this is not feasible
6205; MOV DI,OFFSET TRACK_BUFFER
6206; MOV AX,[EXT_K]
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
6210;SETCTRL:
6211;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6212;
6213; modification sunilp
6214;
6215 dec [valid_emm] ; signal prescence of emm record
6216no_emm_rec:
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
6220;
6221; for olivetti u memory we still have to install according to ol' microsoft st
6222;
6223old_st:
6224 cmp [valid_emm],0h ; do we have a valid emm
6225 jne scan_dev ; if yes go to scan structures
6226set_ctrl: ; else we have to install a new one
6227 MOV DI,OFFSET TRACK_BUFFER
6228 PUSH DI
6229 MOV SI,OFFSET EMM_CONTROL
6230 MOV CX,1024/2
6231 REP MOVSW ; Move in initial EMM_CTRL
6232 POP DI
6233 MOV AX,[EXT_K]
6234 DEC AX ; Size in EMM_CTRL doesn't include EMM_CTRL
6235 MOV [DI.EMM_TOTALK],AX
6236 MOV [DI.EMM_AVAILK],AX
6237SCAN_DEV:
6238 MOV SI,OFFSET TRACK_BUFFER ; DS:SI points to EMM_CTRL
6239 MOV DI,SI
6240 ADD DI,EMM_RECORD ; DS:DI points to EMM records
6241 MOV CX,EMM_NUMREC
6242LOOK_REC:
6243 TEST [DI.EMM_FLAGS],EMM_ALLOC
6244 JNZ CHECK_SYS
6245 JMP GOT_FREE_REC ; Must alloc new region
6246
6247CHECK_SYS:
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]
6254 CMP CX,1
6255 JBE OK_SET_DEV ; If this is last record, must
6256 ; select this size
6257 CMP AX,[DEV_SIZE]
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.
6262 PUSH DI
6263 ADD DI,SIZE EMM_REC
6264 TEST [DI.EMM_FLAGS],EMM_ALLOC
6265 POP DI
6266 JZ SHRINK_BLOCK ; Next block is free, shrink
6267 MOV AX,[DEV_SIZE]
6268 JMP SHORT SET_2
6269
6270SHRINK_BLOCK:
6271 SUB AX,[DEV_SIZE] ; AX is amount to shrink
6272 ADD [SI.EMM_AVAILK],AX
6273 MOV AX,[DEV_SIZE]
6274 MOV [DI.EMM_KSIZE],AX
6275 JMP SHORT SET_2
6276
6277INSUFF_MEM: ; Size of found block is smaller
6278 ; than requested size.
6279 PUSH DI
6280 ADD DI,SIZE EMM_REC
6281 TEST [DI.EMM_FLAGS],EMM_ALLOC
6282 POP DI
6283 JNZ OK_SET_DEV ; Next block is NOT free, can't grow
6284TRY_TO_GROW_BLOCK:
6285 CMP [SI.EMM_AVAILK],0
6286 JZ OK_SET_DEV ; Need SPECIAL check for this case
6287 SUB AX,[DEV_SIZE]
6288 NEG AX ; AX is amount we would like to grow
6289 SUB [SI.EMM_AVAILK],AX
6290 JNC GOT_THE_MEM
6291 ADD AX,[SI.EMM_AVAILK] ; AX is MAX we can grow
6292 MOV [SI.EMM_AVAILK],0 ; We take all that's left
6293GOT_THE_MEM:
6294 ADD [DI.EMM_KSIZE],AX
6295 MOV AX,[DI.EMM_KSIZE]
6296SET_2:
6297OK_SET_DEV:
6298 MOV [DEV_SIZE],AX
6299 OR [DI.EMM_FLAGS],EMM_ISDRIVER
6300 MOV [MY_EMM_REC],DI
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
6304 RET
6305
6306NEXTRECI:
6307NEXTREC:
6308 ADD DI,SIZE EMM_REC ; Next record
6309 LOOP LOOK_RECJ
6310VERROR:
6311 MOV DX,OFFSET ERRMSG2
6312 CALL PRINT
6313 STC
6314 RET
6315
6316LOOK_RECJ:
6317 JMP LOOK_REC
6318
6319GOT_FREE_REC:
6320 MOV AX,[SI.EMM_AVAILK]
6321 CMP AX,16
6322 JB VERROR ; 16K is smallest device
6323 CALL SET_RESET ; Set INT 19,9
6324 CMP AX,[DEV_SIZE]
6325 JBE GOTSIZE ; Not enough for user spec
6326 MOV AX,[DEV_SIZE] ; User size is OK
6327GOTSIZE:
6328 MOV [DEV_SIZE],AX
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
6333 MOV [MY_EMM_REC],DI
6334 SUB [MY_EMM_REC],OFFSET TRACK_BUFFER ; Make start of EMM_CTRL relative
6335 PUSH DI
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
6340 MOV CX,1024
6341 MUL CX ; Mult size by 1024 to get # bytes
6342 ADD AX,BX ; Add size onto base to get next base
6343 ADC DX,DI
6344 POP DI
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
6349 INC AX ; RESET zero
6350 RET
6351
6352;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6353; the new int15 standard
6354;
6355new_st:
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
6363;
6364 sub bx,1 ; subtract the emm ctrl record size
6365 mov di,offset track_buffer ; set up to address the ctrl record
6366 ; read in
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
6371;
6372 cmp bx,128 ; is it the minimum required
6373 jb verror ; if less go to abort
6374;
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
6377;
6378no_adjust:
6379 cmp [dev_size],bx ;
6380 jb skip_adj_dev_size ; if enough space we don't need to adj
6381 ; dev_size
6382 mov [dev_size],bx ; else we have compromise on dev size
6383skip_adj_dev_size:
6384;
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
6387;
6388 mov bx,[ext_k] ; get memory which was reported by int15
6389 add bx,[special_mem] ; account for olivetti guys
6390 sub bx,[dev_size] ;
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
6394;
6395 push ax
6396 push dx
6397 push bx
6398 push es
6399 mov ax,(get_interrupt_vector shl 8) or 15h
6400 int 21h
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
6405 int 21h
6406 pop es
6407 pop bx
6408 pop dx
6409 pop ax
6410;
6411; set up int19 vector
6412;
6413 call set_reset
6414;
6415; now fill device base address in es:di
6416;
6417 mov ax,[ext_k]
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] ;
6423 mov es,dx ;
6424 mov di,ax ;
6425 ret
6426
6427;** SET_RESET - Set up INT 19/INT 9 vectors
6428;
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.
6434;
6435; ENTRY:
6436; NONE
6437; EXIT:
6438; NONE
6439; USES:
6440; None
6441;
6442; COMMON TO TYPE 1, 2 drivers
6443;
6444
6445SET_RESET:
6446ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
6447 cmp U_SWITCH,0 ;; don't do this for at&t 6300 plus
6448 jnz ret005
6449 PUSH AX
6450 PUSH DX
6451 PUSH BX
6452 PUSH ES
6453 MOV AX,(Get_Interrupt_Vector SHL 8) OR 19H
6454 INT 21H
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
6459 INT 21H
6460 MOV AX,(Get_Interrupt_Vector SHL 8) OR 9H
6461 INT 21H
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
6466; INT 21H
6467 POP ES
6468 POP BX
6469 POP DX
6470 POP AX
6471RET005:
6472 RET
6473
6474BREAK </E INIT Code>
6475
6476;** AT_EXT_INIT - Perform /E (TYPE 1) specific initialization
6477;
6478; This code does the drive TYPE specific initialization for TYPE 1
6479; drivers.
6480;
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
6492;
6493; ENTRY:
6494; Invokation line parameter values set.
6495; EXIT:
6496; CARRY 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).
6500; CARRY CLEAR
6501; BASE_ADDR set for this drive from EMM_BASE of EMM_REC
6502; BASE_RESET set from BASE_ADDR
6503; TERM_ADDR set
6504; EMM_REC is marked EMM_ISDRIVER
6505; MY_EMM_REC set
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.
6509;
6510; USES:
6511; ALL but DS
6512;
6513; Code is specific to TYPE 1 driver
6514;
6515
6516AT_EXT_INIT:
6517ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
6518 push ds
6519 call sys_det ; new routine to do more comprehensive
6520 pop ds
6521 jnc at001 ; checks than before
6522 MOV DX,OFFSET BAD_AT
6523ERR_RET:
6524 CALL PRINT
6525 STC
6526 RET
6527
6528AT001:
6529
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.
6532;;
6533 cmp [U_SWITCH],0 ;; patch the code for /U option
6534 jz AT001A
6535 mov ax,00fah
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
6539AT001A:
6540 MOV AX,8800H
6541 INT 15H ; Get extended memory size
6542 MOV DX,OFFSET NO_MEM
6543 OR AX,AX
6544 JZ ERR_RET
6545
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.
6550
6551 cmp [U_SWITCH],0 ;; did we ask for upper extended memory
6552 jz olstuff ;; no
6553 call UpperMemCheck ;; yes, see if anything there
6554 jc ERR_RET ;; no, quit
6555 mov ax,384 ;; yes, but max allowed is 384K
6556 jmp short at001b
6557olstuff:
6558 cmp [S5_FLAG],S_OLIVETTI ;; if not 6300 PLUS, go on
6559 jne at001b
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
6564AT001B:
6565
6566 MOV DX,OFFSET ERRMSG2
6567 CMP AX,128 ; 128k min cache
6568 JB ERR_RET
6569 MOV [EXT_K],AX
6570 MOV BX,AX
6571; DEC BX ; BX is MAX possible cache size
6572 CMP [DEV_SIZE],BX
6573 JBE AT002 ; DEV_SIZE OK
6574 MOV [DEV_SIZE],BX ; Limit DEV_SIZE to available
6575AT002:
6576;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6577; 386 modification
6578 test [sys_flg],M_386
6579 je loadall_setup
6580 mov ax,cs
6581 mov word ptr [cod_seg],ax
6582; set cs descriptor
6583 mov cx,16
6584 mul cx
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
6589; set gdt base
6590 mov si,offset emm_gdt
6591 add ax,offset start_gdt
6592 adc dx,0
6593 mov [si].gdt_base_0,ax
6594 mov [si].gdt_base_2,dx
6595 jmp short common_setup
6596 ;
6597 ; Init various pieces of LOADALL info
6598 ;
6599;;;; SMSW [LDSW]
6600;;;; SIDT QWORD PTR [IDTDES]
6601;;;; SGDT QWORD PTR [GDTDES]
6602;;;; ;
6603;;;; ; NOW The damn SXXX instructions store the desriptors in a
6604;;;; ; different order than LOADALL wants
6605;;;; ;
6606;;;; MOV SI,OFFSET IDTDES
6607;;;; CALL FIX_DESCRIPTOR
6608;;;; MOV SI,OFFSET GDTDES
6609;;;; CALL FIX_DESCRIPTOR
6610loadall_setup:
6611 MOV [LCSS],CS
6612 MOV SI,OFFSET CSDES
6613 MOV AX,CS
6614 CALL SEG_SET
6615common_setup:
6616 CALL MM_SETDRIVE
6617 JC RETXXX
6618 CALL DRIVEPARMS
6619 JNC RETXXX
6620 CALL DISK_ABORT
6621 STC
6622RETXXX:
6623 RET
6624
6625;;* UpperMemCheck - Called by 6300 PLUS to verify existence of
6626;; upper extended memory of 384K at FA0000h
6627;;
6628;; Returns carry set if no upper extended memory.
6629;;
6630;; This routine is called only by a 6300 PLUS, and
6631;; it reads the hardware switch DSW2 to do the job.
6632;;
6633UpperMemCheck:
6634 push ax
6635 in al,66h
6636 and al,00001111b
6637 cmp al,00001011b
6638 pop ax
6639 jnz nomem
6640 clc
6641 ret
6642nomem:
6643 stc
6644 ret
6645
6646BREAK </A INIT Code>
6647
6648;** EMM device driver name
6649;
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.
6653;
6654; This datum is specific to TYPE 2 drivers
6655;
6656
6657ABOVE_DEV_NAME DB "EMMXXXX0"
6658
6659;** ABOVE_INIT - Perform /A (TYPE 2) specific initialization
6660;
6661; This code performes the driver specific initialization for
6662; type 2 drivers.
6663;
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.
6670;
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.
6683;
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
6695;
6696; SEE ALSO
6697; INTEL Expanded Memory Manager Programming Specification
6698;
6699; ENTRY:
6700; Invokation line parameter values set.
6701; EXIT:
6702; ABOVE_BLKMOV code swapped in at BLKMOV
6703; ABOVE_RESET code swapped in at RESET_SYSTEM
6704; CARRY SET
6705; Error, message already printed. Driver not installed.
6706; No Above Board memory allocated.
6707; CARRY CLEAR
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
6711; TERM_ADDR set
6712;
6713; USES:
6714; ALL but DS
6715;
6716; Code is specific to TYPE 2 driver
6717;
6718
6719ABOVE_INIT:
6720ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING
6721 ;
6722 ; Swap above code into place
6723 ;
6724 PUSH CS
6725 POP ES
6726 MOV SI,OFFSET ABOVE_CODE
6727 MOV DI,OFFSET DRIVE_CODE
6728 MOV CX,OFFSET DRIVE_END - OFFSET DRIVE_CODE
6729 REP MOVSB
6730 MOV SI,OFFSET ABOVE_RESET
6731 MOV DI,OFFSET RESET_SYSTEM
6732 MOV CX,OFFSET RESET_INCLUDE - OFFSET RESET_SYSTEM
6733 REP MOVSB
6734 ;
6735 ; Check for presence of Above board memory manager
6736 ;
6737 MOV AX,(Get_Interrupt_Vector SHL 8) OR 67H
6738 INT 21H
6739 MOV DI,SDEVNAME
6740 MOV SI,OFFSET ABOVE_DEV_NAME
6741 MOV CX,8
6742 REPE CMPSB
6743 JZ GOT_MANAGER
6744 MOV DX,OFFSET NO_ABOVE
6745ABOVE_ERR:
6746 CALL PRINT
6747 STC
6748 RET
6749
6750GOT_MANAGER:
6751 ;
6752 ; Check memory status
6753 ;
6754 MOV CX,8000H
6755STLOOP:
6756 MOV AH,ABOVE_STATUS
6757 INT 67H
6758 CMP AH,ABOVE_SUCCESSFUL
6759 JZ MEM_OK
6760 CMP AH,ABOVE_ERROR_BUSY
6761 LOOPZ STLOOP
6762ST_ERR:
6763 MOV DX,OFFSET BAD_ABOVE
6764 JMP ABOVE_ERR
6765
6766MEM_OK:
6767 ;
6768 ; Get base address of map region and set BASE_ADDR
6769 ;
6770 MOV AH,ABOVE_GET_SEG
6771 INT 67H
6772 CMP AH,ABOVE_ERROR_BUSY
6773 JZ MEM_OK
6774 CMP AH,ABOVE_SUCCESSFUL
6775 JNZ ST_ERR
6776 MOV WORD PTR [BASE_ADDR],0
6777 MOV WORD PTR [BASE_ADDR + 2],BX
6778 ;
6779 ; Allocate drive memory
6780 ;
6781GET_AVAIL:
6782 MOV AH,ABOVE_GET_FREE
6783 INT 67H
6784 CMP AH,ABOVE_ERROR_BUSY
6785 JZ GET_AVAIL
6786 CMP AH,ABOVE_SUCCESSFUL
6787 JNZ ST_ERR
6788 MOV AX,DX ; AX is total 16K pages
6789 ; BX is un-allocated 16K pages
6790 MOV DX,OFFSET NO_MEM
6791 OR AX,AX
6792 JZ ABOVE_ERR
6793 MOV DX,OFFSET ERRMSG2
6794;
6795; change in allocation strategy new default is all of available pages
6796; < 8192K.
6797;
6798; algorithm: if (free_pages < 8) then error();
6799; else {
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)
6803
6804
6805
6806 CMP BX,8 ; 128K = 16K * 8 = Min cache size
6807 JB ABOVE_ERR
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
6811ab0$1:
6812 mov cx,4 ; to convert number of pages into no of k
6813 shl bx,cl
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
6817 jmp short ab001 ;
6818ab0$2:
6819 cmp [dev_size],bx ; minimum of dev size and bx
6820 jb ab001
6821 mov [dev_size],bx ;
6822
6823
6824ab001:
6825 mov bx,[dev_size]
6826 mov [current_dev_size],bx ; Initialize current device size
6827 ;
6828 ; BX is K we want to allocate (limited by available K)
6829 ; BX is at least 16
6830 ;
6831 MOV AX,BX
6832 MOV CX,4 ; Convert back to # of 16K pages
6833 SHR BX,CL
6834 TEST AX,0FH ; Even????
6835 JZ OKAYU ; Yes
6836 INC BX ; Gotta round up
6837 PUSH BX
6838 MOV CX,4
6839 SHL BX,CL
6840 MOV [DEV_SIZE],BX ; Correct dev size too by rounding it up to
6841 ; next multiple of 16K, no sense wasting
6842 ; part of a page.
6843;A
6844 mov [current_dev_size],bx ; Correct current device size also
6845;A
6846 POP BX
6847OKAYU:
6848 MOV AH,ABOVE_ALLOC
6849 INT 67H
6850 CMP AH,ABOVE_ERROR_BUSY
6851 JZ OKAYU
6852 CMP AH,ABOVE_SUCCESSFUL
6853 JZ GOT_ID
6854 CMP AH,ABOVE_ERROR_MAP_CNTXT
6855 JZ ST_ERRJ
6856 CMP AH,ABOVE_ERROR_OUT_OF_PIDS
6857 JB ST_ERRJ
6858 MOV DX,OFFSET ERRMSG2
6859 JMP ABOVE_ERR
6860
6861ST_ERRJ:
6862 JMP ST_ERR
6863
6864GOT_ID:
6865 MOV [ABOVE_PID],DX
6866 ;
6867 ; INSTALL ABOVE RESET handler
6868 ;
6869 CALL SET_RESET
6870 ;
6871 ; We are now in good shape.
6872 ;
6873 CALL DRIVEPARMS
6874 JNC RETYYY
6875 CALL DISK_ABORT
6876 STC
6877RETYYY:
6878 RET
6879
6880BREAK <Drive code for /A driver. Swapped in at BLKMOV>
6881
6882;
6883; This label defines the start of the code swapped in at DRIVE_CODE
6884;
6885ABOVE_CODE LABEL WORD
6886
6887;
6888; WARNING DANGER!!!!!!!
6889;
6890; This code is tranfered over the /E driver code at DRIVE_CODE
6891;
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.
6899
6900IF2
6901 IF((OFFSET ABOVE_BLKMOV - OFFSET ABOVE_CODE) NE (OFFSET BLKMOV - OFFSET DRIVE_CODE))
6902 %out ERROR BLKMOV, ABOVE_BLKMOV NOT ALIGNED
6903 ENDIF
6904 IF((OFFSET ABOVE_END - OFFSET ABOVE_CODE) GT (OFFSET DRIVE_END - OFFSET DRIVE_CODE))
6905 %out ERROR ABOVE CODE TOO BIG
6906 ENDIF
6907ENDIF
6908
6909 DD ? ; 24 bit address of start of this RAMDRV
6910;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6911;** ABOVE_BLKMOV - Perform transfer for TYPE 2 driver
6912;
6913; This routine is the transfer routine for moving bytes
6914; to and from the Above Board memory containing the cache.
6915;
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:
6921;
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; |------------|------------|------------|------------|------------|
6926; |---|---| | |
6927; | | |---------------- 64K bytes of sectors -------------|
6928; Byte | | |
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
6941; physical page
6942; 0
6943;
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
6947; possible to map.
6948;
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.
6954;
6955; SEE ALSO
6956; INTEL Expanded Memory Manager Programming Specification
6957;
6958; ENTRY:
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
6963;
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
6968;
6969; EXIT:
6970; Carry Clear
6971; OK, operation performed successfully
6972; Carry Set
6973; Error during operation, AL is error number
6974;
6975; USES:
6976; ALL
6977;
6978; This routine is specific to TYPE 2 driver
6979;
6980above_blkmov:
6981assume ds:int13code,es:nothing,ss:nothing
6982;
6983; save mapping context and return with error if save fails
6984;
6985 save_mapping_context
6986 jnc ab_blk$1
6987 ret
6988;
6989; find logical page number, offset of i/o in first page
6990;
6991ab_blk$1:
6992 push cx
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
6998 pop cx
6999;
7000; find case and dispatch accordingly
7001;
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
7007;
7008 push bx
7009 push cx
7010;
7011; if( final_user_off < pm_base_addr ) then case 0
7012;
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
7018 shr ax,1 ;
7019 shr ax,1 ;
7020 mov bx,es ; get segment of buffer
7021 add ax,bx ; now we have the segment of the user buffer
7022 ; with offset < 16
7023 sub ax,word ptr [base_addr+2] ; compare against page map
7024 jc aar_cd ; if below page map then execute old code
7025;
7026; if( initial_user_off < pm_base_addr ) then error
7027;
7028 mov cx,4
7029 mov bp,di ; get initial offset in bp
7030 shr bp,cl ;
7031 add bp,bx ;
7032 sub bp,word ptr [base_addr +2]
7033 jc ab_error ;
7034;
7035; if ( initial_user_off >= pm_end_addr ) then case1
7036;
7037 cmp bp,4*1024 ;
7038 jae aar_cd ;
7039;
7040; if ( final_addr >= pm_end_addr ) then error
7041;
7042 cmp ax,4*1024
7043 jae ab_error
7044;
7045; case 2
7046;
7047within_pm: jmp new_code ; user buffer in page map
7048 ; so we need to execute new code
7049ab_error:
7050 add sp,4
7051 mov al,0bbh ; general failure
7052 stc
7053 jmp short REST_CONT ; RESTORE CONTEXT!!!
7054aar_cd:
7055 pop cx
7056 pop bx
7057;
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
7065; executed.
7066;
7067; METHOD:
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
7071; of the I/O.
7072; Set appropriate seg and index registers and CX to perform the
7073; main piece of the I/O into the page window
7074; REP MOVSW
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
7079; REP MOVSW
7080; Restore Above Board page mapping context
7081;
7082 XOR BP,BP ; No tail page
7083 PUSH BX
7084 ;
7085 ; DX is first page #, SI is byte offset of start of I/O in first page
7086 ;
7087 MOV AX,DX
7088 MOV BX,SI
7089 SHR BX,1 ; # Words in first 16k page which are not part
7090 ; of I/O
7091 PUSH CX
7092 ADD BX,CX ; # of words we need to map to perform I/O
7093 MOV DX,BX
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?
7099 JZ NO_REM
7100 INC BX ; Need one more page
7101NO_REM:
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
7105 JBE NO_TAIL
7106 MOV BP,DX ; Words to move in tail page saved in BP
7107 DEC CX ; Need second map for the 5th page
7108 POP AX
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
7112NO_TAIL:
7113 ; Map CX pages
7114 MOV DX,[ABOVE_PID]
7115 MOV AX,ABOVE_MAP SHL 8 ; Physical page 0
7116 PUSH AX
7117MAP_NEXT:
7118 POP AX ; Recover correct AX register
7119 PUSH AX
7120 PUSH BX
7121 PUSH DX
7122 INT 67H ; Damn call ABOVE_MAP zaps BX,DX,AX
7123 POP DX
7124 POP BX
7125 OR AH,AH
7126 JNZ MAP_ERR1 ; error
7127IF2
7128 IF (ABOVE_SUCCESSFUL)
7129 %out ASSUMPTION IN CODE THAT ABOVE_SUCCESSFUL = 0 IS INVALID
7130 ENDIF
7131ENDIF
7132NEXT_PAGE:
7133 INC BX ; Next logical page
7134 POP AX
7135 INC AL ; Next physical page
7136 PUSH AX
7137 LOOP MAP_NEXT
7138 POP AX ; Clean stack
7139 POP CX ; Word count for first page mapping
7140 POP AX ; Operation in AH
7141 ;
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
7147 ;
7148 PUSH AX ; Save op for possible second I/O
7149 OR AH,AH
7150 JZ READ_A
7151 ;
7152 ; WRITE
7153 ;
7154 PUSH ES
7155 PUSH DI
7156 MOV DI,SI ; Start page offset to DI
7157 POP SI ; DS:SI is transfer addr
7158 POP DS
7159ASSUME DS:NOTHING
7160 MOV ES,WORD PTR [BASE_ADDR + 2] ; ES:DI -> start
7161 JMP SHORT FIRST_MOVE
7162
7163READ_A:
7164ASSUME DS:INT13CODE
7165 MOV DS,WORD PTR [BASE_ADDR + 2] ; DS:SI -> start
7166ASSUME DS:NOTHING
7167FIRST_MOVE:
7168 REP MOVSW
7169 OR BP,BP ; Tail?
7170 JNZ TAIL_IO ; Yup
7171ALL_DONE:
7172 POP AX
7173 CLC
7174REST_CONT:
7175 ; Restore page mapping context
7176 PUSH AX ; Save possible error code
7177 PUSHF ; And carry state
7178REST_AGN:
7179 MOV DX,[ABOVE_PID]
7180 MOV AH,ABOVE_RESTORE_MAP_PID
7181 INT 67H
7182 OR AH,AH
7183 JZ ROK
7184IF2
7185 IF (ABOVE_SUCCESSFUL)
7186 %out ASSUMPTION IN CODE THAT ABOVE_SUCCESSFUL = 0 IS INVALID
7187 ENDIF
7188ENDIF
7189 CMP AH,ABOVE_ERROR_BUSY
7190 JZ REST_AGN
7191 CMP AH,ABOVE_ERROR_NO_CNTXT
7192 JZ ROK ; Ignore the invalid PID error
7193 POP DX
7194 POP DX ; Clean stack
7195 MOV AL,0BBH ; General failure
7196 STC
7197 RET
7198
7199ROK:
7200 POPF ; Recover carry state
7201 POP AX ; and possible error code
7202 RET
7203
7204TAIL_IO:
7205 MOV DX,[ABOVE_PID]
7206MAP_AGN:
7207 MOV AX,ABOVE_MAP SHL 8 ; map logical page BX to phys page 0
7208 PUSH BX
7209 PUSH DX
7210 INT 67H ; Damn call ABOVE_MAP zaps BX,DX,AX
7211 POP DX
7212 POP BX
7213 OR AH,AH
7214 JNZ MAP_ERR2 ; Error
7215IF2
7216 IF (ABOVE_SUCCESSFUL)
7217 %out ASSUMPTION IN CODE THAT ABOVE_SUCCESSFUL = 0 IS INVALID
7218 ENDIF
7219ENDIF
7220SECOND_MOVE:
7221 POP AX ; Recover Op type
7222 PUSH AX
7223 OR AH,AH
7224 JZ READ_SEC
7225 ;
7226 ; WRITE
7227 ;
7228 XOR DI,DI ; ES:DI -> start of tail
7229 JMP SHORT SMOVE
7230
7231READ_SEC:
7232 XOR SI,SI ; DS:SI -> start of tail
7233SMOVE:
7234 MOV CX,BP
7235 REP MOVSW
7236 JMP ALL_DONE
7237
7238MAP_ERR1:
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
7242 JMP SHORT DNR_ERR
7243
7244MAP_ERR2:
7245 CMP AH,ABOVE_ERROR_BUSY
7246 JZ MAP_AGN
7247 ADD SP,2
7248DNR_ERR:
7249 MOV AL,0AAH ; Drive not ready
7250 STC
7251 JMP REST_CONT
7252;
7253;
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;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7261; the algorithm is:
7262; ******************************************************
7263; [STEP1: determine the page we can use for the cache]
7264;
7265; if (initial_para_offset_user in page 1 or above ) then {
7266; physical_cache_page = 0;
7267; cache_segment = above board segment;
7268; }
7269; else {
7270; physical_cache_page = 3;
7271; cache_segment = above_board_segment + 3*1024;
7272; }
7273;
7274; ******************************************************
7275; [STEP2: initial setup]
7276;
7277; count = user_count_requested;
7278; number_to_be_transferred = min ( count, (16K - si) >> 2 );
7279; exchange source and destination if necessary;
7280;
7281; *******************************************************
7282; [STEP3: set up transfer and do it]
7283;
7284; count = count - number_to_be_transferred;
7285; map_page cache_handle,physical_cache_page,logical_cache_page
7286; mov data
7287;
7288; *******************************************************
7289; [STEP4: determine if another transfer needed and setup if so]
7290;
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++ ;
7296;
7297; *******************************************************
7298; [STEP5: go to do next block]
7299;
7300; goto [STEP3]
7301;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7302;
7303new_code:
7304 assume ds:int13code,es:nothing,ss:nothing
7305;
7306; input parameters:
7307;
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
7314;
7315; on stack { cx,bx } where cx = number of words, bx = read / write status
7316;
7317; [STEP1: finding physical cache page and page frame]
7318;
7319 ;
7320 ; assume is physical page 0
7321 ;
7322 xor al,al ; use page 0 for cache
7323 mov bx,word ptr [base_addr+2]
7324 ;
7325 ; see if this assumption valid
7326 ;
7327 cmp bp,1024 ; is initial in page 1 or above
7328 jae ab$30 ; if so or assumption is valid
7329 ;
7330 ; else we have to correct our assumption
7331 ;
7332 mov al,3 ; use page 3 for cache
7333 add bx,3*1024 ;
7334 ;
7335 ; initialise page frame segment
7336 ;
7337ab$30:
7338 mov ds,bx
7339 ;
7340assume ds:nothing
7341;
7342; [STEP2: initialising transfer parameters]
7343;
7344 ;
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
7349 ;
7350 ; initially si offset into logical page, so we can only do 16*1024 - si
7351 ; byte transfer
7352 ;
7353 mov cx,16*1024
7354 sub cx,si
7355 shr cx,1 ; convert to word count
7356 ;
7357 ; number to be transferred is the minimum of this and the user requested
7358 ; count
7359 ;
7360 cmp cx,bp
7361 jb ab$31
7362 mov cx,bp
7363 ;
7364ab$31:
7365 ;
7366 ; see if write, then we have to switch source with destination
7367 ;
7368 or bh,bh
7369 je ab$32 ; if read we don't have to do anything
7370 ; else we have to switch
7371 src_dest_switch
7372ab$32:
7373 ;
7374 ; set direction flag so that we don't have to do it repeatedly
7375 ;
7376 cld
7377;
7378; [STEP3: set up transfer and do it]
7379;
7380ab$33:
7381 ;
7382 ; update count of words still left to be transferred after this
7383 ;
7384 sub bp,cx
7385 ;
7386 ; map the logical page in cache to the physical page selected
7387 ;
7388 mov bx,dx ; get logical page into bx
7389 ; al already holds the physical page #
7390 map_page
7391 jnc ab$34 ; suceeded ?
7392 ;
7393 ; else report error
7394 ;
7395 add sp,4
7396 stc
7397 jmp short restore_mp ; and go to restore page map
7398ab$34:
7399 ;
7400 ; succeeded, do the transfer
7401 ;
7402rep movsw
7403 ;
7404;
7405; [STEP4: check if transfer done, if not set up for next block]
7406; [STEP5: go back to STEP3]
7407 ;
7408 ; check if done
7409 ;
7410 or bp,bp ; count 0
7411 je ab$40 ; yes, go to finish up
7412 ;
7413 ; recover original dx and bx, increment dx and then save both again
7414 ;
7415 pop dx
7416 pop bx
7417 inc dx
7418 push bx
7419 push dx
7420 ;
7421 ; words to be transferred minimum of count and 8*1024 words
7422 ;
7423 mov cx,8*1024 ; 8k words in a page
7424 cmp cx,bp ;
7425 jbe ab$35 ; if below or equal this is what we want
7426 ;
7427 mov cx,bp ; else we can transfer the whole count
7428ab$35:
7429 ;
7430 ; see whether cache src or dest and accordingly reset either si or di
7431 ;
7432 or bh,bh ; read?
7433 jne ab$36 ; if write go to modify
7434 ;
7435 ; read, zero si and go back to step3
7436 ;
7437 xor si,si
7438 jmp short ab$33 ; to step 3
7439ab$36:
7440 ;
7441 ; write, zero di and go back to step3
7442 ;
7443 xor di,di
7444 jmp short ab$33 ; to step 3
7445;
7446; finishing up we have to restore the page map
7447;
7448ab$40:
7449 add sp,4
7450 clc
7451restore_mp:
7452 restore_mapping_context
7453 ret
7454
7455 DW ? ; SPACE for ABOVE_PID
7456
7457;
7458; This label defines the end of the code swapped in at DRIVE_CODE
7459;
7460ABOVE_END LABEL WORD
7461
7462BREAK <Drive code for /A driver. Swapped in at RESET_SYSTEM>
7463
7464
7465;
7466; WARNING DANGER!!!!!!!
7467;
7468; This code is tranfered over the /E driver code at RESET_SYSTEM
7469;
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.
7475;
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.
7479;
7480
7481IF2
7482 IF((OFFSET ABOVE_RESET_END - OFFSET ABOVE_RESET) GT (OFFSET RESET_INCLUDE - OFFSET RESET_SYSTEM))
7483 %out ERROR ABOVE_RESET CODE TOO BIG
7484 ENDIF
7485ENDIF
7486
7487;** ABOVE_RESET perform TYPE 2 (/A) driver specific reboot code
7488;
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".
7492;
7493; ENTRY
7494; NONE
7495; EXIT
7496; NONE
7497; USES
7498; NONE
7499;
7500; This code is specific to TYPE 2 drivers
7501;
7502
7503ABOVE_RESET:
7504ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
7505 PUSH AX
7506 PUSH DX
7507AGAIN_RESET:
7508 MOV DX,[ABOVE_PID]
7509 MOV AH,ABOVE_DEALLOC ; Close PID
7510 INT 67H
7511 CMP AH,ABOVE_ERROR_BUSY
7512 JZ AGAIN_RESET
7513 POP DX
7514 POP AX
7515 RET
7516
7517;
7518; This label defines the end of the code swapped in at RESET_SYSTEM
7519;
7520ABOVE_RESET_END LABEL BYTE
7521
7522BREAK <messages and common data>
7523
7524;** Message texts and common data
7525;
7526; Init data. This data is disposed of after initialization.
7527; it is mostly texts of all of the messages
7528;
7529; COMMON to TYPE 1 and 2 drivers
7530;
7531; THIS IS THE START OF DATA SUBJECT TO TRANSLATION
7532
7533NO_ABOVE db "SMARTDrive : Expanded Memory Manager not present",13,10,"$"
7534BAD_ABOVE db "SMARTDrive : Expanded Memory Status shows error",13,10,"$"
7535BAD_AT db "SMARTDrive : Cannot run on this computer",13,10,"$"
7536NO_MEM db "SMARTDrive : No extended memory available",13,10,"$"
7537ERRMSG1 db "SMARTDrive : Invalid parameter",13,10,"$"
7538ERRMSG2 db "SMARTDrive : Insufficient memory",13,10,"$"
7539INIT_IO_ERR db "SMARTDrive : I/O error accessing cache memory",13,10,"$"
7540NOHARD db "SMARTDrive : No hard drives on system",13,10,"$"
7541BIGTRACK db "SMARTDrive : Too many bytes per track on hard drive",13,10,"$"
7542BADVERMES db 13,10,"SMARTDrive : Incorrect DOS version",13,10,"$"
7543
7544;
7545; This is the Int13 header message.
7546;
7547HEADERMES db 13,10,"Microsoft SMARTDrive Disk Cache v2.10",13,10,"$"
7548
7549;
7550; This is the status message used to display INT13 configuration
7551; it is:
7552;
7553; STATMES1<size in K><STATMES1A|STATMES1E>STATMES2<# tracks in cache>STATMES3
7554; <sectors per track>STATMES4
7555;
7556; It is up to translator to move the message text around the numbers
7557; so that the message is printed correctly when translated
7558;
7559STATMES1 db " Cache size: $"
7560STATMES1A db "K in Expanded Memory$"
7561STATMES1E db "K in Extended Memory$"
7562STATMES2 db 13,10," Room for $"
7563STATMES3 db " tracks of $"
7564STATMES4 db " sectors each",13,10,13,10,"$"
7565ifdef OMTI
7566omti_msg db " OMTI controller release",13,10,"$"
7567endif
7568
7569;-----------------------------------------------------------------------
7570;
7571; END OF DATA SUBJECT TO TRANSLATION
7572;
7573
7574IF DEBUG
7575STATMES5 db "Device CS = $"
7576STATMES6 db " decimal",13,10,"$"
7577s5flagmsg db " = S5 flag",13,10,"$"
7578U_msg db " = U Switch", 13,10,'$'
7579ENDIF
7580
7581 db "This program is the property of Microsoft Corporation."
7582
7583INT13_END LABEL BYTE
7584
7585INT13CODE ENDS
7586 END
7587 \ No newline at end of file