TITLE EXTENDED/EXPANDED MEMORY DISK CACHE PAGE 58,132 ; ;; Will use IBM extended memory on PC-AT or ;; use Above Board on PC, XT, or AT, and ;; use extended, expanded, or upper extended memory on AT&T 6300 PLUS ; ; ;; device = SMARTDRV.sys [bbbb] [/a] [/u] ; ; bbbb First numeric argument, if present, is memory size ; in K bytes. Default value is 256. Min is 128. Max ; is 8192 (8 Meg). ; ; By default PC AT Extended Memory is to be used. ; It is an error if /E is specified on a machine other ; than an IBM PC AT. /E is the default. ; NOTE: Information in cache in PC AT extended memory ; will be lost at system re-boot (warm or cold). This is ; due to the fact that the IBM PC AT ROM bootstrap code ; zeroes all of memory. ; NOTE: There is 1k of memory overhead. That is to say, ; if there are 512k bytes of extended memory, there ; will be 511k bytes available for assignment to int13. ; This 1k overhead is fixed and makes int13 compatible ; with RAMDRive. ; NOTE: The same allocation strategy as is used in RAMDrive ; is used. This allows RAMDrive and INT13 to coexist on ; the same system. Mixing with IBM VDISK is NOT supported. ; ; /a Specifies that Above Board memory is to be used. It ; is an error if the above board device driver is not ; present. ; NOTE: Information in cache in Above Board memory ; will be lost at system re-boot (warm or cold). This is ; due to the fact that the EMM device driver performs a ; destructive test when it is installed which zeros all ; of the Above Board memory. ;; /u Specifies that upper extended memory will be used ;; on the AT&T 6300 PLUS. Upper extended memory ;; is the memory beginning at FA0000. It is used ;; to hold the UNIX kernel when the machine is running ;; Simul-Task. However, when operating as a pure ;; MS-DOS machine, this 384K of memory is available ;; for SMARTDRIVE. ;; Note that it is an error to specify this switch ;; if the machine is not a 6300 PLUS. ; ; NOTE WARNING: ALL OF THIS CODE ASSUMES THAT ALL HARDFILES ARE 512 BYTES ; PER SECTOR!!! All other hardfile parameters are read via INT 13, but ; Bytes/sector MUST be IBM standard 512. ; ; MODIFICATION HISTORY ; ; 1.00 5/10/86 ARR Initial version based on RAMDrive 1.16. ; 1.01 5/20/86 ARR Slight re-organization of places where FLUSH_CACHE ; is called to discard a track to make sure ; TRACK_BUFFER is invalidated correctly. ; 1.10 5/26/86 ARR Added Timer Int to flush cache after passage ; of user setable time. ; 1.20 5/27/86 ARR Additions at request of Neilk. /t:nnnnn /d /wb:on ; /wb:off /wt:on /wt:off can be on device = line. ; Lock cache function added. ; 1.21 5/29/86 ARR Lock code made more intelligent. ; 1.22 5/30/86 ARR /r reboot flush code added ; 1.23 6/03/86 ARR Cache statistics added ; 1.24 6/05/86 ARR Added /a "all cache" code ; 1.25 6/10/86 ARR Added total used, total locked to status ; 1.26 6/12/86 ARR /wb changed to /wc to align with docs. Discard ; of track when write to locked track changed to ; unlock. Discard of track when write with /wc:off ; changed to immediate write through. ; 1.27 6/17/86 ARR Bug regarding the INT 13 error which is not ; an error (error 11H, ECC error corrected). ; changed error handling logic to handle this ; correctly (ignore it). ; 1.28 7/31/86 ARR Default seg reg access byte changed from ; 82H to 92H. This was needed for 80386 functionality. ; Change to LOADALL.ASM, also RAMDrive problem. ; 1.30 8/04/86 ARR Default cache size uped to 256K ; Min cache size uped to 128K ; 1.31 8/07/86 ARR Moved SMSW SIDT SGDT set into BLKMOV code for ; problem with CEMM ; 1.32 8/27/86 ARR Added code to A20 routine to provide approp ; settle time for A20 switch to occur. This will ; help us on Compaq machines and faster ATs and ; 80386 machines. Thanks to CC of Compaq for fix. ; 1.33 9/22/86 ARR Added more info to startup header, in particular, ; tells you whether /A or /E cache. ; ; SMARTDRV ; ------ ; ; 1.00 5/13/87 SUNILP, GREGH, DAVIDW: ; Modified INT13 to take care of multi track caching ; Reduced functionality ; Added two new IOCTL calls to increase/decrease ; cache size, dynamically ; ; ;; 7/24/87 WSH Added 6300 PLUS support. This code is marked by ;; the use of double semi-colons to make it easy to ;; find. ; ; 8/31/87 SUNILP ; New extended memory allocation scheme. 386 support. ; Support for new ps/2 systems. better 286 loadall ; transfer. more complete expanded memory access. ; several bug fixes. ; ; 1.01 9/17/87 SUNILP ; Removed check that was limiting tracks to 32k bytes ; Tracks can be upto 64k bytes now ; ; 1.02 10/22/87 SUNILP ; Reduced statically allocated track buffer size to ; minimum required ; ; 1.03 10/29/87 SUNILP ; Changed name reported in messages to SMARTDrive ; ; 1/08/88 GREGH ; Added support for OMTI controller. This code ; is ifdef'd in with the OMTI keyword. ; ; 1.04 3/02/88 SUNILP ; fix for recognition of 20MHz model 80. ; fix for READ DASD int 13 dispatch. ; ; 1.05 5/13/88 SUNILP ; fixed version checking to include dos 4.00 ; ; 2.10 6/13/88 CHIPA Merged in these changes for HP Vectra ; 11/20/87 RCP ; Fixed a20 enabling/disabling problems on ; Vectra machines. ; 8/24/88 MRW Merged changes from Windows tree into DOS tree ; BREAK MACRO subtitle SUBTTL subtitle PAGE ENDM .286p ; Use some 286 instructions in /E code DEBUG EQU 0 S_OLIVETTI EQU 1 ; Flag for olivetti 6300 plus machine S_VECTRA EQU 2 ; Flag for HP Vectra machines WINDOWS_SWITCHES EQU 1 ; 1 = uses switches for windows, 0 = all switches ;OMTI EQU 1 ; Used for code specific to the OMTI Controller MAX_HARD_FILES EQU 16 ; Max number of hardfiles our data structures support MIN_CACHE_SIZE_K EQU 128 ; Minimum size for cache in K (multiple of 16) IF1 IFDEF OMTI %out OMTI Controller release ENDIF ENDIF IF1 IF DEBUG %out DEBUG VERSION!!!!!! ENDIF ENDIF ;; In order to address memory above 1 MB on the AT&T 6300 PLUS, it is ;; necessary to use the special OS-MERGE hardware to activate lines ;; A20 to A23. However, these lines can be disabled only by resetting ;; the processor. The return address offset and segment can be found ;; at 40:a2, noted here as RealLoc1. ;; BiosSeg segment at 40h ;; Used to locate 6300 PLUS reset address org 00a2h RealLoc1 dd 0 BiosSeg ends ; R_Mode_IDT segment at 0h R_mode_IDT ends ; .xlist include devsym.asm include syscall.asm include mi.asm .list ; The INT13 device driver has 2 basic configurations. ; ; TYPE 1 - /E configuration using PC-AT extended memory and the LOADALL ; instruction. ;; The /U configuration using upper extended memory on the ;; 6300 PLUS is a special case of the type 1 configuration. ; ; TYPE 2 - /A configuration using Above Board memory and EMM device ; driver. ; ; The TYPE 2 driver uses the Above Board EMM device driver via INT 67H ; to control access to, and to access the available memory. ; ; The TYPE 1 configuration uses the EMM control sector to ; control access to the available memory ; include emm.asm include loadall.asm include above.asm include ab_macro.asm BREAK ; ; Define I/O packet offsets for useful values. ; ; SEE ALSO ; MS-DOS Technical Reference manual section on Installable Device Drivers ; ; READ/WRITE PACKET OFFSETS RW_COUNT EQU WORD PTR (SIZE SRHEAD) + 5 RW_TRANS EQU DWORD PTR (SIZE SRHEAD) + 1 RW_START EQU WORD PTR (SIZE SRHEAD) + 7 ; MEDIA CHECK PACKET OFFSETS MCH_RETVAL EQU BYTE PTR (SIZE SRHEAD) + 1 MCH_MEDIA EQU BYTE PTR (SIZE SRHEAD) + 0 ; BUILD BPB PACKET OFFSETS BPB_BUFFER EQU DWORD PTR (SIZE SRHEAD) + 1 BPB_MEDIA EQU BYTE PTR (SIZE SRHEAD) + 0 BPB_BPB EQU DWORD PTR (SIZE SRHEAD) + 5 ; INIT PACKET OFFSETS INIT_NUM EQU BYTE PTR (SIZE SRHEAD) + 0 INIT_BREAK EQU DWORD PTR (SIZE SRHEAD) + 1 INIT_BPB EQU DWORD PTR (SIZE SRHEAD) + 5 INIT_DOSDEV EQU BYTE PTR (SIZE SRHEAD) + 9 BREAK ; ; The cache control structure is the "management" data associated ; with all of the data in the cache. The cache structures are ; part of the device driver and they contain pointers to the ; actual cache memory where the data is. This is more efficient ; than putting the structures with the data as there is more overhead ; to access stuff in the data area. The structures form a double ; linked list in LRU order. The head points to the MRU element. The ; tail points to the LRU element. Scans start at MRU as this is the ; highest probability of a hit. Selection for bump is at LRU. All of ; the links are short pointers. This limits the size of the cache ; as the cache structures together with the device code must all ; fit in 64K. This is more efficient than FAR links. Each cache element ; contains one complete track of one of the INT 13 hard files (INT 13 ; floppy drives are NOT cached). Cache "read ahead" is obtained by ; reading complete tracks into the cache even though the INT 13 user ; may have only requested one sector. Write behind is accomplished ; (if enabled) by holding tracks for a while allowing user writes ; on that track to "accumulate". ; ; The original INT 13 caching algorithm (Aaronr) is sumarized as follows: ; ; If user read is in cache ; Perform user read from cache ; Else ; If read is full track ; Perform write through (if enabled) ; Pass Read to old INT 13 handler (no cache operation) ; Else ; Read track into cache using old INT 13 handler ; Perform user read out of cache ; ; If user write is in cache ; If write buffering is enabled ; Perform user write into cache ; Else ; Discard cache for this track ; Pass write through to old INT 13 handler ; Else ; If write is full track ; Perform write through (if enabled) ; Pass Write to old INT 13 handler (no cache operation) ; Else ; If write buffering is enabled ; Read track into cache using old INT 13 handler ; Perform user write into cache ; Else ; Pass write through to old INT 13 handler ; ; SMARTDRV modifications: ; ; 1. Write through always. ; 2. Multi track I/O capability support. ; 3. Direct transfer between cache and user buffer address for ; full tracks. ; ; ; CACHE_CONTROL STRUC FWD_LRU_LNK DW ? ; Link to next CACHE_CONTROL, -1 if last BACK_LRU_LNK DW ? ; Link to previous CACHE_CONTROL, -1 if first BASE_OFFSET DD ? ; Offset releative to start of cache ; memory of start of this track buffer TRACK_FLAGS DW ? ; Flags ; ; NOTE: The next two bytes are refed as a word. ; MOV DX,WORD PTR STRC.TRACK_DRIVE ; OR DH,80H ; Puts the INT 13 drive in DL, and the head in DH which is correct ; for an INT 13 ; TRACK_DRIVE DB ? ; INT 13 drive with high bit = 0 TRACK_HEAD DB ? ; INT 13 head TRACK_CYLN DW ? ; INT 13 cylinder ; High byte is low byte of cylinder # ; High two bits of Low byte are high ; two bits of cylinder #. Other bits ; are 0. This makes it easy to load the ; CX register for an INT 13 for this ; track: ; MOV CX,STRC.CACHE_CYLN ; OR CL,1 CACHE_CONTROL ENDS ; ; TRACK_FLAGS bits ; TRACK_FREE EQU 0000000000000001B ; Track buffer is free TRACK_DIRTY EQU 0000000000000010B ; Track needs to be written TRACK_LOCKED EQU 0000000000000100B ; Track is locked BREAK INT13CODE SEGMENT ASSUME CS:INT13CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING IF DEBUG public strategy,int13$in,cmderr,err$cnt,err$exit,devexit public INT13$IOCTL_Read,INT13$Read_St,INT13$Write_St,INT13$IOCTL_Write public int_1C_handler,int_13_handler,POP_NO_PROC,INVALIDATE_CACHE public CACHE_READ,CACHE_WRITE,FLUSH_PASS,FLUSH_INVALID_PASS public FLUSH_WHOLE_CACHE_SAV,FLUSH_WHOLE_CACHE,FLUSH_CACHE public WRITE_FROM_CACHE public Cache_hit public blkmov,INT_9,INT_19,RESET_SYSTEM,DO_INIT,SETBPB public PRINT,ITOA,INT13$INIT,DRIVEPARMS,GETNUM,DISK_ABORT public CTRL_IO,MM_SETDRIVE,FIND_VDRIVE,SET_RESET public AT_EXT_INIT,FIX_DESCRIPTOR,ABOVE_INIT public process_read_partial,process_block_read,pr_acc_trks public pr_acc_trks,pr_cur_trk,process_write_partial,process_block_write public check_parameters,process_regions,bytes_in_trk,sect_in_trk public not_in_mem,read_disk public not_in_memw,rd_partw,rd_part public region public SECTRKARRAY ENDIF ;** ; ; INT13 DEVICE HEADER ; ; COMMON TO TYPE 1, 2 drivers ; ; SEE ALSO ; MS-DOS Technical Reference manual section on ; Installable Device Drivers ; ;; The internal name of the device driver has been changed from SMARTDRV ;; to SMARTAAR to avoid DOS name conflicts with files named SMARTDRV.* ;; INT13DEV LABEL WORD DW -1,-1 DEVATS DW DEVOPCL + CharDev + DevIOCtl DW STRATEGY DW INT13$IN DB "SMARTAAR" ;Name of device BREAK ;** ; ; This is the device driver command dispatch table. ; ; The first byte indicates the size of the table and therefore defines ; which device function codes are valid. ; ; The entries in the table are NEAR word addresses of the appropriate ; device routine. Thus the address of the routine to handle device function ; 3 is: ; WORD at ((INT13TBL + 1) + (2 * 3)) ; ; COMMON TO TYPE 1, 2 drivers ; ; INT13TBL LABEL WORD DB 15 ; Max allowed command code DW INT13$INIT ; Init DW CMDERR ; Media check DW CMDERR ; Build BPB DW INT13$IOCTL_Read ; IOCTL input DW CMDERR ; Read DW CMDERR ; Non-des read no-wait DW INT13$Read_St ; Read status DW CMDERR ; Read flush DW CMDERR ; Write DW CMDERR ; Write with verify DW INT13$Write_St ; Output status DW CMDERR ; Output flush DW INT13$IOCTL_Write ; IOCTL output DW DEVEXIT ; Open DW DEVEXIT ; Close DW CMDERR ; Rem media? BREAK STATISTICS_SIZE EQU 40 DRIVER_SEL DB 0 ; 0 if /E (TYPE 1), 1 if /A (TYPE 2), DEV_SIZE DW 256 ; Size in K of the cache SECTRACK DW ? ; Sectors per track current_dev_size dw ? ; Current size in K of cache ;; Data peculiar to AT&T 6300 PLUS. S5_FLAG DB 0 ;; = S_OLIVETTI if 6300 plus machine ;; = S_VECTRA if HP Vectra machine db ? ; Spacer A20On dw 0DF90h A20Off dw 0DD00h special_mem dw 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Unfortunately the code in smartdrv is very machine dependent ; necessitating the use of a system flag to store the machine ; configuration. The system flag is initialised during init time ; and used when the caching services are requested. One bit which ; is set and tested during caching is the state of the a20 line ; when the cache code is entered. This is used because there are ; applications which enable the a20 line and leave it enabled ; throughout the duration of execution. Since smartdrv is a device ; driver it shouldn't change the state of the environment. ; ; The system flag bit assignments are: ; ; ------------------------------------------------- ; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ; ------------------------------------------------- ; |-----|-----| | | | | | ; | | | | | -----286 (and AT) ; | | | | -----------386 (later than B0) ; not | | -----------------PS/2 machine ; used | -----------------------Olivetti (not used) ; -----------------------------A20 state (enabled ?) ; ; The Olivetti guys have defined a flag of their own. This should be removed ; and the bit assigned out here for them should be used. ; sys_flg db ? ; ; equates used for the system flag ; M_286 equ 00000001B M_386 equ 00000010B M_PS2 equ 00000100B M_OLI equ 00001000B A20_ST equ 00010000B ifdef OMTI OMTI_EXT equ 00100000B endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; flag to indicate that reset code is being executed reboot_flg db 0 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; emm ctrl address, needed for the reset code emm_ctrl_addr dw EXTMEM_LOW dw EXTMEM_HIGH ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; A20 address line state determination addresses ; low_mem label dword dw 20h*4 dw 0 high_mem label dword dw 20h*4 + 10h dw 0ffffh ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; A20 PS2 equates ; PS2_PORTA equ 0092h GATE_A20 equ 010b ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 386 working areas start_gdt label byte nul_des desc <> cs_des desc <0FFFFh,0,0,09Fh,0,0> ss_des desc <0FFFFh,0,0,093h,0,0> ds_des desc <0FFFFh,0,0,093h,0,0> es_des desc <0FFFFh,0,0,093h,0,0> end_gdt label byte emm_gdt gdt_descriptor ; ; int 15 gdt ; int15_gdt label byte desc <> ;dummy descriptor desc <> ;descriptor for gdt itself src desc <0ffffh,,,93h,,> tgt desc <0ffffh,,,93h,,> desc <> ;bios cs descriptor desc <> ;stack segment descriptor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 0 if device is valid ; Non-0 if device install failed (device non-functional) ; ; We need this state because there is no way to "un-install" ; a character device as there is with block devices ; NULDEV DB 0 ; ; 0 if caching is off ; Non-0 if caching is on ; ENABLE_13 DB 1 ; 0 for debug ; ; 0 if no write through ; Non-0 if write through ; WRITE_THROUGH DB 0 ; ; 0 if no write buffering ; Non-0 if write buffering enabled ; WRITE_BUFF DB 0 ; ; 0 if cache is unlocked ; Non-0 if cache is locked ; LOCK_CACHE DB 0 ; ; 0 if full track I/O to tracks not in cache is not cached. ; Non-0 if ALL I/O is to be cached ; ALL_CACHE DB 1 ; ; 0 if reboot flush is disabled ; Non-0 if reboot flush is enabled ; REBOOT_FLUSH DB 0 ; ; An exclusion sem so that the INT 13 handler and the timer interact ; without re-entrancy problems ; INT_13_BUSY DB 0 ; Exclusion sem EVEN ; Force word data to word align ; ; Statistics counters ; ; WARNING!!!! Do not disturb the order of these!!!! See IOCTL_READ code. ; TOTAL_WRITES DD 0 WRITE_HITS DD 0 TOTAL_READS DD 0 READ_HITS DD 0 TTRACKS DW ? ; Total number of track buffers that fit ; in DEV_SIZE K (number of cache elements) TOTAL_USED DW 0 TOTAL_LOCKED DW 0 TOTAL_DIRTY DW 0 ; ; Tick counters ; TICK_SETTING DW 1092 ; Approx 1 minute TICK_CNT DW 1092 ; ; Non-zero if there are dirty buffers in the cache ; DIRTY_CACHE DW 0 ; 0 if no dirty elements in cache BREAK ; INT13 DEVICE ENTRY POINTS - STRATEGY, INT13$IN ; ; This code is standard DOS device driver function dispatch ; code. STRATEGY is the device driver strategy routine, INT13$IN ; is the driver interrupt routine. ; ; INT13$IN uses INT13TBL to dispatch to the appropriate handler ; for each device function. It also does standard packet ; unpacking. ; ; SEE ALSO ; MS-DOS Technical Reference manual section on ; Installable Device Drivers ; ASSUME CS:INT13CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING PTRSAV DD 0 ; Storage location for packet addr ;** STRATEGY - Device strategy routine ; ; Standard DOS 2.X 3.X device driver strategy routine. All it does ; is save the packet address in PTRSAV. ; ; ENTRY ES:BX -> Device packet ; EXIT NONE ; USES NONE ; ; COMMON TO TYPE 1, 2 drivers ; ; STRATP PROC FAR STRATEGY: MOV WORD PTR [PTRSAV],BX ; Save packet addr MOV WORD PTR [PTRSAV+2],ES RET STRATP ENDP ;** INT13$IN - Device interrupt routine ; ; Standard DOS 2.X 3.X device driver interrupt routine. ; ; ; ENTRY PTRSAV has packet address saved by previous STRATEGY call. ; EXIT Dispatch to appropriate function handler ; CX = Packet RW_COUNT ; DX = Packet RW_START ; ES:DI = Packet RW_TRANS ; DS = INT13CODE ; STACK has saved values of all regs but FLAGS ; All function handlers must return through one of ; the standard exit points ; USES FLAGS ; ; COMMON TO TYPE 1, 2 drivers ; ; INT13$IN: PUSH SI PUSH AX PUSH CX PUSH DX PUSH DI PUSH BP PUSH DS PUSH ES PUSH BX LDS BX,[PTRSAV] ;GET POINTER TO I/O PACKET ; ; Set up registers for READ or WRITE since this is the most common case ; MOV CX,DS:[BX.RW_COUNT] ;CX = COUNT MOV DX,DS:[BX.RW_START] ;DX = START SECTOR MOV AL,DS:[BX.REQFUNC] ; Command code MOV AH,BYTE PTR [INT13TBL] ; Valid range CMP AL,AH JA CMDERR ; Out of range command code MOV SI,OFFSET INT13TBL + 1 ; Table of routines CBW ; Make command code a word ADD SI,AX ; Add it twice since one word in ADD SI,AX ; table per command. LES DI,DS:[BX.RW_TRANS] ; ES:DI transfer address PUSH CS POP DS ASSUME DS:INT13CODE JMP WORD PTR [SI] ; GO DO COMMAND ;** EXIT - ALL ROUTINES RETURN THROUGH ONE OF THESE PATHS ; ; Exit code entry points: ; ; SEE ALSO ; MS-DOS Technical Reference manual section on ; Installable Device Drivers ; ; GENERAL ENTRY for all entry points ; All packet values appropriate to the specific device function ; filled in except for the status word in the static request ; header. ; ; CMDERR - Used when an invalid device command is detected ; ; ENTRY Stack has frame set up by INT13$IN ; EXIT Standard Device driver with error 3 ; USES FLAGS ; ; ERR$CNT - Used when READ or WRITE wants to return with error code. ; The packet RW_COUNT field is zeroed ; ; ENTRY AL is error code for low byte of packet status word ; Stack has frame set up by INT13$IN ; EXIT Standard Device driver with error AL ; USES FLAGS ; ; ERR$EXIT - Used when a function other that READ or WRITE wants to ; return an error ; ; ENTRY AL is error code for low byte of packet status word ; Stack has frame set up by INT13$IN ; EXIT Standard Device driver with error AL ; USES FLAGS ; ; DEVEXIT - Used when a function wants to return with no error ; ; ENTRY AL is value for low byte of packet status word ; NOTE: Typically there is no meaningful value ; in the AL register when EXITing through here. ; This is OK as the low 8 bits of the status word ; have no meaning unless an error occured. ; Stack has frame set up by INT13$IN ; EXIT Standard Device driver with no error ; USES FLAGS ; ; ERR1 - Used when a function wants to return with a value ; for the whole status word ; ; ENTRY AX is value for packet status word ; Stack has frame set up by INT13$IN ; EXIT Standard Device driver with or without error ; USES FLAGS ; ; COMMON TO TYPE 1, 2 drivers ; ; ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING CMDERR: MOV AL,3 ;UNKNOWN COMMAND ERROR JMP SHORT ERR$EXIT ERR$CNT: LDS BX,[PTRSAV] MOV [BX.RW_COUNT],0 ; NO sectors transferred ERR$EXIT: ; Error in AL MOV AH,(STERR + STDON) SHR 8 ;MARK ERROR RETURN JMP SHORT ERR1 EXITP PROC FAR DEVEXIT: MOV AH,STDON SHR 8 ERR1: LDS BX,[PTRSAV] MOV [BX.REQSTAT],AX ; Set return status POP BX POP ES POP DS POP BP POP DI POP DX POP CX POP AX POP SI RET ;RESTORE REGS AND RETURN EXITP ENDP ; ; The following functions are not supported at this time. ; INT13$Read_St: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING INT13$Write_St: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING JMP CMDERR BREAK SET_ZRJ3: JMP SET_ZR INT13$IOCTL_Read: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CLD CMP CX,STATISTICS_SIZE ; Must have room to transfer data. JB SET_ZRJ3 ; Not enough room from user MOV [TOTAL_USED],0 MOV [TOTAL_LOCKED],0 MOV [TOTAL_DIRTY],0 ; ; Count all occupied, dirty and locked elements ; MOV [INT_13_BUSY],1 MOV SI,[CACHE_HEAD] INC SI NEXTCC: DEC SI TEST [SI.TRACK_FLAGS],TRACK_FREE JNZ SKIPCC INC [TOTAL_USED] TEST [SI.TRACK_FLAGS],TRACK_LOCKED JZ TEST_DIRTY INC [TOTAL_LOCKED] TEST_DIRTY: TEST [SI.TRACK_FLAGS],TRACK_DIRTY JZ SKIPCC INC [TOTAL_DIRTY] SKIPCC: MOV SI,[SI.FWD_LRU_LNK] INC SI JNZ NEXTCC MOV [INT_13_BUSY],0 MOV AL,[WRITE_THROUGH] MOV AH,[WRITE_BUFF] STOSW MOV AL,[ENABLE_13] MOV AH,[NULDEV] STOSW MOV AX,[TICK_SETTING] STOSW MOV AL,[LOCK_CACHE] MOV AH,[REBOOT_FLUSH] STOSW MOV AL,[ALL_CACHE] XOR AH,AH ; Unused currently STOSW MOV SI,OFFSET TOTAL_WRITES MOV CX,12 REP MOVSW ; ; Transfer Above Board Information ; xor dx,dx mov es:[di][0],dx mov es:[di][2],dx mov es:[di][4],dx cmp [driver_sel],dl ; is it expanded memory? jz no_ems ; no, info already set mov cx,16 mov ax,[current_dev_size] div cx or dx,dx jz no_remain inc ax xor dx,dx no_remain: stosw mov ax,[dev_size] div cx or dx,dx jz no_remaind inc ax no_remaind: stosw mov ax,MIN_CACHE_SIZE_K / 16 stosw no_ems: LDS BX,[PTRSAV] ASSUME DS:NOTHING MOV [BX.RW_COUNT],STATISTICS_SIZE ; transfer amount JMP DEVEXIT BREAK ; ; Command table for IOCTL Write functions. The first byte of the written ; data contains the "function" code which is dispatched via this ; table. The first byte is the maximum legal function code, then the word ; addresses of the handlers for each function. ; IOCTLTBL DB 0Ch DW IOCTL_FLUSH ; Function 0h DW IOCTL_FLUSH_INVALID ; Function 1h DW IOCTL_DISABLE ; Function 2h DW IOCTL_ENABLE ; Function 3h DW IOCTL_WRITE_MOD ; Function 4h DW IOCTL_SET_TICK ; Function 5h DW IOCTL_LOCK ; Function 6h DW IOCTL_UNLOCK ; Function 7h DW IOCTL_REBOOT ; Function 8h DW IOCTL_STAT_RESET ; Function 9h DW IOCTL_ALL_CACHE ; Function Ah dw ioctl_reduce_cache_size ; Function Bh dw ioctl_increase_cache_size ; Function Ch SET_ZR: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING LDS BX,[PTRSAV] ASSUME DS:NOTHING MOV [BX.RW_COUNT],0 ; NO bytes transferred JMP DEVEXIT SET_ERR_CNT: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV AL,3 ;UNKNOWN COMMAND ERROR JMP ERR$CNT INT13$IOCTL_Write: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CMP [NULDEV],0 ; Is the device valid? JNZ SET_ZR ; No, you get an error MOV AL,ES:[DI] ; Get command byte MOV AH,BYTE PTR [IOCTLTBL] ; Valid range CMP AL,AH ; In range? JA SET_ERR_CNT ; No MOV SI,OFFSET IOCTLTBL + 1 ; Table of routines CBW ; Make command code a word ADD SI,AX ; Add it twice since one word in ADD SI,AX ; table per command. JMP WORD PTR [SI] ; GO DO COMMAND ;** IOCTL_FLUSH -- Flush the cache, but keep the data ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_FLUSH: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 CALL FLUSH_WHOLE_CACHE MOV [INT_13_BUSY],0 JMP DEVEXIT ;** IOCTL_FLUSH_INVALIDATE -- Flush the cache, and discard the data ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_FLUSH_INVALID: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 CALL FLUSH_WHOLE_CACHE CALL INVALIDATE_CACHE MOV [INT_13_BUSY],0 JMP DEVEXIT ;** IOCTL_DISABLE -- Disable the caching for both reads and writes ; ; Also flush and invalidate the cache ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_DISABLE: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 CALL FLUSH_WHOLE_CACHE CALL INVALIDATE_CACHE MOV [ENABLE_13],0 MOV [INT_13_BUSY],0 JMP DEVEXIT ;** IOCTL_ENABLE -- Enable the caching for reads (and possibly writes) ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_ENABLE: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV [ENABLE_13],1 JMP DEVEXIT ;** IOCTL_WRITE_MOD -- En/Disable Write through and write caching ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; Second byte of data indicates what to set ; 0 Turn off Write through ; 1 Turn on Write through ; 2 Turn off Write buffering (also flush) ; 3 Turn on Write buffering (also flush) ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_WRITE_MOD: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CMP CX,2 ; Did user write enough? JB SET_ZR ; No, error MOV AL,ES:[DI.1] ; Get second byte CMP AL,3 ; In range? JA SET_ZR ; No, error CMP AL,2 ; WT or WB? JB SET_WRITE_TH ; WT DEC AL DEC AL ; 2 = 0, 3 = 1 MOV [INT_13_BUSY],1 MOV [WRITE_BUFF],AL CALL FLUSH_WHOLE_CACHE MOV [INT_13_BUSY],0 JMP DEVEXIT SET_WRITE_TH: MOV [WRITE_THROUGH],AL JMP DEVEXIT ;** IOCTL_SET_TICK -- Set tick count for auto flush ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; Second byte and third byte of data is value to set ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_SET_TICK: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CMP CX,3 ; Did user write enough? JB SET_ZRJ ; No, error MOV AX,ES:[DI.1] ; Get second byte and third byte as word MOV [TICK_SETTING],AX JMP DEVEXIT SET_ZRJ: JMP SET_ZR ;** IOCTL_LOCK -- Lock the current cache ; ; Also flush the cache ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_LOCK: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 CALL FLUSH_WHOLE_CACHE MOV [LOCK_CACHE],1 ; ; Lock all cache elements that have something in them ; MOV SI,[CACHE_HEAD] INC SI NEXTCS: DEC SI TEST [SI.TRACK_FLAGS],TRACK_FREE JNZ SKIPCS OR [SI.TRACK_FLAGS],TRACK_LOCKED SKIPCS: MOV SI,[SI.FWD_LRU_LNK] INC SI JNZ NEXTCS MOV [INT_13_BUSY],0 JMP DEVEXIT ;** IOCTL_UNLOCK -- Unlock the cache ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_UNLOCK: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 MOV [LOCK_CACHE],0 ; ; UnLock all cache elements ; MOV SI,[CACHE_HEAD] INC SI NEXTCX: DEC SI AND [SI.TRACK_FLAGS],NOT TRACK_LOCKED MOV SI,[SI.FWD_LRU_LNK] INC SI JNZ NEXTCX MOV [INT_13_BUSY],0 JMP DEVEXIT ;** IOCTL_REBOOT -- En/Disable Reboot flush ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; Second byte of data indicates what to set ; 0 Turn off reboot flush ; 1 Turn on reboot flush ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_REBOOT: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CMP CX,2 ; Did user write enough? JB SET_ZRJ ; No, error MOV AL,ES:[DI.1] ; Get second byte CMP AL,1 ; In range? JA SET_ZRJ ; No, error MOV [REBOOT_FLUSH],AL JMP DEVEXIT ;** IOCTL_STAT_RESET -- Reset the INT 13 statistics counters ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_STAT_RESET: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING XOR AX,AX MOV WORD PTR [TOTAL_WRITES],AX MOV WORD PTR [TOTAL_WRITES + 2],AX MOV WORD PTR [WRITE_HITS],AX MOV WORD PTR [WRITE_HITS + 2],AX MOV WORD PTR [TOTAL_READS],AX MOV WORD PTR [TOTAL_READS + 2],AX MOV WORD PTR [READ_HITS],AX MOV WORD PTR [READ_HITS + 2],AX JMP DEVEXIT ;** IOCTL_ALL_CACHE -- En/Disable All cache ; ; ENTRY: ; ES:DI is transfer address ; CX is transfer count ; Second byte of data indicates what to set ; 0 Turn off all cache ; 1 Turn on all cache ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; IOCTL_ALL_CACHE: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CMP CX,2 ; Did user write enough? JB SET_ZRJ2 ; No, error MOV AL,ES:[DI.1] ; Get second byte CMP AL,1 ; In range? JA SET_ZRJ2 ; No, error MOV [ALL_CACHE],AL JMP DEVEXIT SET_ZRJ2: JMP SET_ZR ;** ioctl_reduce_cache_size Dynamically reduce the size of the cache ; ; This routine dynamically reduces the size of an Above Board memory (/A) ; cache. The routine is passed the number of pages that the cache should ; be reduced by. The minimum size for a cache is 64K or 4 pages. In ; removing the tracks from the cache, the memory is returned to the EMM ; and then the cache control structures for that memory are taken off the ; LRU list, and set to free. ; ; NOTE: In this version of INT13, only reads are cached, so that when ; a cached track is "removed" from memory, it's contents are not ; updated to disk. ; ; ENTRY: ; ES:DI is transfer address ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; error_reduce_exitj: jmp error_reduce_exit ioctl_reduce_cache_size: assume ds:Int13Code assume es:nothing assume ss:nothing cmp byte ptr [driver_sel],1 ; Make sure using Above Board jnz error_reduce_exitj mov ax,es:[di.1] ; Get second byte or ax,ax ; Reduce by a non-0 amount? jnz do_it jmp devexit do_it: mov cl,4 ; Multiply by 16 to get # of K bytes shl ax,cl ; AX has requested reduction in K mov bx,word ptr [current_dev_size] mov si,bx ; Save current_dev_size in SI for error sub bx,ax ; BX now has remaining cache memory in K cmp bx,MIN_CACHE_SIZE_K ; Compare BX with min cache in K bytes jl error_reduce_exitj mov word ptr [current_dev_size],bx shr bx,cl ; BX has new cache size in pages mov byte ptr [int_13_busy],1 ; ; Request Reallocation of Pages from EMM ; push ax mov dx,word ptr [above_pid] mov ah,ABOVE_REALLOCATE_PID int 67h or ah,ah jnz real_fail ; ; Determine how many cache tracks will be lost ; mov bx,word ptr [sectrack] ; Init code checked for too large track mov cl,9 shl bx,cl ; BX has bytes/track mov ax,word ptr [ttracks] mul bx ; AX:DX has bytes used by tracks xchg ax,si mov di,dx mov cx,1024 mul cx ; AX:DX has total bytes allocated sub ax,si ; Find difference in allocated and used sbb dx,di xchg ax,si xchg dx,di pop ax ; AX still has requested reduction in K mul cx ; DX:AX has requested reduction in byte ; ; Make sure the we have to free up tracks in order to satisfy the request ; HACK! HACK! HACK! Smartdrv should only allocate as many pages as it needs. ; ChipA 14-Mar-1988 ; cmp dx,di ja free_tracks jb no_need_to_free_tracks cmp ax,si ja free_tracks no_need_to_free_tracks: sub ax,ax mov dx,ax jmp all_freed free_tracks: sub ax,si ; Account for part not used sbb dx,di div bx ; AX has number of tracks being "lost" or dx,dx jz no_remainder inc ax ; Add one more track if remainder ; ; Determine which cache control structures we are to remove ; no_remainder: mov si,ax mov cx,SIZE CACHE_CONTROL mul cx xchg ax,bx ; BX has backward offset into CCS's mov ax,word ptr [ttracks] sub word ptr [ttracks],si mul cx ; AX has end of Cache Control sub ax,bx ; AX has start offset of CCS's to remove mov cx,si ; CX has number of CCS's to remove mov si,word ptr [cache_control_ptr] add si,ax ; SI points to first CCS to modify ; ; Loop through each cache control structure, removing from LRU list ; remove_cache_entries: mov word ptr [si].track_flags,TRACK_FREE call unlink_cache add si,SIZE CACHE_CONTROL loop remove_cache_entries all_freed: mov byte ptr [int_13_busy],0 jmp devexit real_fail: pop ax mov [current_dev_size],si ; Restore former dev size mov byte ptr [int_13_busy],0 error_reduce_exit: mov al,0Ch ; General failure jmp err$cnt ;** ioctl_increase_cache_size Dynamically increase the size of the cache ; ; This routine dynamically increases the size of an Above Board memory (/A) ; cache. The routine is passed the number of pages that the cache should ; be increased by. The maximum size allowed is the size specified from the ; INT13.SYS command line. The cache control structures for the memory added ; to the cache are placed on the LRU list at the LRU position, so that they ; will be immediately available for incoming tracks. ; ; ENTRY: ; ES:DI is transfer address ; EXIT: ; Through one of the device exit paths ; USES: ; ALL ; error_increase_exitj: jmp error_increase_exit ioctl_increase_cache_size: assume ds:Int13Code assume es:nothing assume ss:nothing cmp [driver_sel],1 ; Make sure using Above Board jnz error_increase_exitj mov ax,es:[di.1] ; Get second byte mov cl,4 ; Multiply by 16 to get # of K bytes shl ax,cl ; AX has requested addition in K mov bx,word ptr [current_dev_size] mov si,bx ; Save current dev size for error add bx,ax ; BX now has new cache memory size in K cmp bx,[dev_size] ; Compare BX with largest size in K bytes jbe increase_size_ok mov bx, [dev_size] ; Go to MAX size mov ax, bx sub ax, si ; Correct increase increase_size_ok: mov word ptr [current_dev_size],bx shr bx,cl ; BX has new cache size in pages mov [int_13_busy],1 ; ; Request Reallocation of Pages from EMM ; push ax mov dx,[above_pid] mov ah,ABOVE_REALLOCATE_PID int 67h or ah,ah jnz realloc_fail ; ; Determine how many cache tracks will be gained ; mov bx,word ptr [sectrack] ; Init code checked for too large track mov cl,9 shl bx,cl ; BX has bytes/track mov ax,word ptr [ttracks] mul bx ; AX:DX has bytes used by tracks xchg ax,si mov di,dx mov cx,1024 mul cx ; AX:DX has total bytes allocated sub ax,si ; Find difference in allocated and used sbb dx,di xchg ax,si xchg dx,di pop ax ; AX still has requested reduction in K mul cx ; DX:AX has requested reduction in byte add ax,si ; Account for part not used adc dx,di div bx ; AX has number of tracks being "gained" or ax, ax ; DIV trashes flags jz nothing_to_gain ; ; Determine which cache control structures we are to add ; mov si,ax mov cx,SIZE CACHE_CONTROL mov ax,[ttracks] add [ttracks],si ; Update TTRACKS mul cx ; AX has end of Cache Control mov cx,si ; CX has number of CCS's to add mov si,[cache_control_ptr] add si,ax ; SI points to first CCS to modify ; ; Loop through each cache control structure, adding to LRU list ; add_cache_entries: mov di,si ; Place element at LRU position xchg di,[cache_tail] mov [di].fwd_lru_lnk,si mov [si].back_lru_lnk,di mov [si].fwd_lru_lnk,-1 mov [si].track_flags,TRACK_FREE add si,SIZE CACHE_CONTROL loop add_cache_entries nothing_to_gain: mov [int_13_busy],0 jmp devexit realloc_fail: pop ax mov [current_dev_size],si ; Restore to original size mov [int_13_busy],0 error_increase_exit: mov al,0Ch ; General failure jmp err$cnt ; ; If the device errors out during install, we set the break address here. ; ERROR_END LABEL BYTE BREAK EVEN ; Force word align ; ; Storage for the INT 1C vector BEFORE cache installed ; OLD_1C DD ? ;** INT_1C_HANDLER - Handler for INT 1C timer ticks ; ; ENTRY ; None ; ; EXIT ; To next 1C handler, EOI may be sent ; ; USES ; None ; ; SEE ALSO ; IBM PC TECH REF MANUAL section on INT 1C ; DOS PRINT utility (most of this is stolen from there) ; INT_1C_HANDLER PROC FAR ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH AX DEC [TICK_CNT] JNZ CHAIN_1C CMP [INT_13_BUSY],0 JZ TRY_FLSH RE_TRIGGER: INC [TICK_CNT] ; Set it back to 1 so next tick triggers CHAIN_1C: POP AX JMP [OLD_1C] TRY_FLSH: mov al,00001011b ; Select ISR in 8259 out 20H,al jmp short yyyy yyyy: jmp short zzzz zzzz: in al,20H ; Get ISR register and al,0FEH ; Mask timer int jnz RE_TRIGGER ; Another int is in progress INC [INT_13_BUSY] ; Exclude CMP [DIRTY_CACHE],0 ; Anything to do? JZ SKIP_FLSH ; No STI mov al,20H out 20H,al CALL FLUSH_WHOLE_CACHE_SAV SKIP_FLSH: MOV AX,[TICK_SETTING] CLI MOV [TICK_CNT],AX MOV [INT_13_BUSY],0 JMP CHAIN_1C INT_1C_HANDLER ENDP BREAK ; INT 13 stack frame ; STACK_FRAME STRUC USER_OFF DW ? ; added for user transfer address USER_ES DW ? USER_DS DW ? USER_DI DW ? USER_SI DW ? USER_BP DW ? DW ? USER_BX DW ? USER_DX DW ? USER_CX DW ? USER_AX DW ? USER_IP DW ? USER_CS DW ? USER_FL DW ? STACK_FRAME ENDS EVEN ; Force word align ; ; Storage for the INT 13 vector BEFORE cache installed ; OLD_13 DD ? ; ; Array of sec/track for all hardfiles on system. First element cooresponds ; to first hard file. ; Value = 0 indicates no hardfile ; SECTRKARRAY DB MAX_HARD_FILES DUP (0) ; ; ARRAY OF MAXIMUM USEABLE HEADS FOR ALL THE HARDFILES IN THE SYSTEM. FIRST ; ELEMENT CORRESPONDS TO FIRST HARDFILE. ; VALUE = FF INDICATES NO HARDFILE (SUNILP) ; HDARRAY DB MAX_HARD_FILES DUP (0FFH) ifdef OMTI OMTI_SET_CYL EQU 0EEh OMTI_GET_CYL EQU 0FEh OMTI_GET_REV EQU 0F9h endif ;OMTI ; ; INT 13 function dispatch ; Addresses of routines for AH INT 13 functions ; INT13DISPATCH LABEL WORD DW POP_NO_PROC ; 0, reset DW POP_NO_PROC ; 1, Read status DW CACHE_READ ; 2, read DW CACHE_WRITE ; 3, write DW POP_NO_PROC ; 4, verify DW INVALID_PASS ; 5, format DW INVALID_PASS ; 6, format DW INVALID_PASS ; 7, format DW POP_NO_PROC ; 8, drive parms DW INVALID_PASS ; 9, Init drive characteristic DW INVALID_PASS ; A, Read long DW INVALID_PASS ; B, Write long DW POP_NO_PROC ; C, Seek DW POP_NO_PROC ; D, Alt reset DW INVALID_PASS ; E, Read buffer DW INVALID_PASS ; F, Write buffer DW POP_NO_PROC ; 10, Test drive rdy DW POP_NO_PROC ; 11, Recalibrate DW POP_NO_PROC ; 12, Controller diag DW INVALID_PASS ; 13, Drive diag DW POP_NO_PROC ; 14, Controller diag internal DW POP_NO_PROC ; 15, READ DASD ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; MODIFICATIONS TO DATA STRUCT TO SUPPORT MULTI-TRACK I/O ; ; sunilp ; ; extra declarations needed ; max_hd db ? ;maximum useable head number for curr. drive sect_in_trk db ? ;maximum sector number in trk for curr drive bytes_in_trk dw ? ;numb of bytes in trk for current drive int13err db ? ; TRUE = 0ffH FALSE = NOT TRUE REG1_P = 001B REG2_P = 010B REG3_P = 100B ; REG1t struc START_H db ? ;start head START_T dw ? ;start track COUNT dw ? ;number of words START_S dw ? ;start sector REG1t ends ; REG2t struc db ? ;start head dw ? ;start track TRACKS db ? ;number of tracks REG2t ends ; REG3t struc db ? ;start head dw ? ;start track dw ? ;number of words REG3t ends ; REGIONt struc FLAG db 0 REG1 db size REG1t dup(?) REG2 db size REG2t dup(?) REG3 db size REG3t dup(?) REGIONt ends ; ; REGION db size REGIONt dup(?) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; INT_13_HANDLER - Handler for INT 13 requests ; ; ENTRY ; All regs as for INT 13 ; ; EXIT ; To old INT 13 handler with regs unchanged if cache not involved ; Else return in AH and flags as per INT 13. ; ; USES ; AH and carry bit of FLAGS ; ; SEE ALSO ; IBM PC TECH REF MANUAL section on INT 13 ; INT_13_HANDLER PROC FAR ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 ; Exclude TEST DL,80H ; Hard file? JNZ IS_HARD ; Yes NO_PROC: PUSHF CLI CALL [OLD_13] ; Let old handler do it MOV [INT_13_BUSY],0 ; Clear sem RET 2 ; "IRET" chucking flags ; ; Pop off the stack frame and pass to old handler ; POP_NO_PROC: POP BX POP ES POP DS ; Simulate POPA POP DI POP SI POP BP POP BX ; Dummy for pop sp POP BX POP DX POP CX POP AX ; JMP NO_PROC IS_HARD: STI ; INTs ok now ; ; Set up standard stack frame ; ; Simulate PUSHA PUSH AX PUSH CX PUSH DX PUSH BX PUSH BX ; dummy for push sp PUSH BP PUSH SI PUSH DI ; PUSH DS PUSH ES push bx ; Set frame pointer MOV BP,SP MOV BL,AH XOR BH,BH ; Command in BX PUSH CS POP DS ASSUME DS:INT13CODE CMP [ENABLE_13],0 ; Are we enabled? JZ POP_NO_PROC ; No, ignore ifdef OMTI ; ; The following code is used to handle the extended cylinder access method ; used by the OMTI controller. This controller has a modified INT13 routine ; that uses a unique function number to tell the controller that the next ; access to INT13 is for an extended cylinder. ; test sys_flg,OMTI_EXT ; Are we in extended state? jnz in_extended_state cmp bx,OMTI_SET_CYL ; Is this the OMTI extended function? jnz check_command_range or sys_flg,OMTI_EXT jmp pop_no_proc in_extended_state: ; ; If we are in the extended state, we want to make sure that this call ; does not go through into the cache. Therefore, we will check to see ; if this is a read or write function, and will return to the old INT13 ; routine if so. ; and sys_flg,NOT OMTI_EXT ; Clear the extended flag cmp bx,2h ; READ jz pop_no_proc cmp bx,3h ; WRITE jz pop_no_proc check_command_range: ; ; Allow the other OMTI functions to pass through ; cmp bx,OMTI_GET_CYL jz pop_no_proc cmp bx,OMTI_GET_REV jz pop_no_proc endif ; OMTI CMP BX,15H ; Command in range? JA INVALID_PASS ; No, throw out cache SHL BX,1 ; Times two bytes per table entry JMP [BX.INT13DISPATCH] ; Dispatch to handler ;** FLUSH_INVALID_PASS -- Discard cache and pass through ; ; ENTRY: ; INT 13 regs except for BP,BX and DS ; EXIT: ; To old INT13 handler ; FLUSH_INVALID_PASS: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CALL FLUSH_WHOLE_CACHE CALL INVALIDATE_CACHE JMP POP_NO_PROC ;** INVALID_PASS -- DISCARD CACHE, NO NEED TO FLUSH, PASS THROUGH ; ;** SUNIL PAI ; ; ENTRY: ; INT 13 regs except for BP,BX and DS ; EXIT: ; To old INT 13 handler ; INVALID_PASS: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CALL INVALIDATE_CACHE JMP POP_NO_PROC ;** FLUSH_PASS -- Flush cache (but retain data) and pass through ; ; ENTRY: ; INT 13 regs except for BP,BX and DS ; EXIT: ; To old INT13 handler ; FLUSH_PASS: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CALL FLUSH_WHOLE_CACHE JMP POP_NO_PROC BREAK ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING ; cache_read: ; ; inputs: int13 regs except for bp - user stack frame ; bx - function (read,write) ; ds - int13code segment ; ; exits: to user success routine ; to user error routine ; to old int13 handler call ; ; uses: all but bp ; ; written by: sunil pai ; call check_parameters ; check user params jc pop_no_proc ; if error go to old int 13 handler ; call process_regions ; for multi-track business find the ; initial partial track, middle block ; and final partial track ; sets up REGION struct ; ; mov [int13err],FALSE ; clear int 13 error flag ; test [REGION.FLAG],REG1_P ; is region1 present je cr$2 ; if not present try region2 ; mov dh,[REGION.REG1.START_H] ;start head mov cx,[REGION.REG1.START_T] ;get start track mov bx,[REGION.REG1.START_S] ;start sector mov ax,[REGION.REG1.COUNT] ;number of words call process_read_partial ; jc error ;go to process error ; cr$2: test [REGION.FLAG],REG2_P ; is region2 present je cr$3 ; if not present try region3 ; mov dh,[REGION.REG2.START_H] ; get start head mov cx,[REGION.REG2.START_T] ; get start track mov al,[REGION.REG2.TRACKS] ; get number of tracks call process_block_read ; multi track capability jc error ; fouled? ; cr$3: test [REGION.FLAG],REG3_P ; is region3 present je suc ; if not we are done ; mov dh,[REGION.REG3.START_H] ; start head mov cx,[REGION.REG3.START_T] ; start track mov bx,1 ; start sector is 1 mov ax,[REGION.REG3.COUNT] ; number of words call process_read_partial ; jc error ; just as we were about to fin! ; ; exit points - suc and error. ; suc: and [bp.USER_FL],NOT f_Carry ; mov byte ptr [bp.USER_AX.1],0 ; ; operation_done: mov cs:[int_13_busy],0 ;clear semaphore pop bx ;user offset ; ; simulate popa ; pop es pop ds pop di pop si pop bp pop bx pop bx pop dx pop cx pop ax ; iret ; error: ; ; the next two instructions were removed because of the way Compaq ; handles bad sectors. they mark sectors bad not tracks. so in a ; track there may be good and bad sectors. However our int13 caching ; system does i/o from disk in tracks and will not get any track with ; bad sectors. to take care of this, we pass the read to the old int13 ; handler even when there is an int 13 error ; ; test [int13err],TRUE ; jne er$1 ;if not int13 error go to call ;int13 jmp pop_no_proc ;er$1: or [bp.USER_FL],f_Carry ; ; mov byte ptr [bp.USER_AX.1],AL ;int13 error code ; jmp operation_done ; pop_no_procw: call invalidate_cache jmp pop_no_proc ; cache_write: ; ; inputs: int13 regs except for bp - user stack frame ; bx - function (read,write) ; ds - int13code segment ; ; uses: all but bp ; ; written by: sunil pai ; call check_parameters ;check user params jc pop_no_procw ;error ; call process_regions ;for multi-track business find the ;initial partial track, middle block ;and final partial track ;sets up REGION struct ; ; writes always update disk, i.e., write through always operational ; mov ax,[bp.user_ax] ; restore int13 registers mov cx,[bp.user_cx] ; mov dx,[bp.user_dx] ; mov bx,[bp.user_bx] ; mov es,[bp.user_es] ; pushf ; since interrupt routine being called cli call [old_13] ; call old int 13 handler jnc cw$1 ; no error in writing to disk, continue ; or [bp.user_fl],f_Carry ; error then set carry mov byte ptr [bp.user_ax.1],ah ; and error code jmp short errorw ; and take error exit ; ; int 13 was successful, now we have to update cache as well ; cw$1: and [bp.user_fl],not f_Carry ; int 13 success mov byte ptr [bp.user_ax.1],0 ; ; and dl,not 80h ; test [REGION.FLAG],REG1_P ; is region1 present je cw$2 ; if not present try region2 ; mov dh,[REGION.REG1.START_H] ; get start head mov cx,[REGION.REG1.START_T] ; get start track mov bx,[REGION.REG1.START_S] ; start sector mov ax,[REGION.REG1.COUNT] ; number of words call process_write_partial ; partial write jc errorw ; go to process error ; cw$2: test [REGION.FLAG],REG2_P ; is region2 present je cw$3 ; if not present try region3 ; mov dh,[REGION.REG2.START_H] ; get start head mov cx,[REGION.REG2.START_T] ; get start track mov al,[REGION.REG2.TRACKS] ; get number of tracks call process_block_write ; jc errorw ; fouled? ; cw$3: test [REGION.FLAG],REG3_P ; is region3 present je operation_donew ; if not we are done ; mov dh,[REGION.REG3.START_H] ; start head mov cx,[REGION.REG3.START_T] ; start track mov bx,1 ; start sector is 1 mov ax,[REGION.REG3.COUNT] ; number of words call process_write_partial ; jnc operation_donew ; no error - finish ; errorw: call invalidate_cache ; we are not sure of the operation_donew: jmp operation_done ; ; INT_13_HANDLER endp ; process_read_partial proc near ; ; is used to read a partial track ; ; inputs: dx: head and drive (8th bit stripped) ; cx: track ; bx: start sector ; ax: number of words ; ; outputs:cy set if error ; clear if success ; ; strategy: ; if (track in cache) then { ; if (track in track_buffer) then ; perform user read from track buffer; ; else ; perform user read from cache; ; } ; else { ; read track into track buffer; ; read track buffer into cache; ; perform user read from track buffer; ; } ; ; cache transfers handled by freeing cache element if possible ; and making it lru ; ; uses: only dx assumed unchanged ; call track_in_cache ; is track in cache jc read_disk ; if not we have to read from disk ; cmp cx,[track_buffer_cyln] ; is it in the track buffer jnz not_in_mem ; no cmp dx,[track_buffer_hddr] ; jz read_bufferj ; yes ; ; read buffer from cache ; not_in_mem: push dx xchg ax,bx ;get number of words in bx and start sect in ax dec ax ;0 based number mov cl,9 ; shl ax,cl ;byte offset xor dx,dx ; ; mov cx,bx ;number of words mov es,[bp.user_es] ; mov di,[bp.user_off]; shl bx,1 ; number of bytes add [bp.user_off],bx ; update user offset ; add ax,word ptr [si.base_offset] adc dx,word ptr [si.base_offset.2] xor bh,bh push si push ds push bp call blkmov pop bp pop ds pop si pop dx jc err_cache call cache_is_mru clc ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; track not in cache. see if free cache available to initiate transfer ; read_disk: cmp di,-1 ;free cache jnz rd_part ;if present we can initiate read stc ; else error exit ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; read_bufferj: jmp short read_buffer ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; cache element available. we can start by transferring track from disk ; to track buffer. ; rd_part: mov si,di ;get element in si mov [track_buffer_cyln],-1 ;invalidate present track buffer mov [track_buffer_hddr],-1 mov [si.track_flags],track_free ;if read fails push dx push cx push bx push ax or cl,1 or dl,80h mov al,[sect_in_trk] mov ah,2 ; read push cs pop es mov bx,[track_buffer_ptr] pushf cli call [old_13] jnc rd$1 cmp ah,11h ;the int13 error which is not an error je rd$1 mov [int13err],TRUE add sp,8 mov al,ah stc ret ; ; transfer track buffer to cache ; rd$1: mov di,[track_buffer_ptr] ;es:di is transfer address mov cx,[bytes_in_trk] shr cx,1 ; cx is number words in track mov bh,1 ;write mov ax,word ptr [si.base_offset] ;address of cache mov dx,word ptr [si.base_offset.2] ; push ds push si push bp call blkmov pop bp pop si pop ds pop ax pop bx pop cx pop dx jnc rd$2 ; ; error in transferring info to cache. invalidate cache element and ; make it lru ; err_cache: mov [si.track_flags],track_free call cache_is_lru ; make cache element lru stc ret ; ; info transfer to cache successful. fill in track, head and drive ; info into cache control element and track buffer control element ; if debug rd_2: endif rd$2: mov word ptr [si.track_cyln],cx mov word ptr [si.track_drive],dx and [si.track_flags], not track_free mov word ptr [track_buffer_cyln],cx mov word ptr [track_buffer_hddr],dx ; ; perform user i/o from track buffer ; read_buffer: call cache_is_mru ; mov si,bx ; start sector dec si ; 0 based number mov cl,9 ; shl si,cl ; byte offset in si add si,[track_buffer_ptr] mov cx,ax mov es,[bp.user_es] mov di,[bp.user_off] cld rep movsw mov [bp.user_off],di clc ret ; process_read_partial endp ; process_block_read proc near ; ; inputs: ax = number of tracks ; dx = drive and head (8th bit stripped) ; cx = start track ; bp = user stack frame ; ; outputs: cy set if error ; cy clear if okay ; ; algorithm: ; ; repeat ; if (cur_trk in cache) then ; transfer track from cache to user buffer; ; no_of_trks = no_of_trks - 1 ; else ; accumulate tracks which are not in cache ; read these in one disk operation ; transfer these from user buffer to cache ; no_of_trks = no_of_trks - accumulated_trks ; until no_of_trks == 0 ; pbr$1: ; ; since we are going to look ahead and see how many tracks can ; be dealt with together, we have to save start head and track ; push cx ; save start head push dx ; save start track xor ah,ah ;number of accumulated tracks pbr$2: call track_in_cache ; jnc pbr$4 ; if current track in cache start processing inc dh ; go to next track call adj_hd_trk ; inc ah ;accumulate the tracks dec al ;are we done jne pbr$2 ;go to see next track pop dx ;restore start head and track pop cx ; call pr_acc_trks ;process the accumulated tracks ret ;we are done pbr$4: pop dx ;restore start head and track pop cx or ah,ah ;are there any accumulated je pbr$5 call pr_acc_trks ;process the accumulated tracks jc pbr$7 ;if carry set finish with error add dh,ah ;adjust track and head call adj_hd_trk jmp pbr$1 ; pbr$5: call pr_cur_trk ;process current track which is in cache jc pbr$7 ;if carry set finish with error inc dh call adj_hd_trk pbr$6: dec al ;are we done? jne pbr$1 ; clc pbr$7: ret ; process_block_read endp ; pr_acc_trks proc near ; ; inputs: cx = start track ; dh = start head ; ah = number of accumulated tracks ; ; outputs: if success :- cy clear, [bp.user_off] modified ; if failure :- cy set ; ; regs to be preserved: al,cx,si ; ; algorithm: ; ; read buffer from disk; ; for (cur_trk=start_trk,transfer_off=user_off; no_of_trks-- > 0; ; transfer_off=transfer_off+size_of_trk) do ; if ((cache=get_cache())!=-1) then ; transfer_trk_to_cache; ; else ; exit with error; ; ; push si push dx push cx push ax ; stack:- {ax,cx,dx,si} ; ; initialise int13 registers for reading the accumulated tracks into ; user memory. ; mov al,ah ;number of tracks xor ah,ah ;in ax ; mul [sect_in_trk] ;get number of sectors in ax ; mov ah,2 or cx,1 ;start sector ; or dl,80h ;set hard disk bit mov bx,[bp.user_off]; mov es,[bp.user_es] ; ; pushf cli call [old_13] ;perform multi track read ; ; check for int 13 error ; jnc pat$1 ;if okay proceed ; add sp,8 ;clear stack mov al,ah mov [int13err],TRUE ; stc ret ;error exit ; ; we have succesfully read al tracks into user memory. now these have ; to transfer these to cache. ; pat$1: and dl,not 80h ;clear off 8th bit pop ax ;restore ax pop cx ; push cx ; push ax ; mov di,[bp.user_off];initialise transfer offset ; ; ah has number of tracks still left to be transferred ; di has transfer offset ; cx has current track number ; dx has current drive and head ; pat$2: call get_cache ;get free cache element ; ; si = cache element ; cmp si,-1 ;was there an element je pat$5 ;cache saturated exit ; ; check if track is in track buffer ; cmp cx,[track_buffer_cyln] jne pat$21 cmp dx,[track_buffer_hddr] jne pat$21 ; ; track is in track buffer. to avoid two transfers invalidate ; track buffer ; mov [track_buffer_cyln],-1 mov [track_buffer_hddr],-1 ; ; transfer track from user buffer to cache ; pat$21: mov [si.track_flags],track_free ;if write fails push cx push ax push ds push si push bp push di push dx ; mov es,[bp.user_es] mov cx,[bytes_in_trk] shr cx,1 ;words mov ax,word ptr [si.base_offset] mov dx,word ptr [si.base_offset.2] mov bh,1 ;write call blkmov ; pop dx pop di pop bp pop si pop ds pop ax pop cx ; jnc pat$3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; error in transferring information to cache. make it lru ; ; call cache_is_lru pat$5: add sp,8 stc ret ;cache error exit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; successfully transferred information to cache. fill in track and ; drive and head information into cache control element ; pat$3: mov word ptr [si.track_cyln],cx mov word ptr [si.track_drive],dx and [si.track_flags],not track_free call cache_is_mru ; add di,[bytes_in_trk] ;advance pointer in user transfer buffer to ;next track ; inc dh ;advance track call adj_hd_trk ; pat$4: dec ah ;have we processed all the accumulated trks jne pat$2 mov [bp.user_off],di ;update user transfer address to beyond ;this accumulated block pop ax pop cx pop dx pop si clc ret ;success exit ; pr_acc_trks endp ; pr_cur_trk proc near ; ; inputs: ; si = cache element ; cx = current track ; dx = drive and head ; ; outputs: ; cy = set if error ; = clear if success ; ; algorithm: ; transfer track from cache to user memory ; push ax push cx push dx push si push ds push bp ; mov cx,[bytes_in_trk] shr cx,1 mov ax,word ptr [si.base_offset] mov dx,word ptr [si.base_offset.2] mov es,[bp.user_es] mov di,[bp.user_off] xor bh,bh ;read call blkmov ; pop bp pop ds pop si pop dx pop cx pop ax ; jnc pct$1 ; mov [si.track_flags],track_free call cache_is_lru stc ret ;error exit ; pct$1: call cache_is_mru mov di,[bytes_in_trk] add [bp.user_off],di clc ret ; pr_cur_trk endp ; process_write_partial proc near ; ; is used to write a partial track ; ; inputs: dx: int13 dx with high bit of dl set off ; cx: track ; bx: start sector ; ax: number of words ; ; outputs:cy set if error ; clear if success ; ; strategy: ; if (track in cache) then { ; if (track in track_buffer) then ; invalidate track buffer; ; perform user write into cache; ; } ; else { ; read track into track buffer; ; write track buffer into cache; ; } ; ; call track_in_cache ;is track in cache jc read_diskw ;if not we have to read from disk ; cmp cx,[track_buffer_cyln] ;is it in the track buffer jnz not_in_memw ;no cmp dx,[track_buffer_hddr] ; jnz not_in_memw ;no ;yes ; mov [track_buffer_cyln],-1 ;invalidate trk buf mov [track_buffer_hddr],-1 ;to avoid two transfers ; ; update cache element from user buffer ; not_in_memw: push dx xchg ax,bx ;get number of words in bx and start sect in ax dec ax ;0 based number mov cl,9 ; shl ax,cl ;byte offset xor dx,dx ; ; mov cx,bx ;number of words mov es,[bp.user_es] ; mov di,[bp.user_off]; shl bx,1 add [bp.user_off],bx ; add ax,word ptr [si.base_offset] adc dx,word ptr [si.base_offset.2] mov bh,1 ;write push si push ds push bp call blkmov pop bp pop ds pop si pop dx jc err_cw call cache_is_mru clc ret ; ; cache error ; err_cw: mov [si.track_flags],track_free call cache_is_lru stc ret ; ; track not in cache. see if free cache element available ; read_diskw: cmp di,-1 ;free cache jnz rd_partw ;if present we can initiate read stc ret ; ; free cache element available. read track from disk into track buffer ; rd_partw: mov si,di ;get element in si mov [track_buffer_cyln],-1 mov [track_buffer_hddr],-1 mov [si.track_flags],track_free push dx push cx push bx push ax or cl,1 or dl,80h mov al,[sect_in_trk] mov ah,2 push cs pop es mov bx,[track_buffer_ptr] pushf cli call [old_13] jnc wr$1 cmp ah,11h ;the int13 error which is not an error je wr$1 mov [int13err],TRUE add sp,8 mov al,ah stc ret ; ; since we have already updated disk, the track read doesn't need ; to be updated from user buffer. transfer track from track buffer ; into cache ; wr$1: mov di,[track_buffer_ptr] ;es:di is transfer address mov al,[sect_in_trk] xor ah,ah mov cl,8 shl ax,cl mov cx,ax mov bh,1 ;write mov ax,word ptr [si.base_offset] mov dx,word ptr [si.base_offset.2] push ds push si push bp call blkmov pop bp pop si pop ds pop ax pop bx pop cx pop dx jnc wr$2 ret ; ; information successfully transferred to cache. fill in cache ; control element with the info on head, drive and track ; wr$2: mov word ptr [si.track_cyln],cx mov word ptr [si.track_drive],dx and [si.track_flags], not track_free mov word ptr [track_buffer_cyln],cx mov word ptr [track_buffer_hddr],dx ; call cache_is_mru ; shl ax,1 add [bp.user_off],ax clc ret ; process_write_partial endp ; ; process_block_write proc near ; ; al = number of tracks ; cx = start track ; dx = drive and start head ; bp = user stack frame ; ; cy set if error ; cy clear if success ; ; algorithm: for (cur_trk=st_trk; trks-- > 0; ) ; if (cur_trk in cache) then ; write cur_trk from user buffer into cache; ; else { ; get a free cache element; ; write cur_trk from user buffer into this cache elem ; } ; mov di,[bp.user_off] ; pbw$1: push di ;save it because next call destroys di call track_in_cache ;is the track in cache jnc pbw$2 ; cmp di,-1 ;no free cache je pbw$4 ;error exit ; mov si,di ;track was not in cache. now there will be one mov [si.track_flags],track_free mov word ptr [si.track_cyln],cx mov word ptr [si.track_drive],dx ; pbw$2: pop di ; ; check if it is in track buffer also in which case invalidate trk buffer ; cmp cx,[track_buffer_cyln] jne pbw$21 cmp dx,[track_buffer_hddr] jne pbw$21 ; ; invalidate track buffer to avoid two transfers ; mov [track_buffer_cyln],-1 mov [track_buffer_hddr],-1 ; ; update cache from user buffer pbw$21: push cx ; push ax push ds push si push bp push di push dx ; mov es,[bp.user_es] mov cx,[bytes_in_trk] shr cx,1 mov ax,word ptr [si.base_offset] mov dx,word ptr [si.base_offset.2] mov bh,1 call blkmov ; pop dx pop di pop bp pop si pop ds pop ax pop cx ; jnc pbw$22 jmp err_cw ; pbw$22: and [si.track_flags],not track_free ; inc dh ;go to next track call adj_hd_trk pbw$3: add di,[bytes_in_trk] call cache_is_mru ; dec al ; are we done jne pbw$1 ; ; success exit mov [bp.user_off],di clc ret ; ; error exit ; pbw$4: pop di stc ret ; process_block_write endp ; check_parameters proc near ; ; inputs: same as int13 registers except for ; BP - user_stack_frame ; BX - function (either read or write) ; DS - int13code segment ; ; outputs: carry set if params invalid ; carry clear if params okay ; ; if parameters okay : ; dl - drive with high bit off ; bx - sector number ; cl - cleared of the sector number ; ; ; check for number of drives ; and dl, not 80H ; turn off high bit of drive cmp dl,MAX_HARD_FILES ; more than allowed drives? jae bad_parmx ; error. ; ; check for number of sectors ; or al,al ; zero sectors ? je bad_parmx ; error. ; cmp al,80h ; more than 80h sectors ? ja bad_parmx ; error. ; ; check for wrap in user transfer address ; mov di,[bp.user_bx] ; es:di is transfer address xor ah,ah ; ax has number of sectors mov si,ax ; get it into si push cx ; mov cl,9 shl si,cl ; convert number of sectors into ; number of bytes dec si ; convert into zero based number pop cx ; add di,si ; add to transfer address offset jc bad_parmx ; if exceeds 64k offset then wrap ; ; get drive number into di to use as offset into sect / trk table ; mov di,dx ; drive number and di,0000000011111111B ; just get the relevant bits ; ; form sector number in bx and compare against allowed number of sectors ; in track on the drive indicated ; mov bx,cx ; and bx,0000000000111111B ; bl will then have sector number or bx,bx ; zero sector number je bad_parmx ; is bad cmp bl, [di.sectrkarray] ; is it more than number of sectors ; allowed ja bad_parmx ; ; ; check head parameter ; cmp dh,[di.hdarray] ; is it more than max useable val for drive ja bad_parmx ; ; ; do we need some code to check if read exceeds tracks in system ? ; should we also put a limit on the number of sectors that could ; be looked up in cache ? ; ; ; clear off sector number from cl to leave just trk number in cx ; and cl,11000000B ; clear off sector number ; ; store sectors in track for current drive and bytes in track for ; current drive in memory ; push bx push cx mov bl,[di.hdarray] mov [max_hd],bl ; maximum head number for cur. drive mov bl,[di.sectrkarray] ; number of sectors in trk mov [sect_in_trk],bl ; store this mov cl,9 shl bx,cl ; bytes in track mov [bytes_in_trk],bx ; store this pop cx pop bx clc ret ;return with no error ; bad_parmx: stc ret ; check_parameters endp ; process_regions proc near ; ; inputs: bx - start sector ; al - number of sectors ; cx - start track ; ; outputs: none ; ; action: initialise regions struct ; mov byte ptr [REGION.FLAG],0 ; clear regions flag cmp bx,1 ; if start sector is one je pr$2 ; might as well start with region2 ; ; process region1 ; or [REGION.FLAG],REG1_P ; mark region1 present mov ah,[sect_in_trk] ; get sectors in track sub ah,bl ; remaining number of sectors in track inc ah ; adjust 0 based numb to one based numb cmp ah,al jbe pr$1 ; if below or equal ah has number of sectors ; in region1 mov ah,al ; else all the sectors are in region1 pr$1: sub al,ah ; adjust number of sectors mov [REGION.REG1.START_H],dh mov [REGION.REG1.START_T],cx mov [REGION.REG1.START_S],bx push cx ; save these registers push ax ; mov al,ah ; xor ah,ah ; ax has number of sectors now mov cl,8 ; shl ax,cl ; multiply by 256 to get number of words mov [REGION.REG1.COUNT],ax ; store this count pop ax ; pop cx inc dh call adj_hd_trk ; ; process region2 ; pr$2: or al,al ; are we done je pr$end ; xor ah,ah ; ax has number of sectors div [sect_in_trk] ; find number of tracks or al,al ; al will have number of full tracks ; ah will have number of sectors left je pr$3 ; if no full tracks no region2 ; or [REGION.FLAG],REG2_P ; mark region2 present mov [REGION.REG2.START_H],dh mov [REGION.REG2.START_T],cx; store start track mov [REGION.REG2.TRACKS],al ; and number of tracks add dh,al ; adjust track number call adj_hd_trk ; ; process region3 ; pr$3: or ah,ah ; are we done (no sectors left) je pr$end ; if yes go to fin or [REGION.FLAG],REG3_P ; else mark region3 present mov [REGION.REG3.START_H],dh mov [REGION.REG3.START_T],cx ;store track number mov cl,8 mov al,ah xor ah,ah ; convert number of sectors into number of shl ax,cl ; words mov [REGION.REG3.COUNT],ax ; store this pr$end: ; ret ; process_regions endp ; ; Support Routines: ; ; track_in_cache proc near ; ; input: dl = drive number with bit 8 set off ; cx = cylinder number ; mov di,-1 ; di will return lru item nearest to matching ; element, -1 if none mov si,[cache_head] ; start with mru cache entry inc si ; to counter next instruction nexte: dec si ; to counter last instruction in the loop test [si.track_flags],track_locked ; is the track locked? jnz no_set ; mov di,si ; if not locked update di to this element no_set: test [si.track_flags],track_free ; is element free jnz skipe ; if free we need not check this one cmp dx,word ptr [si.track_drive] ; if not free check drive+head jnz skipe ; cmp cx,[si.track_cyln] ; and cylinder number jz tic$1 ; if found exit routine skipe: mov si,[si.fwd_lru_lnk] ; else go to check next cache element inc si ; if last element was end then si = -1 jnz nexte ; and incrementing it will set zero flag stc ret tic$1: clc ret ; track_in_cache endp ; get_cache proc near ; ; inputs: none ; outputs: si = lru cache element not locked ; = -1 if all elems locked ; mov si,[cache_tail] ;start with lru element inc si ;to counter next instruction gc$1: dec si ; to counter last instruction in loop test [si.track_flags],track_locked ; is the element locked jz gc$2 ; if not locked this is the lucky(?) guy ; mov si,[si.back_lru_lnk] ; else go back in chain to check the ; next recently used cache element inc si ; as before -1 is the end of chain jnz gc$1 ; incrementing it will set zero flag ; dec si ; no element found, si = -1 ; gc$2: ret ; get_cache endp ; adj_hd_trk proc near ; ; inputs: ch,cl track number ; dh changed head number to be checked and adjusted ; ; outputs: cx and dh updated ; pushf aht$1: cmp dh,[max_hd] ;is the head number > heads on drive jbe aht$2 sub dh,[max_hd] ;if so decrease head number by number of ;heads on drive dec dh ;by one more add ch,1 ;and step to next track jnc aht$1 add cl,40h jmp aht$1 aht$2: popf ret ; adj_hd_trk endp CACHE_HIT PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CACHE_HIT ENDP ;** INVALIDATE_CACHE -- Discard all cache info ; ; ENTRY ; Cache is flushed (If it is not, all dirty info will simply be chucked) ; EXIT ; All elements of cache are marked free ; USES ; BX,FLAGS ; INVALIDATE_CACHE PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV BX,[CACHE_HEAD] INC BX ; Counter next instruction NEXTC: DEC BX MOV [BX.TRACK_FLAGS],TRACK_FREE MOV BX,[BX.FWD_LRU_LNK] INC BX JNZ NEXTC ; ; Track buffer invalid too ; MOV [TRACK_BUFFER_CYLN],-1 MOV [TRACK_BUFFER_HDDR],-1 MOV [DIRTY_CACHE],0 ret INVALIDATE_CACHE ENDP ;** CACHE_IS_MRU -- Put cache element in LRU chain at MRU position ; ; ENTRY ; SI points cache element to place at MRU position ; EXIT ; SI is at MRU position (head) ; USES ; DI,FLAGS ; CACHE_IS_MRU PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING push di CMP SI,[CACHE_HEAD] JZ RET444 CALL UNLINK_CACHE MOV DI,SI XCHG DI,[CACHE_HEAD] MOV [DI.BACK_LRU_LNK],SI MOV [SI.FWD_LRU_LNK],DI MOV [SI.BACK_LRU_LNK],-1 RET444: pop di RET CACHE_IS_MRU ENDP ;** CACHE_IS_LRU -- Put cache element in LRU chain at LRU position ; ; ENTRY ; SI points to cache element to place at LRU position ; EXIT ; SI is at LRU position (tail) ; USES ; DI,FLAGS ; CACHE_IS_LRU PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING push di CMP SI,[CACHE_TAIL] JZ RET555 CALL UNLINK_CACHE MOV DI,SI XCHG DI,[CACHE_TAIL] MOV [DI.FWD_LRU_LNK],SI MOV [SI.BACK_LRU_LNK],DI MOV [SI.FWD_LRU_LNK],-1 RET555: pop di RET CACHE_IS_LRU ENDP ;** UNLINK_CACHE -- Unlink cache element from LRU chain ; ; ENTRY ; SI points to element to unlink ; EXIT ; SI is unlinked ; USES ; DI,FLAGS ; UNLINK_CACHE PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING PUSH BX MOV DI,[SI.BACK_LRU_LNK] ; Get prev guy INC DI ; Guy First? JZ NEW_HEAD ; Yes DEC DI MOV BX,[SI.FWD_LRU_LNK] ; Get next guy MOV [DI.FWD_LRU_LNK],BX ; Prev fwd is my fwd INC BX ; Is that guy last? JZ NEW_TAIL ; Yes DEC BX MOV [BX.BACK_LRU_LNK],DI ; Next back is my back NULL_CACHE: POP BX RET NEW_HEAD: MOV DI,[SI.FWD_LRU_LNK] ; Is head also tail? INC DI JZ NULL_CACHE ; Yes DEC DI MOV [CACHE_HEAD],DI ; New head MOV [DI.BACK_LRU_LNK],-1 ; New head has no back link POP BX RET NEW_TAIL: MOV [CACHE_TAIL],DI ; New tail POP BX RET UNLINK_CACHE ENDP RETRY_CNT DB ? ;** WRITE_FROM_CACHE -- Write out cache element to disk ; ; ENTRY ; SI -> cache element to write ; EXIT ; Carry Clear ; Written OK ; Track buffer is set to this track ; Carry Set ; Error, AL is error code ; Track buffer is set to empty ; USES ; AX,BX,CX,DX,ES,DI,FLAGS ; WRITE_FROM_CACHE PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING ; ; First transfer track down to track buffer because we can't write ; direct to extended or expanded mem with INT 13 ; PUSH CS POP ES MOV DI,[TRACK_BUFFER_PTR] ; ES:DI is transfer addr for BLKMOV MOV BX,WORD PTR [SI.TRACK_DRIVE] XOR BH,BH MOV AL,[BX.SECTRKARRAY] XOR AH,AH PUSH AX MOV CL,8 SHL AX,CL ; AX is words in track MOV CX,AX XOR BH,BH ; Read MOV AX,WORD PTR [SI.BASE_OFFSET] MOV DX,WORD PTR [SI.BASE_OFFSET.2] ; DX:AX is address in mem PUSH DS PUSH SI PUSH BP CALL BLKMOV ; Track buffer contents to track buffer POP BP POP SI POP DS JC ERR_FLP POP AX ; AL is sec/trk ; ; Now write it out to drive from the track buffer ; MOV CX,[SI.TRACK_CYLN] MOV DX,WORD PTR [SI.TRACK_DRIVE] MOV [TRACK_BUFFER_CYLN],CX ; Set track buffer currency MOV [TRACK_BUFFER_HDDR],DX OR CL,1 OR DL,80H MOV AH,3 PUSH CS POP ES MOV BX,[TRACK_BUFFER_PTR] PUSH AX MOV [RETRY_CNT],5 RETRY_WRITE: PUSHF CALL [OLD_13] ; Write it out JC ERR_RETRY NO_ERR1: POP AX ERR_FL: ret ERR_RETRY: CMP AH,11H ; The error that is not an error? JZ NO_ERR1 ; Yes, cmp cleared carry PUSH AX ; Save error in AH MOV AH,0 ; Reset INT 13H POP AX ; Get error back DEC [RETRY_CNT] JZ SET_ERR ; Return error POP AX ; Recover correct AX for INT 13 PUSH AX JMP RETRY_WRITE SET_ERR: MOV AL,AH ; INT 13 error to AL ERR_FLP: ADD SP,2 STC MOV [TRACK_BUFFER_CYLN],-1 ; Zap the track buffer MOV [TRACK_BUFFER_HDDR],-1 JMP ERR_FL WRITE_FROM_CACHE ENDP ;** FLUSH_CACHE -- Flush specific cache element if it's dirty ; ; ENTRY ; SI points to element to flush ; EXIT ; Carry Clear ; SI is flushed if it was dirty ; SI.TRACK_FLAGS dirty bit clear ; Track buffer set to this track ; Carry Set ; SI could not be flushed ; SI.TRACK_FLAGS = free ; AL is error code ; Track buffer set to empty ; USES ; AX,BX,CX,DX,ES,DI,FLAGS ; FLUSH_CACHE PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING TEST [SI.TRACK_FLAGS],TRACK_FREE ; Clears carry JNZ IGNORE_TRK TEST [SI.TRACK_FLAGS],TRACK_DIRTY ; Clears carry JZ IGNORE_TRK CALL WRITE_FROM_CACHE DEC [DIRTY_CACHE] ; Doesn't effect carry JC FLUSH_ERRX AND [SI.TRACK_FLAGS],NOT TRACK_DIRTY ; Clears carry IGNORE_TRK: ret FLUSH_ERRX: MOV [SI.TRACK_FLAGS],TRACK_FREE ; Track gone, unlocked RET FLUSH_CACHE ENDP ;** FLUSH_WHOLE_CACHE_SAV -- Flush all dirty cache elements saving regs ; ; ENTRY ; None ; EXIT ; Cache flushed ; USES ; FLAGS ; FLUSH_WHOLE_CACHE_SAV PROC NEAR ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING ; Simulate PUSHA PUSH AX PUSH CX PUSH DX PUSH BX PUSH BX ; dummy for push sp PUSH BP PUSH SI PUSH DI ; PUSH DS PUSH ES PUSH CS POP DS ASSUME DS:INT13CODE CALL FLUSH_WHOLE_CACHE POP ES POP DS ; Simulate POPA POP DI POP SI POP BP POP BX ; Dummy for pop sp POP BX POP DX POP CX POP AX ; ret FLUSH_WHOLE_CACHE_SAV ENDP ;** FLUSH_WHOLE_CACHE -- Flush all dirty cache elements ; ; ENTRY ; None ; EXIT ; Cache flushed ; USES ; AX,BX,CX,DX,ES,SI,DI,FLAGS ; FLUSH_WHOLE_CACHE PROC NEAR ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV SI,[CACHE_HEAD] INC SI ; Counter next instruction FLSH_LP: DEC SI CALL FLUSH_CACHE MOV SI,[SI.FWD_LRU_LNK] INC SI JNZ FLSH_LP FLUSH_DONE: MOV [DIRTY_CACHE],0 ; No dirty guys in cache ret FLUSH_WHOLE_CACHE ENDP BREAK ; ; The following label defines the start of the I/O code which is driver type ; specific. ; ; THE TYPE 2 driver must REPLACE this code with code appropriate ; to the driver type. ; EVEN ; Force start of drive code to word boundary DRIVE_CODE LABEL WORD EXTMEM_LOW EQU 0000H ; 24 bit addr of start of extended memory EXTMEM_HIGH EQU 0010H ;** BASE_ADDR data element ; ; The next value defines the 24 bit address of the start of the memory for ; the cache. It is equal to the EMM_BASE value in the ; EMM_REC structure for the cache. ; ; NOTE THAT IT IS INITIALIZED TO THE START OF EXTENDED MEMORY. This is ; because BLKMOV is used to read the EMM_CTRL sector during initialization ; of a TYPE 1 driver. ; ; NOTE: This data element is shared by TYPE 1, 2 drivers, but ; its meaning and correct initial value are driver type specific. ; ;; NOTE: The value at BASE_ADDR is patched during initialization when ;; loading a RAMDrive into upper extended memory on a PLUS ;; BASE_ADDR LABEL DWORD ; 24 bit address of start of this RAMDRV DW EXTMEM_LOW DW EXTMEM_HIGH ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;** BLKMOV - Perform transfer for TYPE 1 driver ; ; This routine is the transfer routine for moving bytes ; to and from the AT extended memory in real mode using ; the LOADALL instruction. The LOADALL instruction is used ; to set up a segment descriptor which has a 24 bit address. ; During the time the LOADALL 24 bit segment descriptor is ; in effect we must have interrupts disabled. If a real mode ; 8086 interrupt handler was given control it might perform ; a segment register operation which would destroy the special ; segment descriptor set up by LOADALL. This is prevented by ; doing a CLI during the "LOADALL time". ; ; WARNING NUMBER ONE: ; THIS CODE WILL NOT WORK ON ANY 80286 MACHINE WHERE THE NMI ; INTERRUPT IS ANYTHING BUT A FATAL, SYSTEM HALTING ERROR. ; ; Since it is bad to leave interrupts disabled for a long ; time, the I/O is performed 256 words at a time enabling ; interrupts between each 256 word piece. This keeps the time ; interrupts are disabled down to a reasonable figure in the 100mSec ; range. ; ; To use the LOADALL instruction 102 bytes at location 80:0 must ; be used. INT13 copies the contents of 80:0 into its own buffer, ; copies in the LOADALL info, performs the LOADALL, and then copies ; back the previous contents of 80:0. These operations are all ; performed during the time interrupts are disabled for each 256 word ; block. This must be done with interrupts disabled because this area ; on DOS 2.X and 3.X contains variable BIOS data. ; ; In order to gain full 24 bit addressing it is also required ; that address line 20 be enabled. This effects 8086 compatibility ; on 80286 systems. This code leaves address line 20 enabled ; for the ENTIRE duration of the I/O because it is too time ; expensive to disable/enable it for each 256 word block. ; ; WARNING NUMBER TWO: ; IF A MULTITASKING PRE-EMPTIVE SYSTEM SCHEDULES AND RUNS ; AN APPLICATION WHICH RELIES ON THE 1 MEG ADDRESS WRAP ; PROPERTY OF THE 8086 AND 8088 DURING THE TIME INT13 ; IS IN THE MIDDLE OF DOING AN I/O WITH ADDRESS LINE 20 ENABLED, ; THE APPLICATION WILL NOT RUN PROPERLY AND MAY DESTRUCT THE ; INT13 MEMORY. ; ; METHOD: ; Perform various LOADALL setup operations ; Enable address line 20 ; While there is I/O to perform ; Do "per 256 word block" LOADALL setup operations ; Set up copy of 80:0 to INT13 buffer ; CLI ; copy 80:0 to INT13 buffer ; copy LOADALL info to 80:0 ; LOADALL ; do 256 word transfer ; copy INT13 80:0 buffer back to 80:0 ; STI ; Disable address line 20 ; ; SEE ALSO ; INTEL special documentation of LOADALL instruction ; ; ENTRY: ; ES:DI is packet transfer address. ; CX is number of words to transfer. ; DX:AX is 32 bit start byte offset (0 = start of cache) ; BH is 1 for WRITE, 0 for READ ; ; BASE_ADDR set to point to start of cache memory ; This "input" is not the responsibility of the caller. It ; is up to the initialization code to set it up when the ; device is installed ; ; EXIT: ; Carry Clear ; OK, operation performed successfully ; Carry Set ; Error during operation, AL is error number (INT 13 error) ; ; USES: ; ALL ; ; This routine is specific to TYPE 1 driver ; ; sunilp - incorporated blkmov_386 (thanks to gregh) ; incorporated loadall_286 trick (thanks to scottra) ; added new a20 functionality ; ideally the code should be all relocatable abd the 386 ; blkmov should be relocated on the 286 blkmov for the ; 386 case. Also the A20 routines for the Olivetti or PS/2 ; should also ideally be relocated on top of the normal A20 BLKMOV: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING test [sys_flg],M_386 je blkmov_286 jmp blkmov_386 ; ; Compute 32 bit address of start of I/O ; blkmov_286: ADD AX,WORD PTR [BASE_ADDR] ADC DX,WORD PTR [BASE_ADDR + 2] ; ; Dispatch on function ; OR BH,BH JZ READ_IT ; ; Write ; MOV WORD PTR [ESDES.SEG_BASE],AX MOV BYTE PTR [ESDES.SEG_BASE + 2],DL ; MOV [LSI],DI mov [lbx],di ;sp MOV [LDI],0 MOV SI,OFFSET DSDES JMP SHORT SET_TRANS READ_IT: MOV WORD PTR [DSDES.SEG_BASE],AX MOV BYTE PTR [DSDES.SEG_BASE + 2],DL MOV [LDI],DI ; MOV [LSI],0 ;sp mov [lbx],0 MOV SI,OFFSET ESDES SET_TRANS: MOV AX,ES CALL SEG_SET ; Set ES or DS segreg ; ; Set stack descriptor ; MOV AX,SS MOV [LSSS],AX MOV SI,OFFSET SSDES CALL SEG_SET MOV [LSP],SP ; SUB [LSP],2 ; CX is on stack at LOADALL ; ; the loadall kludge ; mov ax,cs ;sp inc ax ;sp mov [lcss],ax ;sp mov si,offset CSDES ;sp mov ax,cs ;sp call seg_set ;sp ; ; Set Other LOADALL stuff ; SMSW [LDSW] SIDT FWORD PTR [IDTDES] SGDT FWORD PTR [GDTDES] ; ; NOW The damn SXXX instructions store the desriptors in a ; different order than LOADALL wants ; MOV SI,OFFSET IDTDES CALL FIX_DESCRIPTOR MOV SI,OFFSET GDTDES CALL FIX_DESCRIPTOR ; ; Enable address line 20 ; ;; ;; Enable address line 20 on the PC AT or activate A20-A23 on the 6300 PLUS. ;; The former can be done by placing 0dfh in AH and activating the keyboard ;; processor. On the PLUS, 90h goes in AL and the port at 03f20h is written. ;; So the combined value of 0df90h can be used for both machines with ;; appropriate coding of the called routine A20. ;; ;; MOV AH,0DFH mov ax,cs:[A20On] ;; set up for PLUS or AT CALL A20 Jc NR_ERR ; JMP SHORT IO_START ;sp jmp short move_main_loop ;sp NR_ERR: MOV AL,0AAH ; Drive not ready error STC RET ;IOLOOP: ;sp ; PUSH CX ;sp move_main_loop: ;sp assume ds:nothing ;sp jcxz io_done ;sp mov cs:[ldx],cx ;sp MOV AX,80H MOV DS,AX PUSH CS POP ES XOR SI,SI MOV DI,OFFSET cs:[SWAP_80] MOV CX,102/2 mov cs:[ssSave],ss CLD CLI ; Un interruptable REP MOVSW ; Save contents of 80:0 PUSH DS PUSH ES POP DS POP ES XOR DI,DI MOV SI,OFFSET cs:LOADALL_TBL MOV CX,102/2 REP MOVSW ; Transfer in LOADALL info DW 050FH ; LOADALL INSTRUCTION AFTER_LOADALL: ; set up stack for moving 80:0 information back again ; xor bp,bp mov ss,ax mov si,offset cs:[swap_80] mov cx,102/2 move_loop: lods word ptr cs:[si] mov ss:[bp],ax inc bp inc bp loop move_loop mov ss,cs:[ssSave] mov cx,dx mov si,bx ;critical code sti rep movsw cli ; bugfix sunilp mov ax,cs dec ax push ax mov ax,offset io_done push ax db 0cbh ; db 16 dup (0fah) ; bugfix sunilp mov ax,cs dec ax push ax mov ax,offset resume_int push ax db 0cbh ; resume_int: mov cs:[ldi],di mov cs:[lbx],si jmp move_main_loop ; REP MOVSW ; Move data ;IO_START: ; JCXZ IODN ; MOV WORD PTR [LCX],256 ; ASSUME full block ; SUB CX,256 ; JNC IOLOOP ; OK ; ADD [LCX],CX ; OOPs, partial block ; XOR CX,CX ; This is the last block ; JMP IOLOOP ;IODN: io_done: sti ; bugfix sunilp MOV CX,800H ; Retry this many times OFFLP: ;; ;; Reset of line A20 on the PC AT requires writing 0ddh to the keyboard ;; processor. On the PLUS, the appropriate value is 00. ;; ;; MOV AH,0DDH mov ax,cs:[A20Off] ;; setup for PLUS or AT. ah for IBM, al for PLUS CALL A20 ; Disable address line 20 jnc dis_done LOOP OFFLP dis_done: CLC RET ;** A20 - ENABLE/DISABLE ADDRESS LINE 20 ON IBM PC-AT ; ; This routine enables/disables address line 20 by twiddling bits ; in one of the keyboard controller registers. ; ; SEE ALSO ; IBM Technical Reference Personal Computer AT Manual #1502243 ; Page 5-155 ; ; ENTRY ; AH = 0DDH to disable A20 ; AH = 0DFH to enable A20 ; EXIT ; CY Failed ; NC Succeeded ; USES ; AL, FLAGS ; ; WARNING If this routine is called in a CLI state this routine has ; the side effect of enabling interrupts. ; ; This routine is specific to TYPE 1 driver ; A20: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING ;; CS override needed on S5_FLAG to avoid phase errors on ;; forward declaration of this variable. cmp cs:[S5_FLAG],S_OLIVETTI ;; test for 6300 PLUS jne test_vec ;; yes, do this code jmp a20s5 test_vec: cmp cs:[S5_FLAG],S_VECTRA jne test_ps2 jmp VecA20 test_ps2: test cs:[sys_flg],M_PS2 ; is it a ps2 machine jne a20ps2 ; if yes it has separate a20 routine old_a20: CLI call check_a20 ; check to see if it can be enb /disb jc a20suc ; no it may not be toggled CALL E_8042 JNZ a20err MOV AL,0D1H OUT 64H,AL CALL E_8042 JNZ a20err MOV AL,AH OUT 60H,AL CALL E_8042 JNZ a20err ; ; We must wait for the a20 line to settle down, which (on an AT) ; may not happen until up to 20 usec after the 8042 has accepted ; the command. We make use of the fact that the 8042 will not ; accept another command until it is finished with the last one. ; The 0FFh command does a NULL 'Pulse Output Port'. Total execution ; time is on the order of 30 usec, easily satisfying the IBM 8042 ; settling requirement. (Thanks, CW!) ; mov al,0FFh ;* Pulse Output Port (pulse no lines) out 64H,al ;* send cmd to 8042 CALL E_8042 ;* wait for 8042 to accept cmd jnz A20Err A20Suc: sti clc RET A20Err: sti stc ret ; ; Helper routine for A20. It waits for the keyboard controller to be "ready". ; E_8042: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH CX XOR CX,CX E_LOOP: IN AL,64H AND AL,2 LOOPNZ E_LOOP POP CX RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; A20 status checking. If request is to enable a20 we must check to ; see if it is already enabled. If so we just set the sys_flg to ; indicate this. On disabling the routine checks to see if disabling ; is allowed ; check_a20: assume ds:nothing,es:nothing,ss:nothing cmp ah,0ddh ; is it a disable operation jne check_a20_enable ; ; check if a20 disabling allowed ; test cs:[sys_flg],a20_st jne no_toggle toggle: clc ret ; ; a20 enabling, check if allowed ; check_a20_enable: and cs:[sys_flg], not A20_ST push cx push ds push si push es push di lds si,cs:low_mem les di,cs:high_mem mov cx,3 cld repe cmpsw pop di pop es pop si pop ds jcxz not_enabled pop cx or cs:[sys_flg],A20_ST no_toggle: stc ret not_enabled: pop cx clc ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; A20 routine for PS2s. The PS2 A20 hardware has shifted and toggling ; a bit in the system port is all that is required. A20PS2: assume ds:nothing,es:nothing,ss:nothing cli ; ; first separate disable operation from enable operation ; cmp ah,0ddh je disbl_PS2 ; ; enabling the a20 ; and cs:[sys_flg],not A20_ST in al,PS2_PORTA ; input a20 status test al,GATE_A20 ; is the a20 line set je set_it ; or cs:[sys_flg],A20_ST ; indicate that it was already set ps2a20suc: clc sti ret set_it: push cx xor cx,cx or al,GATE_A20 out PS2_PORTA,al ; set it see_agn: in al,PS2_PORTA ; read status again test al,GATE_A20 loopz see_agn pop cx jz ps2err clc sti ret ; ; disabling the ps2 ; disbl_PS2: test cs:[sys_flg],A20_ST jne ps2a20suc ; push cx xor cx,cx in al,PS2_PORTA and al,not GATE_A20 out PS2_PORTA,al see_agn1: in al,PS2_PORTA test al,GATE_A20 loopnz see_agn1 pop cx jnz ps2err clc sti ret ; ps2err: stc sti ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;* VECA20 - Address enable/disable routine for Vectra family computers ;; ;; This routine does the same function as A20 for Vectra machines. ;; Vectra machines require writing single byte as opposed to ;; double byte commands to the 8041. This is due to a bug ;; in older versions in the Vectra 8041 controllers. IBM ;; machines must use double byte commands due to lack of ;; implementation of single byte commands in some of their machines. ;; ;; Uses al, flags ;; Has same results as A20 ;; VecA20: CLI call check_a20 jc VecA20Suc call E_8042 jnz VecA20Err mov al,ah ;sigle byte command is code passed out 64H,al call E_8042 jnz VecA20Err ; See A20 for a description of the following code. It simply makes ; sure that the previous command has been completed. We cannot ; pulse the command reg since there is a bug in some Vectra 8041s ; instead we write the byte again knowing that when this one is ; accepted the previous one has been processed. mov al,ah out 64H,al call E_8042 jnz VecA20Err VecA20Suc: sti clc ret VecA20Err: sti stc ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;* A20S5 - Address enable/disable routine for the 6300 PLUS. ;; ;; This routine enables lines A20-A23 on the PLUS by writing ;; to port 03f20h. Bit 7 turns the lines on, and bit 4 sets ;; the power-up bit. To disable the lines, the processor ;; must be reset. This is done by saving the world and ;; jumping to the ROM 80286 reset code. Since the power-up bit ;; is set, the data segment is set to the BiosSeg at 40h ;; and a jump is then made to the address at RealLoc1. ;; At RealLoc1, one can find the CS:IP where the code ;; is to continue. ;; ;; Uses ax, flags. ;; Returns with zero flag set. ;; A20S5: test [reboot_flg],0ffh ;; sunilp jne a20s5boot ;; sunilp cli or al,al ;; if zero, then resetting processor jnz A20S5Next call RSet ;; must return with entry value of ax A20S5Next: push dx ;; set/reset port mov dx,3f20h out dx,al pop dx clc ;; sunilp modification cy flag now important STI RET ;;* a20S5BOOT - This code bypasses the processor reset on a reboot ;; of the 6300 PLUS. Otherwise the machine hangs. a20s5BOOT: ;; use this code before reboot cli jmp short a20s5next OldStackSeg dw 0 ;; used during PLUS processor reset ;; to save the stack segment ;;* Rset - Reset the 80286 in order to turn off the address lines ;; on the 6300 PLUS. Only way to do this on the ;; current hardware. The processor itself can be ;; reset by reading or writing prot 03f00h ;; ;; Uses flags. ;; RSet: pusha ;; save world push ds ;; save segments push es mov ax,BiosSeg ;; point to the bios segment mov ds,ax ;; ds -> 40h assume ds:BiosSeg push word ptr [RealLoc1] ;; save what might have been here push word ptr [RealLoc1+2] mov word ptr [RealLoc1],cs:[offset ReturnBack] ;; load our return address mov word ptr [RealLoc1+2],cs assume ds:nothing mov [OldStackSeg],ss ;; save the stack segment, too mov dx,03f00h ;; reset the processor in ax,dx nop nop nop cli hlt ;; should never get here ReturnBack: mov ss,[OldStackSeg] ;; start the recovery assume ds:BiosSeg pop word ptr [RealLoc1+2] pop word ptr [RealLoc1] pop es pop ds popa ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; blkmov_386: ;_protect: assume ds:int13code,es:nothing,ss:nothing ; ; Compute 32 bit address of start of I/O ; add ax,word ptr [base_addr] adc dx,word ptr [base_addr + 2] ; push cx ; ; Are we in virtual mode ; smsw cx test cx,01B ; is the pe bit set je pr_mode_tran jmp int_15_tran ; ; Dispatch on function ; pr_mode_tran: or bh,bh jz read_it_1 ; ; Write ; ; Update ES descriptor with address of track in cache ; mov si,offset es_des mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; ; Update DS descriptor with transfer address ; mov ax,es mov cx,16 mul cx mov si,offset ds_des mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; Switch SI and DI for write transfer mov si,di xor di,di jmp short set_trans_1 read_it_1: ; ; Update DS descriptor with address of track in cache ; mov si,offset ds_des mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; ; Update ES descriptor with transfer address ; mov ax,es mov cx,16 mul cx mov si,offset es_des mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; ; Keep SI and DI the same for read transfer ; xor si,si set_trans_1: ; ; Restore Transfer Count ; pop cx ; mov ax,cs:[A20On] call A20 jc nr_err_1 ; ; we shall do the transfer 1024 words at a time ; db 66h push ax mov bx,cx assume ds:nothing pr_io_agn_1: mov cx,1024 cmp bx,cx ja pr_strt_1 mov cx,bx pr_strt_1: sub bx,cx cli ; Un interruptable cld lgdt fword ptr emm_gdt ; ; Switch to protected mode ; db 66h,0Fh, 20h, 0 ;mov eax,cr0 or ax,1 db 66h,0Fh,22h, 0 ;mov cr0,eax ; ; Clear prefetch queue ; db 0eah ; far jump dw offset flush_prefetch dw cs_des - start_gdt ; flush_prefetch: assume cs:nothing ; ; Initialize segment registers ; mov ax,ds_des - start_gdt mov ds,ax assume ds:nothing mov ax,es_des - start_gdt mov es,ax assume es:nothing shr cx,1 ; convert word count to dword count db 0f3h,066h,0a5h ; rep movsd ; rep movsw ; Move data ; ; ; Return to Real Mode ; ; db 66h,0Fh, 20h, 0 ; mov eax,cr0 and ax,0FFFEh db 66h,0Fh, 22h, 0 ; mov cr0,eax ; ; Flush Prefetch Queue ; db 0EAh ; Far jump dw offset flushcs cod_seg dw ? ; Fixed up at initialization time assume cs:Int13Code flushcs: ; sti ; see if transfer done else go to do next block ; or bx,bx jne pr_io_agn_1 ; db 66h pop ax mov ax,cs mov es,ax assume es:nothing mov ds,ax assume ds:Int13Code mov cx,800h ; Retry this many times offlp_1: mov ax,cs:[A20Off] call A20 ; Disable address line 20 jnc offlp1_out loop offlp_1 offlp1_out: clc ret nr_err_1: mov al,0AAh ; Drive not ready error stc ret ; int_15_tran: assume ds:int13code,es:nothing,ss:nothing or bh,bh jz read_it_2 ; ; Write ; ; Update tgt descriptor with address of track in cache ; mov si,offset tgt mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; ; Update src descriptor with transfer address ; mov ax,es mov cx,16 mul cx add ax,di adc dx,0 mov si,offset src mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; jmp short set_trans_2 read_it_2: ; ; Update src descriptor with address of track in cache ; mov si,offset src mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; ; Update tgt descriptor with transfer address ; mov ax,es mov cx,16 mul cx add ax,di adc dx,0 mov si,offset tgt mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; set_trans_2: ; ; Restore Transfer Count ; pop bx ; ; we shall do the transfer 1024 words at a time ; pr_io_agn_2: mov cx,1024 cmp bx,cx ja pr_strt_2 mov cx,bx pr_strt_2: sub bx,cx push cs pop es mov si,offset int15_gdt mov ax,emm_blkm shl 8 int emm_int jc nr_err_1 ; ; ; see if transfer done else fo to do next block ; or bx,bx je io_exit ; add [src.bas_0_15],2048 adc [src.bas_16_23],0 adc [src.bas_24_31],0 ; add [tgt.bas_0_15],2048 adc [tgt.bas_16_23],0 adc [tgt.bas_24_31],0 ; jmp pr_io_agn_2 io_exit: clc ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;** SEG_SET - Set up a LOADALL segment descriptor as in REAL mode ; ; This routine sets the BASE value in the segment descriptor ; pointed to by DS:SI with the segment value in AX as the 80286 ; does in REAL mode. This routine is used to set a descriptor ; which DOES NOT have an extended 24 bit address. ; ; SEE ALSO ; INTEL special documentation of LOADALL instruction ; ; ENTRY: ; DS:SI -> Seg register descriptor ; AX is seg register value ; EXIT: ; NONE ; USES: ; AX ; ; This routine is specific to TYPE 1 driver ; SEG_SET: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH DX PUSH CX MOV CX,16 MUL CX ; Believe or not, this is faster than a 32 bit SHIFT MOV WORD PTR [SI.SEG_BASE],AX MOV BYTE PTR [SI.SEG_BASE + 2],DL POP CX POP DX RET ;** FIX_DESCRIPTOR - Shuffle GTD IDT descriptors ; ; The segment descriptors for the IDT and GDT are stored ; by the SIDT instruction in a slightly different format ; than the LOADALL instruction wants them. This routine ; performs the transformation by PUSHing the contents ; of the descriptor, and then POPing them in a different ; order. ; ; SEE ALSO ; INTEL special documentation of LOADALL instruction ; INTEL 80286 processor handbook description of SIDT instruction ; ; ENTRY: ; DS:SI points to IDT or GDT descriptor in SIDT form ; EXIT: ; DS:SI points to IDT or GDT descriptor in LOADALL form ; USES: ; 6 words of stack ; ; NOTE: The transformation is reversable, so this routine ; will also work to transform a descriptor in LOADALL ; format to one in SIDT format. ; ; Specific to TYPE 1 driver ; FIX_DESCRIPTOR: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH WORD PTR [SI + 4] PUSH WORD PTR [SI + 2] PUSH WORD PTR [SI] POP WORD PTR [SI + 4] POP WORD PTR [SI] POP WORD PTR [SI + 2] RET ;** DATA SPECIFIC TO THE LOADALL INSTRUCTION USAGE ; ; SWAP_80 and LOADALL_TBL are data elements specific to the use ; of the LOADALL instruction by TYPE 1 drivers. ; ; ; Swap buffer for contents of 80:0 ; EVEN ; Force word alignment of SWAP_80 and LOADALL_TBL SWAP_80 DB 102 DUP(?) ssSave dw ? ; ; LOADALL data buffer placed at 80:0 ; LOADALL_TBL LABEL BYTE DB 6 DUP(0) LDSW DW ? DB 14 DUP (0) TR DW 0 FLAGS DW 0 ; High 4 bits 0, Int off, Direction clear ; Trace clear. Rest don't care. LIP DW OFFSET AFTER_LOADALL LDT DW 0 LDSS DW 8000h LSSS DW ? LCSS DW ? LESS DW ? LDI DW ? LSI DW ? LBP DW ? LSP DW ? LBX DW ? LDX DW ? LCX DW ? LAX DW 80H ESDES SEGREG_DESCRIPTOR <> CSDES SEGREG_DESCRIPTOR <> SSDES SEGREG_DESCRIPTOR <> DSDES SEGREG_DESCRIPTOR <> GDTDES DTR_DESCRIPTOR <> LDTDES DTR_DESCRIPTOR <0D000H,0,0FFH,0088H> IDTDES DTR_DESCRIPTOR <> TSSDES DTR_DESCRIPTOR <0C000H,0,0FFH,0800H> ;** TRUE LOCATION OF ABOVE_PID ; ; Define the TRUE (runtime TYPE 2 driver) location of ABOVE_PID. ; This is the only piece of TYPE 2 specific data that we need ; in the resident image. We must define it HERE rather than down ; at ABOVE_BLKMOV so that we have its TRUE location after the ; TYPE 2 code is swapped in at initialization. If we defined ; it down at ABOVE_BLKMOV any instruction like: ; ; MOV DX,[ABOVE_PID] ; ; Would have to be "fixed up" when we moved the ABOVE_BLKMOV ; code into its final location. ; ABOVE_PID EQU WORD PTR $ - 2 ; TRUE location of ABOVE_PID ; ; The following label defines the end of the region where BLKMOV code ; may be swapped in. BLKMOV code to be swapped in MUST fit ; between DRIVE_CODE and DRIVE_END ; DRIVE_END LABEL WORD BREAK ; ; As discussed above in the documentation of the EMM_CTRL sector it ; is necessary to hear about system re-boots so that the EMM_ISDRIVER ; bits in the EMM_REC structure can be manipulated correctly. ; ; On the IBM PC family of machines there are two events which cause a ; "soft" system re-boot which we might expect the EMM_CTRL sector to ; survive through. One is software INT 19H, the other is the Ctrl-Alt-Del ; character sequence which can be detected by "listening" on INT 9 for ; it. The code below consists of a handler for INT 19H, a handler ; for INT 9, and a drive TYPE dependant piece of code. ; ; The drive TYPE dependant piece of code works as follows: ; ; TYPE 1 uses EMM_CTRL sector so it turnd off the ; EMM_ISDRIVER bit in the record indicated by MY_EMM_REC. ; EACH TYPE 1 driver in the system includes the INT 19/9 ; code. ; ; TYPE 2 DOES NOT use the EMM_CTRL sector but it still has ; a handler. What this handler does is issue an ; ABOVE_DEALLOC call to deallocate the Above Board ; memory allocated to INT13. In current versions ; of the EMM device driver this step is unnecessary ; as the EMM device driver is thrown away together ; with all of the allocation information when the system ; is re-booted. We do it anyway because some future version ; of the EMM device driver may be smarter and retain ; allocation information through a warm-boot. Currently, ; doing this doesn't hurt anything. Since this code cannot ; do a global ABOVE_DEALLOC for all TYPE 2 drivers in the ; system, it does an ABOVE_DEALLOC only for its memory ; and EACH TYPE 2 driver in the system includes the INT 19/9 ; code. ; ; ; Storage locations for the "next" INT 19 and INT 9 vectors, the ones ; that were in the interrupt table when the device driver was loaded. ; They are initialized to -1 to indicate they contain no useful information. ; OLD_19 LABEL DWORD DW -1 DW -1 OLD_9 LABEL DWORD DW -1 DW -1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; modification to meet new memory allocation standard OLD_15 LABEL DWORD DW -1 DW -1 int15_size dw 0 ; ; INT_15: ASSUME DS:NOTHING,SS:NOTHING,ES:NOTHING ; ; This piece of code determines the size of extended memory ; which was allocated before this driver and then subtracts ; the amount it has allocated for itself ; ; inputs: ah = 88h is of interest ; outputs: ax = size of extended memory allocated by all before and ; including us ; regs used: flags ; pushf cmp ah,88h je mem_det popf jmp [old_15] mem_det: mov ax,[int15_size] popf clc sti iret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;** INT 9 Keyboard handler ; ; All this piece of code does is look for the Ctrl-Alt-Del event. ; If key is not Ctrl-Alt-Del, it jumps to OLD_9 without doing ; anything. If the Ctrl-Alt-Del key is detected it calls ; RESET_SYSTEM to perform driver TYPE specific re-boot code. ; It then resets INT 13H to disable the cache and then jumps to ; OLD_9 to pass on the event. ; ; NOTE THAT UNLIKE INT 19 THIS HANDLER DOES NOT NEED TO RESET ; THE INT 9 AND INT 19 VECTORS. This is because the Ctrl-Alt-Del ; IBM ROM re-boot code resets these vectors. ; ; We would LIKE to ALSO flush the cache, but we can't. For one the ; keyboard is at a higher IRQ than the disk. We could EOI the keyboard, ; but this doesn't fix the second problem. INT 13s to write ; out any dirty tracks take a LONG time, so long that we lose ; the key. In other words we see Ctrl-Alt-Del, but none of the ; INT 9 handlers after us will. ; ; ; SEE ALSO ; INT 9 IBM ROM code in ROM BIOS listing of ; IBM PC Technical Reference manual for any PC family member ; ; ENTRY ; NONE ; EXIT ; NONE, via OLD_9 ; USES ; FLAGS ; ; THIS CODE IS USED BY TYPE 1,2 drivers. ; INT_9: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH AX PUSH DS IN AL,60H CMP AL,83 ; DEL key? JNZ CHAIN ; No XOR AX,AX MOV DS,AX MOV AL,BYTE PTR DS:[417H] ; Get KB flag NOT AL TEST AL,0CH ; Ctrl Alt? JNZ CHAIN ; No MOV [INT_13_BUSY],1 ; Exclude ; ; We would LIKE to do this, always but we can't. For one the keyboard ; is at a higher IRQ than the disk. We can EOI the keyboard, ; but this doesn't fix the second problem. INT 13s to write ; out any dirty tracks take a LONG time, so long that we loose ; the key. In other words we see Ctrl-Alt-Del, but none of the ; INT 9 handlers after us will. ; CMP [REBOOT_FLUSH],0 ; Reboot flush enabled? JZ NO_REBOOT_FLUSH ; No CMP [DIRTY_CACHE],0 ; Anything to do? JZ NO_REBOOT_FLUSH ; No MOV AL,20H OUT 20H,AL ; EOI the keyboard int CALL FLUSH_WHOLE_CACHE_SAV ; Flush cache NO_REBOOT_FLUSH: ; CALL RESET_SYSTEM ; Ctrl Alt DEL ; ; Reset INT 13 vector to turn cache off ; MOV AX,WORD PTR [OLD_13] CLI MOV WORD PTR DS:[13H * 4],AX MOV AX,WORD PTR [OLD_13 + 2] MOV WORD PTR DS:[(13H * 4) + 2],AX ; ; Reset INT 1C vector to turn cache off ; ; MOV AX,WORD PTR [OLD_1C] ; MOV WORD PTR DS:[1CH * 4],AX ; MOV AX,WORD PTR [OLD_1C + 2] ; MOV WORD PTR DS:[(1CH * 4) + 2],AX MOV [INT_13_BUSY],0 CHAIN: POP DS POP AX JMP [OLD_9] ;** INT 19 Software re-boot handler ; ; All this piece of code does is sit on INT 19 waiting for ; a re-boot to be signaled by being called. It calls ; FLUSH_WHOLE_CACHE_SAV to flush out any dirty cache info then ; RESET_SYSTEM to perform driver TYPE specific re-boot code, ; resets the INT 19, INT 13 and INT 9 vectors, ; and then jumps to OLD_19 to pass on the event. ; ; NOTE THAT UNLIKE INT 9 THIS HANDLER NEEDS TO RESET ; THE INT 9 AND INT 19 VECTORS. This is because the INT 19 ; IBM ROM re-boot code DOES NOT reset these vectors, and we ; don't want to leave them pointing to routines that are not ; protected from getting stomped on by the re-boot. ; ; SEE ALSO ; INT 19 IBM ROM code in ROM BIOS listing of ; IBM PC Technical Reference manual for any PC family member ; ; ENTRY ; NONE ; EXIT ; NONE, via OLD_19 ; USES ; FLAGS ; ; THIS CODE IS USED BY TYPE 1,2 drivers. ; INT_19: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING MOV [INT_13_BUSY],1 ; Exclude cmp [reboot_flush],0 ; je no_flush CALL FLUSH_WHOLE_CACHE_SAV ; Flush out the cache no_flush: CALL RESET_SYSTEM PUSH AX PUSH DS XOR AX,AX MOV DS,AX MOV AX,WORD PTR [OLD_13] CLI ; ; Reset INT 13 vector to trun cache off ; MOV WORD PTR DS:[13H * 4],AX MOV AX,WORD PTR [OLD_13 + 2] MOV WORD PTR DS:[(13H * 4) + 2],AX ; ; Reset INT 1C vector to turn cache off ; ; MOV AX,WORD PTR [OLD_1C] ; MOV WORD PTR DS:[1CH * 4],AX ; MOV AX,WORD PTR [OLD_1C + 2] ; MOV WORD PTR DS:[(1CH * 4) + 2],AX ; ; Since INT 19 DOES NOT reset any vectors (like INT 9 Ctrl Alt DEL does), ; we must replace those vectors we have mucked with. ; ; NOTE THAT WE RESET VECTORS DIRECTLY!!!!!!!!!!!!!!!!!! ; We are not sure that DOS is reliable enough to call. ; MOV AX,WORD PTR [OLD_19] MOV WORD PTR DS:[19H * 4],AX MOV AX,WORD PTR [OLD_19 + 2] MOV WORD PTR DS:[(19H * 4) + 2],AX ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; removed from smartdrv ; MOV AX,WORD PTR [OLD_9] ; MOV WORD PTR DS:[9H * 4],AX ; MOV AX,WORD PTR [OLD_9 + 2] ; MOV WORD PTR DS:[(9H * 4) + 2],AX ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov ax,word ptr [old_15] cmp ax,word ptr [old_15+2] jne res_15 cmp ax,-1 je skip_res res_15: mov word ptr ds:[15h*4],ax mov ax,word ptr [old_15+2] mov word ptr ds:[(15h*4) +2],ax ; skip_res: POP DS POP AX MOV [INT_13_BUSY],0 JMP [OLD_19] ;** RESET_SYSTEM perform TYPE 1 (/E) driver specific reboot code ; ; This code performs the EMM_ISDRIVER reset function as described ; in EMM.ASM for all EMM_REC structure for this device (offset ; stored in MY_EMM_REC). We use the same LOADALL ; method described at BLKMOV to address the EMM_CTRL sector ; at the start of extended memory and perform our changes in ; place. ; ; NOTE: RESET_SYSTEM ALSO defines the start of ANOTHER piece of ; driver TYPE specific code that TYPE 2 drivers ; will have to swap in a different piece of code for. ; ; ENTRY ; NONE ; EXIT ; NONE ; USES ; NONE ; ; This code is specific to TYPE 1 drivers ; RESET_SYSTEM: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING JMP SHORT TRUE_START MY_EMM_REC DW 0 ; Offset into 1K EMM_CTRL of my record TRUE_START: PUSHA mov cs:[reboot_flg],0ffh ; set the reboot flag cmp cs:[my_emm_rec],0 ; was an emm record allocated? je reset_skip PUSH DS PUSH ES ; ; reset base_addr to be address of emm ctrl sector ; mov ax,cs:[emm_ctrl_addr] mov dx,cs:[emm_ctrl_addr+2] mov word ptr cs:[base_addr],ax mov word ptr cs:[base_addr+2],dx ; ; read 1k emm control sector into track buffer, I want to keep memory ; access methods separate from driver code. We end up wasting a lot of ; time here but is reboot code anyway so thats okay ; mov bx,cs mov es,bx ; mov di,cs:[track_buffer_ptr] mov cx,512 xor ax,ax xor dx,dx mov bh,0 ; read push es push di call blkmov pop di pop es jc finish_reset ; ; fix the flags for my emm record so that it is no longer in use ; mov bx,cs:[my_emm_rec] add bx,di and es:[bx.emm_flags],not emm_isdriver ; ; write out the modified emm record out to memory ; xor ax,ax xor dx,dx mov bh,1 ; write call blkmov finish_reset: POP ES POP DS reset_skip: POPA RET ; ; The following label defines the end of the ; Driver TYPE specific RESET_SYSTEM code which will have to be replaced ; for different driver TYPEs as the code between RESET_SYSTEM and ; RESET_INCLUDE. Swapped in code MUST FIT between RESET_SYSTEM and ; RESET_INCLUDE. ; RESET_INCLUDE LABEL BYTE ; ; This data is only used at INIT, but it must be protected from overwrite ; by the DO_INIT code. ; TERM_ADDR LABEL DWORD ; Address to return as break address in INIT packet DW ? ; Computed at INIT time DW ? ; INT13 CS filled in at INIT ; ; THIS CODE MUST BE IN RESIDENT PORTION BECAUSE IT WRITES IN THE AREA ; OCCUPIED BY THE DISPOSABLE INIT CODE. ; ;** DO_INIT - Initialize cache structures to "empty" ; DO_INIT: ASSUME DS:INT13CODE MOV AX,[SECTRACK] MOV CL,9 SHL AX,CL ; AX is bytes per track buffer MOV BX,[CACHE_CONTROL_PTR] MOV CX,[TTRACKS] MOV [CACHE_HEAD],BX MOV [BX.BACK_LRU_LNK],-1 MOV WORD PTR [BX.BASE_OFFSET],0 MOV WORD PTR [BX.BASE_OFFSET+2],0 MOV [BX.TRACK_FLAGS],TRACK_FREE MOV DI,BX ADD BX,SIZE CACHE_CONTROL ; Next structure DEC CX ; One less to do JCXZ SETDONE ; one buffer in cache SETLOOP: MOV [DI.FWD_LRU_LNK],BX MOV [BX.BACK_LRU_LNK],DI MOV [BX.TRACK_FLAGS],TRACK_FREE MOV DX,WORD PTR [DI.BASE_OFFSET] ADD DX,AX MOV WORD PTR [BX.BASE_OFFSET],DX MOV DX,WORD PTR [DI.BASE_OFFSET+2] ADC DX,0 MOV WORD PTR [BX.BASE_OFFSET+2],DX MOV DI,BX ADD BX,SIZE CACHE_CONTROL ; Next structure LOOP SETLOOP SETDONE: MOV [DI.FWD_LRU_LNK],-1 MOV [CACHE_TAIL],DI ; That is the tail ; ; NOTE FALL THROUGH!!!!!!! ; ;** SETBPB - Set INIT packet I/O return values ; ; This entry is used to set the INIT packet Break address ; ; ENTRY ; TERM_ADDR set to device end ; EXIT ; through DEVEXIT ; USES ; DS, BX, CX ; ; COMMON TO TYPE 1, 2 drivers ; SETBPB: ASSUME DS:NOTHING ; ; 7. Set the return INIT I/O packet values ; LDS BX,[PTRSAV] MOV CX,WORD PTR [TERM_ADDR] MOV WORD PTR [BX.INIT_BREAK],CX ;SET BREAK ADDRESS MOV CX,WORD PTR [TERM_ADDR + 2] MOV WORD PTR [BX.INIT_BREAK + 2],CX JMP DEVEXIT EVEN ; Make sure we get word alignment of the track ; buffer. ; ; The following items define the "track buffer". When we want to I/O a track ; this is where we do it. We cannot I/O the track directly into extended ; memory because we have no way to specify a 24 bit address to INT 13. We ; Cannot I/O direct to expanded memory because DMA into the expanded memory ; window is not supported. This buffer also "holds" one track, so we keep ; track of the last track that was in here because it is faster to access ; it here than through extended/expanded memory. A value of -1 in the ; "currency" fields indicates that there is currently nothing interesting ; in the track buffer. NOTE: It is ASSUMED that the track buffer always ; represents a track that is IN the cache, therefore one must be sure to ; invalidate the track buffer when the cache element it represents is ; discarded. The offset of the track buffer is dynamic. Never talk about ; OFFSET TRACK_BUFFER. Always get the track buffer address out of ; TRACK_BUFFER_PTR. The initialization code "moves" the track buffer so ; that it does not cause a DMA Boundary error which would slow things ; down quite a bit. ; ; ; Cylinder and hd/drv of track in track buffer ; TRACK_BUFFER_CYLN DW -1 TRACK_BUFFER_HDDR DW -1 ; ; Pointer to track buffer. May be adjusted for DMA boundary error prevention. ; TRACK_BUFFER_PTR DW OFFSET TRACK_BUFFER ; ; Cache structure pointers ; ; ; Pointer to cache structures. This ends up being right after TRACK_BUFFER. ; CACHE_CONTROL_PTR DW ? ; ; Cache head and tail pointers ; CACHE_HEAD DW ? CACHE_TAIL DW ? ; ; This is the "in the 1Meg address space" track buffer. Its TRUE start ; may be adjusted for DMA boundary violation error prevention, and its ; Size may be adjusted depending on how many sectors per track there ; are. WARNING! This buffer must be AT LEAST 1024 bytes for /E driver ; init code (buffer for 1K EMM control sector). ; TRACK_BUFFER DB (512 * 2 + 16) DUP(0) ; we really don't need those 16 bytes, i ; am just paranoid ; ; The TERM_ADDR for the device will be set somewhere "around" here ; by the init code ; BREAK ;** DISPOSABLE INIT DATA ; ; INIT data which need not be part of resident image ; U_SWITCH db 0 ;; upper extended memory requested on 6300 PLUS EXT_K DW ? ; Size in K of Extended memory. NUM_ARG DB 1 ; Counter for numeric ; arguments bbbb. GOTSWITCH DB 0 ; Bit map of switches seen SWITCH_E EQU 00000001B SWITCH_A EQU 00000010B ; Only switch allowed SWITCH_T EQU 00000100B SWITCH_D EQU 00001000B SWITCH_WT EQU 00010000B SWITCH_WC EQU 00100000B SWITCH_R EQU 01000000B SWITCH_C EQU 10000000B ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; sys_det proc near ; ; author: sunilp, august 1, 1987. thanks to rickha for most of this ; routine. ; ; purpose: to determine whether extended memory cache can be installed ; on this system. also to determine and store in the system ; flag the machine identification. ; ; inputs: none ; ; outputs: CY set if this machine doesn't allow extended memory cache. ; CY clear if this machine allows extended memory cache and ; the system flag is set according to the machine type. ; ; registers used: ax,es,flags ;---------------------------------- ; Clear the state of the system flag ; assume ds:int13code,es:nothing,ss:nothing xor ax,ax ; 0000 into AX mov [sys_flg],al ; clear system flag ;---------------------------------- ; Determine if 8086/8088 system. If so we should abort immediately. ; push ax ; ax has 0 popf ; try to put that in the flags pushf pop ax ; look at what really went into flags and ax,0F000h ; mask off high flag bits cmp ax,0F000h ; Q: was high nibble all ones ? je cpu_err ; Y: it's an 8086 (or 8088) ;---------------------------------- ; Determine if 80286/80386 machine. ; mov ax,0F000h ; N: try to set the high bits push ax popf ; ... in the flags pushf pop ax ; look at actual flags and ax,0F000h ; Q: any high bits set ? je cpu_286 ; N: it's an 80286 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; It is a 386 cpu. We should next try to determine if the ROM is ; B0 or earlier. We don't want these guys. ; cpu_386: pushf ; clear pop ax ; NT and ax,not 0F000h ; and push ax ; IOPL popf ; bits ;---------------------------------- ; the next three instructions were removed because we are loaded ; in real mode. So there is no need to check for virtual mode. ; ; smsw ax ;check for Virtual Mode ; test ax,0001 ; Q: Currently in Virtual Mode ? ; jnz cpu_exit ; Y: quit with error message ;---------------------------------- ; N: check 386 stepping for B0 call is_b0 ; Q: B0 ? jc cpu_err ; Y: abort ;---------------------------------- ; We have a valid 386 guy. Set the flag to indicate this. ; or [sys_flg],M_386 ; set 386 bit jmp short PS2Test ;---------------------------------- ; This is a 286 guy. Check for AT model byte. We don't want non-ATs. ; Set 286 bit if AT type. Then check for PS/2 ; cpu_286: mov ax,0ffffh mov es,ax cmp byte ptr es:[0eh],0fch ; AT model byte jne cpu_err ; if not abort ; or [sys_flg],M_286 ; set 286 flag bit ; ; ; Determine if this is a PS/2 system ; PS2Test: call IsPS2Machine or ax,ax jz NCRTest or [sys_flg],M_PS2 NCRTest: call IsNCRMachine or ax,ax jz cpu_suc ; We're on an NCR machine, send D7 and D5 to the 8042 in order ; to toggle A20 instead of the DF and DD we usually send. ; ChipA 06-16-88 mov cs:[A20On],0D790h mov cs:[A20Off],0D500h ;---------------------------------- ; success exit:-- cpu_suc: clc ret ;---------------------------------- ; error exit:-- cpu_err: stc ret ; sys_det endp ;*--------------------------------------------------------------------------* ;* * ;* IsPS2Machine HARDWARE DEP. * ;* * ;* Check for PS/2 machine * ;* * ;* ARGS: None * ;* RETS: AX = 1 if we're on a valid PS/2 machine, 0 otherwise * ;* REGS: AX and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* IsPS2Machine proc near mov ah,0C0h ; Get System Description Vector stc int 15h jc short IPMNoPS2 ; Error? Not a PS/2. ; Are we on a PS/2 Model 35? cmp es:[bx+2],09FCh je short IPMFoundIt ; Yup, use the PS/2 method ; Do we have a "Micro Channel" computer? mov al,byte ptr es:[bx+5] ; Get "Feature Information Byte 1" test al,00000010b ; Test the "Micro Channel Implemented" bit jz short IPMNoPS2 IPMFoundIt: mov ax,1 ret IPMNoPS2: xor ax,ax ret IsPS2Machine endp ;*--------------------------------------------------------------------------* ;* * ;* IsNCRMachine HARDWARE DEP. * ;* * ;* Check for NCR machine * ;* * ;* ARGS: None * ;* RETS: AX = 1 if we're on a valid NCR machine, 0 otherwise * ;* REGS: AX and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* ; Look for 'NC' at F000:FFEA IsNCRMachine proc near mov ax,0F000h mov es,ax mov ax,word ptr es:[0FFEAh] cmp ax,'CN' je INMFoundIt xor ax,ax ret INMFoundIt: mov ax,1 ret IsNCRMachine endp ;****************************************************************************** ; IS_B0 - check for 386-B0 ; ; This routine takes advantage of the fact that the bit INSERT and ; EXTRACT instructions that existed in B0 and earlier versions of the ; 386 were removed in the B1 stepping. When executed on the B1, INSERT ; and EXTRACT cause an INT 6 (invalid opcode) exception. This routine ; can therefore discriminate between B1/later 386s and B0/earlier 386s. ; It is intended to be used in sequence with other checks to determine ; processor stepping by exercising specific bugs found in specific ; steppings of the 386. ; ; ENTRY: REAL MODE on 386 processor (CPU ID already performed) ; EXIT: CF = 0 if B1 or later ; CF = 1 if B0 or prior ; ; ENTRY: ; EXIT: ; USED: AX, flags ; STACK: ;------------------------------------------------------------------------------ is_b0 proc near push bx push cx push dx push ds xor bx,bx mov ds,bx ; DS = 0000 (real mode IDT) assume ds:R_Mode_IDT push [bx+(6*4)] pop cs:[int6_save] ; save old INT 6 offset push [bx+(6*4)+2] pop cs:[int6_save+2] ; save old INT 6 segment mov word ptr [bx+(6*4)],offset int6 mov [bx+(6*4)+2],cs ; set vector to new INT 6 handler ; ; Attempt execution of Extract Bit String instruction. Execution on ; B0 or earlier with length (CL) = 0 will return 0 into the destination ; (CX in this case). Execution on B1 or later will fail and dummy INT 6 ; handler will return execution to the instruction following the XBTS. ; CX will remain unchanged in this case. ; xor ax,ax mov dx,ax mov cx,0FF00h ; Extract length (CL)=0, CX=non-zero db 0Fh,0A6h,0CAh ; XBTS CX,DX,AX,CL xor bx,bx mov ds,bx ; DS = 0000 (real mode IDT) push cs:[int6_save] ; restore original INT 6 offset pop [bx+(6*4)] ; push cs:[int6_save+2] ; restore original INT 6 segment pop [bx+(6*4)+2] or cx,cx ; Q: CX = 0 (meaning <=B0) ? jz ib_exit ; Y: exit (carry clear) stc ; N: set carry to indicate >=B1 ib_exit: cmc ; flip carry tense pop ds pop dx pop cx pop bx ret ; *** RETURN *** is_b0 endp ; ; Temporary INT 6 handler - assumes the cause of the exception was the ; attempted execution of an XTBS instruction. ; int6 proc near push bp mov bp,sp add word ptr [bp+2],3 ; bump IP past faulting instruction pop bp iret ; *** RETURN *** int6_save dw 0000,0000 int6 endp ;*************************************************************************** ;** PRINT - Print a "$" terminated message on stdout ; ; This routine prints "$" terminated messages on stdout. ; It may be called with only the DX part of the DS:DX message ; pointer set, the routine puts the correct value in DS to point ; at the INT13 messages. ; ; ENTRY: ; DX pointer to "$" terminated message (INT13CODE relative) ; EXIT: ; NONE ; USES: ; AX ; ; COMMON TO TYPE 1, 2, 3, 4 drivers ; PRINT: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH DS PUSH CS POP DS MOV AH,Std_Con_String_Output INT 21H POP DS RET ;** ITOA - Print Decimal Integer on stdout ; ; Print an unsigned 16 bit value as a decimal integer on stdout ; with leading zero supression. Prints from 1 to 5 digits. Value ; 0 prints as "0". ; ; Routine uses divide instruction and a recursive call. Maximum ; recursion is four (five digit number) plus one word on stack ; for each level. ; ; ENTRY AX has binary value to be printed ; EXIT NONE ; USES AX,CX,DX,FLAGS ; ; COMMON TO TYPE 1, 2, 3, 4 drivers ; ITOA: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING MOV CX,10 XOR DX,DX DIV CX ; DX is low digit, AX is higher digits OR AX,AX JZ PRINT_THIS_DIGIT ; No more higher digits PUSH DX ; Save this digit CALL ITOA ; Print higher digits first POP DX ; Recover this digit PRINT_THIS_DIGIT: ADD DL,"0" ; Convert to ASCII MOV AH,Std_CON_Output INT 21H RET ;** INT13$INIT - Device Driver Initialization routine ; ; INT13 Initialization routine. This is the COMMON initialization ; code used by ALL driver TYPEs. Its jobs are to: ; ; 1. Initialize various global values ; 2. Check for correct DOS version and do changes to the device ; based on the DOS version if needed. ; 3. Set OLD_13, OLD_1C and Parse the command line and set values accordingly ; 4. Set up the cache parameters and ; Call a TYPE specific INIT routine based on the Parse ; to set up a specific driver TYPE. ; 5. Print out report of INT13 parameters ; 6. Set the return INIT I/O packet values ; ; The first two lines perform step 1. Step two starts after and ; goes through VER_OK. Step 3 starts at VER_OK and goes through ; ARGS_DONE. Step 4 starts at ARGS_DONE and goes through I001. ; Step 5 starts at I001 and goes through DRIVE_SET. Step 6 starts ; at DRIVE_SET and goes through SETBPB. Step 7 starts at SETBPB ; and ends at the JMP DEVEXIT 10 lines later. ; ; At any time during the above steps an error may be detected. When ; this happens one of the error messages is printed and INT13 ; de-installs itself. It does this at DEVABORT_NOMES by changing ; the Device attributes to a BLOCK DEVICE and setting its size to NULL. ; All INT13 needs to do is make sure any INT vectors it changed ; (INT 9 and INT 19 and INT 13) get restored to what they were ; when INT13 first started. If an EMM_CTRL sector is being ; used (TYPE 1) and one of the EMM_REC structures has been ; marked EMM_ISDRIVER by this driver, it must turn that bit back off ; since the driver did not install. A TYPE 2 driver must make sure it ; ABOVE_DEALLOCs any memory it allocated from the EMM device. The duty ; of reclaiming EMM_CTRL or Above Board memory and re-setting vectors ; is done by the DISK_ABORT routine which may be called by either ; this COMMON INIT code, or the TYPE specific INIT code. ; ; Step 1 initializes the segment part of TERM_ADDR to the correct ; value for type 1, 2 drivers. ; ; Step 2 checks to make sure that we are running on a DOS in the ; 2.X or 3.X series which this driver is restricted to. If running ; on a 2.X series the device header attribute word and device command ; table are patched to exclude those device calls that don't exist ; on DOS 2.X. ; ; Step 3 uses the "DEVICE = xxxxxxxxx" line pointer provided by ; DOS to look for the various device parameters. NOTE: This pointer ; IS NOT DOCUMENTED in the DOS 2.X tech ref material, but it does ; exist in the same way as 3.X. This code is simple even though ; it looks rather long. First it skips over the device name field ; to get to the arguments. In then parses the arguments as they are ; encountered. All parameter errors are detected here. NOTE THAT ; THIS ROUTINE IS NOT RESPONSIBLE FOR SETTING DEFAULT VALUES OF ; PARAMETER VARIABLES. This is accomplished by static initialization ; of the parameter variables. ; ; Step 4 calls a device TYPE specific initialization routine based ; on the parse in step 3 (presence or absense of /E and /A switches). ; NOTE that one of the prime jobs of these device TYPE specific ; routines is to set all of the variables that are needed by Step ; 5 and 6 that haven't been set by the COMMON init code: ; ; DEV_SIZE set to TRUE size of device ; BASE_ADDR set to TRUE start of device so BLKMOV ; can be called ; BASE_RESET set so DISK_ABORT can be called ; TERM_ADDR set to correct end of device ; ; Step 5 makes the status report display of DEVICE SIZE and other info. ; ; Step 6 sets the INIT I/O packet return values for Break address. ; ; ; SEE ALSO ; MS-DOS Technical Reference manual section on ; Installable Device Drivers ; ; ENTRY from INT13$IN ; EXIT Through DEVEXIT ; USES ALL ; ; COMMON TO TYPE 1, 2 drivers ; INT13$INIT: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING ; ; 1. Initialize various global values ; MOV WORD PTR [TERM_ADDR + 2],CS ; ; 2. Check for correct DOS version and do changes to the device ; based on the DOS version if needed. ; CLD MOV AH,GET_VERSION INT 21H XCHG AH,AL CMP AX,(2 SHL 8) + 00 JB BADVER ; Below 2.00, BAD CMP AX,(3 SHL 8) + 00 JB VER2X ; 2.X requires some patches CMP AX,(4 SHL 8) + 00 JBE VER_OK ; 3.X or 4.0 OK BADVER: MOV DX,OFFSET BADVERMES JMP DEVABORT VER2X: AND [DEVATS],NOT DEVOPCL ; No such bit in 2.X MOV BYTE PTR [INT13TBL],11 ; Fewer functions too VER_OK: ;; ;; 2.5 Check here for 6300 PLUS machine. First look for Olivetti copyright, ;; and if found, check id byte at f000:fffd. ;; push es ;; Olivetti Machine? mov ax,0fc00h ;; Look for 'OL' at fc00:50 mov es,ax cmp es:[0050h],'LO' jnz notS5 ;; not found mov ax,0f000h mov es,ax cmp word ptr es:[0fffdh],0fc00h ;; look for 6300 plus jnz notS5 mov [S5_FLAG],S_OLIVETTI ;; yep, set flag notS5: ;; Check here for an HP Vectra machine. Look for HP id byte. ;; mov ax,0f000H mov es,ax cmp es:[0f8H],'PH' jnz notHP mov [S5_FLAG],S_VECTRA notHP: pop es ; ; 3. Set OLD_13, OLD_1C and Parse the command line and set values accordingly ; MOV AX,(Get_Interrupt_Vector SHL 8) OR 13H INT 21H MOV WORD PTR [OLD_13],BX MOV WORD PTR [OLD_13 + 2],ES MOV AX,(Get_Interrupt_Vector SHL 8) OR 1CH INT 21H MOV WORD PTR [OLD_1C],BX MOV WORD PTR [OLD_1C + 2],ES LDS SI,[PTRSAV] ASSUME DS:NOTHING MOV DX,OFFSET HEADERMES CALL PRINT LDS SI,[SI.INIT_BPB] ; DS:SI points to config.sys SKIPLP1: ; Skip leading delims to start of name LODSB CMP AL," " JZ SKIPLP1 CMP AL,9 JZ SKIPLP1 CMP AL,"," JZ SKIPLP1 JMP SHORT SKIPNM ARGS_DONEJ: JMP ARGS_DONE SWITCHJ: JMP SWITCH SKIPLP2: ; Skip over device name LODSB SKIPNM: CMP AL,13 JZ ARGS_DONEJ CMP AL,10 JZ ARGS_DONEJ CMP AL," " JZ FIRST_ARG CMP AL,9 JZ FIRST_ARG CMP AL,"," JZ FIRST_ARG CMP AL,0 ; Need this for 2.0 2.1 JNZ SKIPLP2 SCAN_LOOP: ; PROCESS arguments LODSB FIRST_ARG: OR AL,AL ; Need this for 2.0 2.1 JZ ARGS_DONEJ CMP AL,13 JZ ARGS_DONEJ CMP AL,10 JZ ARGS_DONEJ CMP AL," " JZ SCAN_LOOP CMP AL,9 JZ SCAN_LOOP CMP AL,"," JZ SCAN_LOOP CMP AL,"/" JZ SWITCHJ CMP AL,"0" JB BAD_PARMJ CMP AL,"9" JA BAD_PARMJ DEC SI CALL GETNUM CMP [NUM_ARG],1 JA BAD_PARMJ ; Only 1 numeric argument SET_SIZE: CMP BX,128 JB BAD_PARMJ CMP BX,8192 JA BAD_PARMJ MOV [DEV_SIZE],BX ;A mov [current_dev_size],bx ;A JMP SHORT NUM_DONE BAD_PARMJ: JMP SHORT BAD_PARM NUM_DONE: INC [NUM_ARG] ; Next numeric argument SCAN_LOOPJ: JMP SCAN_LOOP BAD_PARM: MOV DX,OFFSET ERRMSG1 DEVABORT: CALL PRINT DEVABORT_NOMES: ; INC [NULDEV] ;Indicate NUL device ; MOV WORD PTR [TERM_ADDR],OFFSET ERROR_END ;Minimul null device ; JMP SETBPB ;and return LDS BX,[PTRSAV] MOV WORD PTR [BX].INIT_NUM,0 MOV WORD PTR [BX].INIT_BREAK[0],0 MOV WORD PTR [BX].INIT_BREAK[2],CS MOV [DEVATS],0 JMP DEVEXIT SWITCH: LODSB OR AL,20H if WINDOWS_SWITCHES eq 0 CMP AL,"e" JNZ ABOVE_TEST EXT_SET: TEST [GOTSWITCH],SWITCH_E + SWITCH_A JNZ BAD_PARM OR [GOTSWITCH],SWITCH_E MOV [DRIVER_SEL],0 JMP SCAN_LOOP ABOVE_TEST: endif ; WINDOWS_SWITCHES eq 0 ;; Added for /u switch cmp al,'u' ;; Look for U switch for PLUS jnz A_TEST cmp [S5_FLAG],S_OLIVETTI ;; No good unless PLUS jne bad_parm TEST [GOTSWITCH],SWITCH_A ;; Already have switch A ? JNZ BAD_PARM cmp [U_SWITCH],0 jne bad_parm dec [U_SWITCH] jmp scan_loop A_TEST: ;; CMP AL,"a" if WINDOWS_SWITCHES jnz bad_parm else JNZ DIS_TEST endif ;WINDOWS_SWITCHES ABOVE_SET: TEST [GOTSWITCH],SWITCH_A ; Was SWITCH_E + SWITCH_A JNZ BAD_PARM ;; added for /u switch cmp [U_SWITCH],0 jne bad_parm ;; OR [GOTSWITCH],SWITCH_A MOV [DRIVER_SEL],1 JMP SCAN_LOOP if WINDOWS_SWITCHES eq 0 DIS_TEST: CMP AL,"d" JNZ W_TEST DIS_SET: TEST [GOTSWITCH],SWITCH_D JNZ BAD_PARM OR [GOTSWITCH],SWITCH_D MOV [ENABLE_13],0 JMP SCAN_LOOP W_TEST: CMP AL,"w" JNZ T_TEST LODSW OR AL,20H CMP AX,":c" JNZ WT_TEST LODSW OR AX,2020H CMP AX,"fo" JNZ BAD_PARM LODSB OR AL,20H CMP AL,"f" JNZ BAD_PARMJX WC_SET: TEST [GOTSWITCH],SWITCH_WC JNZ BAD_PARMJX OR [GOTSWITCH],SWITCH_WC MOV [WRITE_BUFF],0 JMP SCAN_LOOP WT_TEST: CMP AX,":t" JNZ BAD_PARMJX LODSW OR AX,2020H CMP AX,"no" JNZ BAD_PARMJX WT_SET: TEST [GOTSWITCH],SWITCH_WT JNZ BAD_PARMJX OR [GOTSWITCH],SWITCH_WT MOV [WRITE_THROUGH],1 JMP SCAN_LOOP BAD_PARMJX: JMP BAD_PARM T_TEST: CMP AL,"t" JNZ R_TEST LODSW CMP AL,":" JNZ BAD_PARMJX CMP AH,"0" JB BAD_PARMJX CMP AH,"9" JA BAD_PARMJX DEC SI CALL GETNUM T_SET: TEST [GOTSWITCH],SWITCH_T JNZ BAD_PARMJX OR [GOTSWITCH],SWITCH_T MOV [TICK_SETTING],BX JMP SCAN_LOOP R_TEST: CMP AL,"r" JNZ C_TEST LODSW OR AH,20H CMP AX,"o:" JNZ BAD_PARMJX LODSB OR AL,20H CMP AL,"n" JNZ BAD_PARMJX TEST [GOTSWITCH],SWITCH_R JNZ BAD_PARMJX OR [GOTSWITCH],SWITCH_R MOV [REBOOT_FLUSH],1 JMP SCAN_LOOP C_TEST: CMP AL,"c" JNZ BAD_PARMJX LODSW OR AH,20H CMP AX,"o:" JNZ BAD_PARMJX LODSB OR AL,20H CMP AL,"n" JNZ BAD_PARMJX TEST [GOTSWITCH],SWITCH_C JNZ BAD_PARMJX OR [GOTSWITCH],SWITCH_C MOV [ALL_CACHE],1 JMP SCAN_LOOP endif ;WINDOWS_SWITCHES eq 0 ARGS_DONE: ; ; 4. Call a TYPE specific INIT routine based on the Parse ; to set up a specific driver TYPE. ; PUSH CS POP DS ASSUME DS:INT13CODE MOV AL,[DRIVER_SEL] ; Find out which init to call OR AL,AL JNZ NEXTV CALL AT_EXT_INIT JMP SHORT INI_RET NEXTV: CALL ABOVE_INIT INI_RET: JNC I001 JMP DEVABORT_NOMES I001: DRIVE_SET: ; ; update the current device size ; mov ax,[dev_size] mov [current_dev_size],ax ; ; 6. Print out report of INT13 parameters ; MOV DX,OFFSET STATMES1 CALL PRINT MOV AX,[DEV_SIZE] CALL ITOA MOV DX,OFFSET STATMES1E CMP [DRIVER_SEL],0 JZ PTYPX MOV DX,OFFSET STATMES1A PTYPX: CALL PRINT MOV DX,OFFSET STATMES2 CALL PRINT MOV AX,[TTRACKS] CALL ITOA MOV DX,OFFSET STATMES3 CALL PRINT MOV AX,[SECTRACK] CALL ITOA MOV DX,OFFSET STATMES4 CALL PRINT ifdef OMTI mov dx,offset omti_msg call print endif IF DEBUG MOV DX,OFFSET STATMES5 CALL PRINT MOV AX,CS CALL ITOA MOV DX,OFFSET STATMES6 CALL PRINT ENDIF ; ; Turn on the cache by chaining INT 13, and INT 1C ; MOV DX,OFFSET INT_13_HANDLER MOV AX,(Set_Interrupt_Vector SHL 8) OR 13H INT 21H ; MOV DX,OFFSET INT_1C_HANDLER ; MOV AX,(Set_Interrupt_Vector SHL 8) OR 1CH ; INT 21H JMP DO_INIT ;** DRIVEPARMS Initialize drive related cache parameters ; ; ENTRY ; Stuff set so that BLKMOV can be used to access cache memory ; DEV_SIZE set to TRUE cache size in K ; EXIT ; Carry Set ; Error, message already printed ; Carry clear ; TRACK_BUFFER_PTR adjusted for DMA error prevention ; CACHE_CONTROL_PTR set ; TERM_ADDR set ; TTRACKS set ; SECTRACK set ; SECTRKARRAY set ; HDARRAY set (SUNILP) ; USES ; ALL but DS ; ; COMMON TO TYPE 1, 2 drivers ; ; NO_HARDFILES: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING MOV DX,OFFSET NOHARD PEER: CALL PRINT STC RET TRACK_TOO_BIG: MOV DX,OFFSET BIGTRACK JMP PEER DRIVEPARMS: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING ; ; First figure out sec/track of any hardfiles ; MOV DL,80H MOV AH,8 INT 13H JC NO_HARDFILES OR DL,DL JZ NO_HARDFILES AND CL,00111111B MOV [SECTRKARRAY],CL mov [hdarray],dh XOR CH,CH MOV [SECTRACK],CX MOV CL,DL DEC CX JCXZ FINISHED CMP CX,MAX_HARD_FILES - 1 JBE DNUM_OK MOV CX,MAX_HARD_FILES - 1 DNUM_OK: MOV DL,81H PARMLOOP: PUSH CX PUSH DX MOV AH,8 INT 13H JC IGNORE_DRIVE AND CL,00111111B POP BX PUSH BX AND BX,0000000001111111B MOV [BX.SECTRKARRAY],CL mov [bx.hdarray],dh XOR CH,CH CMP CX,[SECTRACK] JBE IGNORE_DRIVE MOV [SECTRACK],CX IGNORE_DRIVE: POP DX INC DL POP CX LOOP PARMLOOP FINISHED: ; ; Figure out number of full tracks that fit in cache ; MOV AX,[SECTRACK] MOV CX,512 MUL CX ; DX:AX = Bytes per track OR DX,DX JNZ TRACK_TOO_BIG MOV BX,AX ; BX is bytes per track MOV AX,[DEV_SIZE] MOV CX,1024 MUL CX ; DX:AX = size of cache in bytes DIV BX ; AX is full tracks in cache MOV [TTRACKS],AX ; ; Figure out if we have a DMA boundary problem ; mov DX,DS ; Check for 64k boundary error shl DX,1 shl DX,1 shl DX,1 shl DX,1 ; Segment converted to absolute address add DX,[TRACK_BUFFER_PTR] ; Combine with offset add DX,511 ; simulate a one sector transfer ; And set next divide for round up ; ; If carry is set, then we are within 512 bytes of the end of the DMA segment. ; Adjust TRACK_BUFFER_PTR UP by 512 bytes. ; jnc NotWithin512 add [TRACK_BUFFER_PTR],512 ; adjust jmp short SetCachest NotWithin512: ; ; DX is the physical low 16 bits of the proposed track buffer plus 511. ; See how many sectors fit up to boundary. ; shr DH,1 ; DH = number of sectors in DMA segment ; till start of buffer rounded up mov AH,128 ; AH = max number of sectors in DMA segment sub AH,DH ; ; AH is now the number of sectors that we can successfully transfer using this ; address without a DMA boundary problem. If this number is above or equal to ; the track buffer size, then buffer is OK. Otherwise, we adjust buffer UP ; till it is after the boundary by adding ((AH+1)*512) to the buffer address. ; mov al,ah xor ah,ah cmp AX,[SECTRACK] ; can we fit it in? jae SetCachest ; yes, buffer is OK inc ax ; Add 1 mov cl,9 ; Mult by 512 shl ax,cl add [TRACK_BUFFER_PTR],ax ; Adjust SetCachest: ; ; Set pointer to cache control structures ; mov bx,[SECTRACK] mov cl,9 ; Mult by 512 shl bx,cl ; AX is bytes in Track buffer add bx,[TRACK_BUFFER_PTR] ; First byte after track buffer mov [CACHE_CONTROL_PTR],bx mov cx,SIZE CACHE_CONTROL mov ax,[TTRACKS] MUL cx add bx,ax ; ; Set TERM_ADDR ; mov word ptr [TERM_ADDR],bx CLC RET ;** GETNUM - Read an unsigned integer ; ; This routine looks at DS:SI for a decimal unsigned integer. ; It is up to the caller to make sure DS:SI points to the start ; of a number. If it is called without DS:SI pointing to a valid ; decimal digit the routine will return 0. Any non decimal digit ; defines the end of the number and SI is advanced over the ; digits which composed the number. Leading "0"s are OK. ; ; THIS ROUTINE DOES NOT CHECK FOR NUMBERS LARGER THAN WILL FIT ; IN 16 BITS. If it is passed a pointer to a number larger than ; 16 bits it will return the low 16 bits of the number. ; ; This routine uses the MUL instruction to multiply the running ; number by 10 (initial value is 0) and add the numeric value ; of the current digit. Any overflow on the MUL or ADD is ignored. ; ; ENTRY: ; DS:SI -> ASCII text of number ; EXIT: ; BX is binary for number ; SI advanced to point to char after number ; USES: ; AX,BX,DX,SI ; ; COMMON TO TYPE 1, 2, 3, 4 drivers ; GETNUM: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING XOR BX,BX GETNUM1: LODSB SUB AL,"0" JB NUMRET CMP AL,9 JA NUMRET CBW XCHG AX,BX MOV DX,10 MUL DX ADD BX,AX JMP GETNUM1 NUMRET: DEC SI RET BREAK ;** flag to signify valid emm control record ; valid_emm db 0 ;** INITIAL EMM_CTRL sector ; ; This is a datum which represents a correct initial EMM_CTRL ; sector as discussed in the EMM_CTRL documentation. It is used ; to check for the presense of a valid EMM_CTRL by comparing ; the signature strings, and for correctly initializing the ; EMM_CTRL sector if needed. ; ; The DWORD at BASE_RESET, which is the EMM_BASE of the NULL ; 0th EMM_REC structure, is used as a storage location of ; the address of the EMM_CTRL sector (PLUS 1024!!!!!!). ; This value can be used if it is necessary to re-address the ; EMM_CTRL sector during initialization. See the DISK_ABORT routine. ; NOTE THAT BASE_RESET CAN NOT BE USED AT RUNTIME AS THIS DATUM ; IS NOT PART OF THE RESIDENT IMAGE. ; ; This data is appropriate to TYPE 1 drivers ; EMM_CONTROL LABEL BYTE DB "MICROSOFT EMM CTRL VERSION 1.00 CONTROL BLOCK " DW 0 DW 0 ; NULL 0th record DW EMM_ALLOC + EMM_ISDRIVER DW EMM_EMM ;; Note: When using upper extended memory on the PLUS, the value ;; at BASE_RESET + 2 is patched to FA during initialization. ;; BASE_RESET LABEL DWORD ; RESMEM driver must patch this value DW EXTMEM_LOW + 1024 DW EXTMEM_HIGH DW 0 DB 950 DUP(0) DB "ARRARRARRA" BREAK ;** DISK_ABORT - De-install INT13 after init ; ; This routine MUST BE CALLED to de-install a INT13 cache ; if the de-installation takes place: ; ; AFTER INT 19/INT 9 vectors are replaced ; AFTER ABOVE_PID is valid for TYPE 2 ; AFTER an EMM_REC structure in the EMM_CTRL sector ; has been marked EMM_ISDRIVER for TYPE 1. ; ; In all cases the INT 9 and INT 19 vectors are replaced if the ; value of both words of OLD_19 is NOT -1. This is why the initial value ; of this datum is -1. In the event that the INT 9 and INT 19 ; vectors are replaced, this datum takes on some value other than -1. ; ; If this is a TYPE 1 driver the EMM_ISDRIVER bit is ; turned off in the EMM_REC pointed to by MY_EMM_REC. ; NOTE THAT A TYPE 1 DRIVER MAY USE THIS ROUTINE ; IF IT HAS NOT "TURNED ON" AN EMM_ISDRIVER BIT IN ONE OF THE EMM_REC ; STRUCTURES. This is OK because the initial 0 value of MY_EMM_REC ; is checked, and nothing is done if it is still 0. ; ; If this is a TYPE 2 driver, an ABOVE_DEALLOC call is made on ; ABOVE_PID. ; ; ENTRY: ; BASE_RESET valid if TYPE 1 ; ABOVE_PID valid if TYPE 2 ; EXIT: ; NONE ; USES: ; ALL but DS ; ; COMMON TO TYPE 1, 2 drivers ; DISK_ABORT: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING CMP [DRIVER_SEL],1 JNZ NOT_ABOVE AGAIN: ; ; TYPE 2, De-alloc the Above Board memory ; MOV DX,[ABOVE_PID] MOV AH,ABOVE_DEALLOC INT 67H CMP AH,ABOVE_ERROR_BUSY JZ AGAIN JMP SHORT RET002 NOT_ABOVE: CMP [MY_EMM_REC],0 ; Need to turn off bit? JZ RET002 ; No ; ; TYPE 1, turn off EMM_ISDRIVER at MY_EMM_REC ; MOV AX,WORD PTR [BASE_RESET] MOV DX,WORD PTR [BASE_RESET + 2] SUB AX,1024 ; Backup to EMM_CTRL SBB DX,0 MOV WORD PTR [BASE_ADDR],AX MOV WORD PTR [BASE_ADDR + 2],DX XOR BH,BH ; READ CALL CTRL_IO ; Get EMM_CTRL JC RET002 MOV DI,OFFSET TRACK_BUFFER ADD DI,[MY_EMM_REC] AND [DI.EMM_FLAGS],NOT EMM_ISDRIVER ; Undo install MOV BH,1 ; WRITE CALL CTRL_IO ; EMM_CTRL back out RET002: ; ; Reset INT 9, and/or INT 19 if OLD_19 is not -1 ; PUSH DS LDS DX,[OLD_19] ASSUME DS:NOTHING MOV AX,DS CMP AX,-1 JNZ RESET_VECS CMP AX,DX JZ NO_VECS RESET_VECS: MOV AX,(Set_Interrupt_Vector SHL 8) OR 19H INT 21H ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; removed from smartdrv ; LDS DX,[OLD_9] ; MOV AX,(Set_Interrupt_Vector SHL 8) OR 9H ; INT 21H ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; lds dx,[old_15] cmp ax,-1 jne reset_15 cmp ax,dx je no_vecs reset_15: mov ax,(set_interrupt_vector shl 8) or 15h int 21h NO_VECS: POP DS RET ;** CTRL_IO - Read/Write the first 1024 bytes at BASE_ADDR ; ; This routine is used at INIT time to read the first 1024 ; bytes at BASE_ADDR. If TYPE 1 and BASE_ADDR points ; to the EMM_CTRL address (initial value), the EMM_CTRL sector ; is read/written. If TYPE 1 and BASE_ADDR has been set ; to the start of the cache, the first 1024 bytes of the cache ; are read/written. If TYPE 2, the first 1024 bytes of ; the cache are read/written. All this routine does is ; set inputs to BLKMOV to transfer 1024 bytes at offset 0 to/from ; TRACK_BUFFER. ; ; ENTRY: ; BH = 0 for READ, 1 for WRITE ; EXIT: ; TRACK_BUFFER filled in with 1024 bytes at BASE_ADDR ; USES: ; ALL but DS ; ; COMMON TO TYPE 1, 2 drivers ; CTRL_IO: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING XOR DX,DX MOV AX,DX ; Offset 0 MOV CX,512 ; 1024 bytes PUSH CS POP ES MOV DI,OFFSET TRACK_BUFFER PUSH DS CALL BLKMOV ; Read in EMM_CTRL POP DS RET ;** MM_SETDRIVE - Look for/Init EMM_CTRL and DOS volume ; ; This routine is used by TYPE 1 drivers to check for/initialize ; the EMM_CTRL sector. ; ; This routine reads the EMM_CTRL sector in to TRACK_BUFFER ; CALLS FIND_VDRIVE to check out and alloc or find an EMM_REC ; Sets BASE_ADDR to point to the start of the INT13 cache memory ; Writes the updated EMM_CTRL back out from TRACK_BUFFER ; ; ENTRY: ; BASE_ADDR initialized to point at START of extended memory ; so that the EMM_CTRL sector can be accessed by ; doing I/O at offset 0. ; EXT_K is set to size of extended memory ; DEV_SIZE is set to user requested device size ; EXIT: ; CARRY SET - error, message already printed ; CARRY CLEAR ; BASE_ADDR set for this drive ; DEV_SIZE set to TRUE size ; ; USES ; ALL but DS ; ; Used by TYPE 1 drivers ; MM_SETDRIVE: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING XOR BH,BH ; READ CALL CTRL_IO ; Get EMM_CTRL MOV DX,OFFSET INIT_IO_ERR JC ERR_RET2 CALL FIND_VDRIVE ; Snoop JC RET001 PUSH ES ; Save EMM_BASE from EMM_REC PUSH DI ; modification sunilp cmp [u_switch],0 ; we shall use ol' microsoft standard for this je mm_s$1 ; if we are using int15 scheme no need to write ; out emm control record ; end modification sp MOV BH,1 ; WRITE CALL CTRL_IO ; Write EMM_CTRL back out MOV DX,OFFSET INIT_IO_ERR JC ERR_RET2P mm_s$1: POP WORD PTR [BASE_ADDR] ; Set final correct BASE_ADDR POP WORD PTR [BASE_ADDR + 2] CLC RET001: RET ERR_RET2P: ADD SP,4 ERR_RET2: CALL PRINT STC RET ;** FIND_VDRIVE - Check out EMM_CTRL and alloc ; ; This code checks for a valid EMM_CTRL and sets up ; an initial one if there isn't. It then performs the ; algorithm described in the EMM_CTRL documentation ; to either allocate a NEW EMM_REC of type EMM_APPLICATION, ; or find an existing EMM_REC which is EMM_APPLICATION and has ; its EMM_ISDRIVER bit clear. In the later case it ; checks to see if DEV_SIZE is consistent with EMM_KSIZE ; and tries to make adjustments to EMM_KSIZE or DEV_SIZE ; if they are not consistent. ; ; First the EMM_CTRL signature strings are checked. ; If they are not valid we go to SETCTRL to set up a new ; empty EMM_CTRL in SECTOR_BUFFER. ; If the signatures are valid, EMM_TOTALK is checked ; against EXT_K. If they are the same, the EMM_CTRL sector is ; valid and we skip to SCAN_DEV. Otherwise we initialize the ; EMM_CTRL sector at SETCTRL. All we need to do to set up the initial ; EMM_CTRL sector is transfer the record at EMM_CONTROL into ; TRACK_BUFFER and set EMM_TOTALK and EMM_AVAILK to EXT_K - 1. ; ; In either case, finding a valid EMM_CTRL or setting up a correct ; initial one, we end up at SCAN_DEV. This code performs the ; scan of the EMM_REC structures looking for a "free" one ; or an allocated one which is EMM_APPLICATION and has its EMM_ISDRIVER ; bit clear as described in the EMM_CTRL sector documentation. ; ; If we find a "free" EMM_REC structure we go to GOT_FREE_REC ; and try to allocate some memory. This attempt will fail if ; EMM_AVAILK is less than 16K. We then call SET_RESET to do ; the INT 9/INT 19 setup. We adjust DEV_SIZE to equal the ; available memory if DEV_SIZE is > EMM_AVAILK. Then all we do ; is set EMM_AVAILK and all of the fields in the EMM_REC structure ; as described in the EMM_CTRL sector documentation. ; ; Call SET_RESET to do INT 9/INT 19 setup. ; IF the EMM_REC structure we found is the LAST EMM_REC structure ; we cannot edit any sizes and whatever the EMM_KSIZE ; is we stuff it into DEV_SIZE and set the EMM_ISDRIVER ; bit, and we're done. ; NOTE: We DO NOT check that EMM_KSIZE is at least ; 16K as we know this EMM_REC was created ; by some PREVIOUS INT13 program who ; DID make sure it was at least 16K ; ELSE ; IF EMM_KSIZE == DEV_SIZE ; set EMM_ISDRIVER and we're done ; IF EMM_KSIZE < DEV_SIZE ; either the user has edited his DEVICE = line since ; the last time the system was re-booted, or at the ; time we initially allocated this region EMM_AVAILK ; was less than DEV_SIZE and we had to trim the device ; size back. ; This case is handled at INSUFF_MEM. ; IF the next EMM_REC structure is not allocated ; IF EMM_AVAILK == 0 ; We can't do anything, so set DEV_SIZE ; to EMM_KSIZE and we're done. ; ELSE ; allocate appropriate amount off of EMM_AVAILK ; and add it to EMM_KSIZE and we're done. ; ELSE ; We can't do anything, so set DEV_SIZE ; to EMM_KSIZE and we're done. ; ELSE ; This is the EMM_KSIZE > DEV_SIZE case, it means the ; user MUST have edited his DEVICE = line. ; IF next EMM_REC is NOT free ; We can't shrink the allocation block, ; but we'll leave DEV_SIZE set to the user ; specification and let him waste memory. ; ELSE ; SHRINK the allocation block by adding ; the extra memory back onto EMM_AVAILK ; and subtracting it from EMM_KSIZE and ; we're done ; ; ENTRY: ; SECTOR_BUFFER containes POSSIBLE EMM_CTRL sector ; MUST BE CHECKED ; EXT_K is set to size of extended memory ; DEV_SIZE is set to user requested device size ; EXIT: ; CARRY SET ; Error, message already printed ; CARRY CLEAR ; ES:DI = BASE_ADDR for this drive from EMM_BASE of EMM_REC ; EMM_REC is marked EMM_ISDRIVER ; TRACK_BUFFER must be written out, it contains an updated ; EMM_CTRL sector ; DEV_SIZE set to TRUE size ; MY_EMM_REC is the offset in the 1k EMM_CTRL sector of the ; record we allocated. ; ; USES: ; ALL but DS ; ; Specific to TYPE 1 drivers ; ; substancial modification to this routine, would have totally changed ; if it weren't for the olivetti memory ; ; we are going to be int15 guys from now except for the olivetti memory ; FIND_VDRIVE: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING PUSH CS POP ES MOV DI,OFFSET TRACK_BUFFER MOV SI,OFFSET EMM_CONTROL MOV CX,50 CLD REPE CMPSB jnz no_emm_rec ; JNZ SETCTRL ; No EMM_CTRL ADD SI,EMM_TAIL_SIG - 50 ADD DI,EMM_TAIL_SIG - 50 MOV CX,10 REPE CMPSB jnz no_emm_rec ; JNZ SETCTRL ; No EMM_CTRL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; with int15 guys around this is not feasible ; MOV DI,OFFSET TRACK_BUFFER ; MOV AX,[EXT_K] ; DEC AX ; Size in EMM_CTRL doesn't include EMM_CTRL ; CMP AX,[DI.EMM_TOTALK] ; JZ SCAN_DEV ; EMM_CTRL is valid ;SETCTRL: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; modification sunilp ; dec [valid_emm] ; signal prescence of emm record no_emm_rec: cmp [u_switch],0h ; is it a u driver jne old_st ; if not go to install acc to new int15 jmp new_st ; standard ; ; for olivetti u memory we still have to install according to ol' microsoft st ; old_st: cmp [valid_emm],0h ; do we have a valid emm jne scan_dev ; if yes go to scan structures set_ctrl: ; else we have to install a new one MOV DI,OFFSET TRACK_BUFFER PUSH DI MOV SI,OFFSET EMM_CONTROL MOV CX,1024/2 REP MOVSW ; Move in initial EMM_CTRL POP DI MOV AX,[EXT_K] DEC AX ; Size in EMM_CTRL doesn't include EMM_CTRL MOV [DI.EMM_TOTALK],AX MOV [DI.EMM_AVAILK],AX SCAN_DEV: MOV SI,OFFSET TRACK_BUFFER ; DS:SI points to EMM_CTRL MOV DI,SI ADD DI,EMM_RECORD ; DS:DI points to EMM records MOV CX,EMM_NUMREC LOOK_REC: TEST [DI.EMM_FLAGS],EMM_ALLOC JNZ CHECK_SYS JMP GOT_FREE_REC ; Must alloc new region CHECK_SYS: CMP [DI.EMM_SYSTEM],EMM_APPLICATION JNZ NEXTREC ; Not correct type TEST [DI.EMM_FLAGS],EMM_ISDRIVER JNZ NEXTRECI ; Driver already in CALL SET_RESET ; Set up INT 19,9 MOV AX,[DI.EMM_KSIZE] CMP CX,1 JBE OK_SET_DEV ; If this is last record, must ; select this size CMP AX,[DEV_SIZE] JZ OK_SET_DEV ; Exact match, Okay JB INSUFF_MEM ; User asked for more ; Size of found block is bigger than requested size. ; User MUST have edited CONFIG.SYS. PUSH DI ADD DI,SIZE EMM_REC TEST [DI.EMM_FLAGS],EMM_ALLOC POP DI JZ SHRINK_BLOCK ; Next block is free, shrink MOV AX,[DEV_SIZE] JMP SHORT SET_2 SHRINK_BLOCK: SUB AX,[DEV_SIZE] ; AX is amount to shrink ADD [SI.EMM_AVAILK],AX MOV AX,[DEV_SIZE] MOV [DI.EMM_KSIZE],AX JMP SHORT SET_2 INSUFF_MEM: ; Size of found block is smaller ; than requested size. PUSH DI ADD DI,SIZE EMM_REC TEST [DI.EMM_FLAGS],EMM_ALLOC POP DI JNZ OK_SET_DEV ; Next block is NOT free, can't grow TRY_TO_GROW_BLOCK: CMP [SI.EMM_AVAILK],0 JZ OK_SET_DEV ; Need SPECIAL check for this case SUB AX,[DEV_SIZE] NEG AX ; AX is amount we would like to grow SUB [SI.EMM_AVAILK],AX JNC GOT_THE_MEM ADD AX,[SI.EMM_AVAILK] ; AX is MAX we can grow MOV [SI.EMM_AVAILK],0 ; We take all that's left GOT_THE_MEM: ADD [DI.EMM_KSIZE],AX MOV AX,[DI.EMM_KSIZE] SET_2: OK_SET_DEV: MOV [DEV_SIZE],AX OR [DI.EMM_FLAGS],EMM_ISDRIVER MOV [MY_EMM_REC],DI SUB [MY_EMM_REC],OFFSET TRACK_BUFFER ; Make start of EMM_CTRL relative LES DI,[DI.EMM_BASE] XOR AX,AX ; Set zero, clear carry RET NEXTRECI: NEXTREC: ADD DI,SIZE EMM_REC ; Next record LOOP LOOK_RECJ VERROR: MOV DX,OFFSET ERRMSG2 CALL PRINT STC RET LOOK_RECJ: JMP LOOK_REC GOT_FREE_REC: MOV AX,[SI.EMM_AVAILK] CMP AX,16 JB VERROR ; 16K is smallest device CALL SET_RESET ; Set INT 19,9 CMP AX,[DEV_SIZE] JBE GOTSIZE ; Not enough for user spec MOV AX,[DEV_SIZE] ; User size is OK GOTSIZE: MOV [DEV_SIZE],AX SUB [SI.EMM_AVAILK],AX MOV [DI.EMM_KSIZE],AX MOV [DI.EMM_SYSTEM],EMM_APPLICATION MOV [DI.EMM_FLAGS],EMM_ALLOC + EMM_ISDRIVER MOV [MY_EMM_REC],DI SUB [MY_EMM_REC],OFFSET TRACK_BUFFER ; Make start of EMM_CTRL relative PUSH DI SUB DI,SIZE EMM_REC ; Look at prev record to compute base MOV AX,[DI.EMM_KSIZE] LES BX,[DI.EMM_BASE] MOV DI,ES ; DI:BX is prev base MOV CX,1024 MUL CX ; Mult size by 1024 to get # bytes ADD AX,BX ; Add size onto base to get next base ADC DX,DI POP DI MOV WORD PTR [DI.EMM_BASE],AX MOV WORD PTR [DI.EMM_BASE + 2],DX LES DI,[DI.EMM_BASE] XOR AX,AX ; Set zero, clear carry INC AX ; RESET zero RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; the new int15 standard ; new_st: mov bx,[ext_k] ; contiguous memory reported by int15 cmp [valid_emm],0 ; is there a valid emm record je no_adjust ; if not there no need to adjust ; the memory available ; else we have to find how much memory is already allocated by the microsoft ; emm control block and subtract this from the amount that is available. the ; memory allocated is totalk - availk + 1 ; sub bx,1 ; subtract the emm ctrl record size mov di,offset track_buffer ; set up to address the ctrl record ; read in mov ax,[di.emm_totalk] ; ax <- totalk sub ax,[di.emm_availk] ; ax <- totalk - availk sub bx,ax ; adjust memory available jc verror ; if no memory go to abort ; cmp bx,128 ; is it the minimum required jb verror ; if less go to abort ; ; the memory available has been found and is in bx. now compare it with ; requested device size and take the minimum of the two ; no_adjust: cmp [dev_size],bx ; jb skip_adj_dev_size ; if enough space we don't need to adj ; dev_size mov [dev_size],bx ; else we have compromise on dev size skip_adj_dev_size: ; ; now that we have the correct dev size we should proceed with the installation ; of a new int 15 handler which will account for the memory grabbed by this guy ; mov bx,[ext_k] ; get memory which was reported by int15 add bx,[special_mem] ; account for olivetti guys sub bx,[dev_size] ; mov [int15_size],bx ; this is the size thaat will be reported ; by the int 15 handler ; now install the int15 handler ; push ax push dx push bx push es mov ax,(get_interrupt_vector shl 8) or 15h int 21h mov word ptr [old_15],bx mov word ptr [old_15+2],es mov dx,offset int_15 mov ax,(set_interrupt_vector shl 8) or 15h int 21h pop es pop bx pop dx pop ax ; ; set up int19 vector ; call set_reset ; ; now fill device base address in es:di ; mov ax,[ext_k] sub ax,[dev_size] ; this now has memory left mov cx,1024 ; we are going to find size in bytes mul cx ; dx:ax = ax * 1024 add ax,word ptr [base_addr] ; adc dx,word ptr [base_addr+2] ; mov es,dx ; mov di,ax ; ret ;** SET_RESET - Set up INT 19/INT 9 vectors ; ; This routine will install the INT 9 and INT 19 ; code by saving the current INT 9 and INT 19 ; vectors in OLD_9 and OLD_19 (NOTE: the change in the value of OLD_19 ; to something other than -1 indicates that the vectors have been ; replaced), setting the vectors to point to INT_9 and INT_19. ; ; ENTRY: ; NONE ; EXIT: ; NONE ; USES: ; None ; ; COMMON TO TYPE 1, 2 drivers ; SET_RESET: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING cmp U_SWITCH,0 ;; don't do this for at&t 6300 plus jnz ret005 PUSH AX PUSH DX PUSH BX PUSH ES MOV AX,(Get_Interrupt_Vector SHL 8) OR 19H INT 21H MOV WORD PTR [OLD_19],BX MOV WORD PTR [OLD_19 + 2],ES MOV DX,OFFSET INT_19 MOV AX,(Set_Interrupt_Vector SHL 8) OR 19H INT 21H MOV AX,(Get_Interrupt_Vector SHL 8) OR 9H INT 21H MOV WORD PTR [OLD_9],BX MOV WORD PTR [OLD_9 + 2],ES ; MOV DX,OFFSET INT_9 ; MOV AX,(Set_Interrupt_Vector SHL 8) OR 9H ; INT 21H POP ES POP BX POP DX POP AX RET005: RET BREAK ;** AT_EXT_INIT - Perform /E (TYPE 1) specific initialization ; ; This code does the drive TYPE specific initialization for TYPE 1 ; drivers. ; ; Make sure running on 80286 IBM PC-AT compatible system by ; making sure the model byte at FFFF:000E is FC. ; Get the size of extended memory by using 8800H call to INT 15. ; and make sure it is big enough to accomodate thr driver. ; Limit DEV_SIZE to the available memory found in the previous step ; by making DEV_SIZE smaller if necessary. ; Initialize the GLOBAL parts of the LOADALL information which ; are not set by each call to BLKMOV. ; CALL MM_SETDRIVE to look for EMM_CTRL and perform all the ; other initialization tasks. ; Call DRIVEPARMS to set TERM_ADDR and other drive specific cache parms ; ; ENTRY: ; Invokation line parameter values set. ; EXIT: ; CARRY SET ; Error, message already printed. Driver not installed. ; EMM_CTRL not marked (but MAY be initialized if ; a valid one was not found). ; CARRY CLEAR ; BASE_ADDR set for this drive from EMM_BASE of EMM_REC ; BASE_RESET set from BASE_ADDR ; TERM_ADDR set ; EMM_REC is marked EMM_ISDRIVER ; MY_EMM_REC set ; DEV_SIZE set to TRUE size ; RESET_SYSTEM code and INT 9/INT 19 code included, ; INT 19 and 9 vector patched. ; ; USES: ; ALL but DS ; ; Code is specific to TYPE 1 driver ; AT_EXT_INIT: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING push ds call sys_det ; new routine to do more comprehensive pop ds jnc at001 ; checks than before MOV DX,OFFSET BAD_AT ERR_RET: CALL PRINT STC RET AT001: ;; If upper extended memory is used on the PLUS, it is necessary to ;; patch the values of base_reset and base_addr to get the addressing right. ;; cmp [U_SWITCH],0 ;; patch the code for /U option jz AT001A mov ax,00fah mov word ptr [emm_ctrl_addr+2],ax ;; in resident part for reset code mov word ptr [base_reset+2],ax ;; patching upper address mov word ptr [base_addr+2],ax ;; to FA from 10 AT001A: MOV AX,8800H INT 15H ; Get extended memory size MOV DX,OFFSET NO_MEM OR AX,AX JZ ERR_RET ;; If running on a 6300 PLUS, it is necessary to subtract any upper extended ;; memory from the value obtained by int 15 to determine the correct memory ;; available for a type /E RAMDrive. If loading a /U RAMDrive, it is necessary ;; to find out if there IS any upper extended memory. cmp [U_SWITCH],0 ;; did we ask for upper extended memory jz olstuff ;; no call UpperMemCheck ;; yes, see if anything there jc ERR_RET ;; no, quit mov ax,384 ;; yes, but max allowed is 384K jmp short at001b olstuff: cmp [S5_FLAG],S_OLIVETTI ;; if not 6300 PLUS, go on jne at001b call UpperMemCheck ;; yes, see if 384K is there jc at001b ;; no, so int 15h is right sub ax,384 ;; yes, subtract 384K mov [special_mem],384 ;; store special memory size AT001B: MOV DX,OFFSET ERRMSG2 CMP AX,128 ; 128k min cache JB ERR_RET MOV [EXT_K],AX MOV BX,AX ; DEC BX ; BX is MAX possible cache size CMP [DEV_SIZE],BX JBE AT002 ; DEV_SIZE OK MOV [DEV_SIZE],BX ; Limit DEV_SIZE to available AT002: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 386 modification test [sys_flg],M_386 je loadall_setup mov ax,cs mov word ptr [cod_seg],ax ; set cs descriptor mov cx,16 mul cx mov si,offset cs_des mov [si].bas_0_15,ax mov [si].bas_16_23,dl mov [si].bas_24_31,dh ; set gdt base mov si,offset emm_gdt add ax,offset start_gdt adc dx,0 mov [si].gdt_base_0,ax mov [si].gdt_base_2,dx jmp short common_setup ; ; Init various pieces of LOADALL info ; ;;;; SMSW [LDSW] ;;;; SIDT QWORD PTR [IDTDES] ;;;; SGDT QWORD PTR [GDTDES] ;;;; ; ;;;; ; NOW The damn SXXX instructions store the desriptors in a ;;;; ; different order than LOADALL wants ;;;; ; ;;;; MOV SI,OFFSET IDTDES ;;;; CALL FIX_DESCRIPTOR ;;;; MOV SI,OFFSET GDTDES ;;;; CALL FIX_DESCRIPTOR loadall_setup: MOV [LCSS],CS MOV SI,OFFSET CSDES MOV AX,CS CALL SEG_SET common_setup: CALL MM_SETDRIVE JC RETXXX CALL DRIVEPARMS JNC RETXXX CALL DISK_ABORT STC RETXXX: RET ;;* UpperMemCheck - Called by 6300 PLUS to verify existence of ;; upper extended memory of 384K at FA0000h ;; ;; Returns carry set if no upper extended memory. ;; ;; This routine is called only by a 6300 PLUS, and ;; it reads the hardware switch DSW2 to do the job. ;; UpperMemCheck: push ax in al,66h and al,00001111b cmp al,00001011b pop ax jnz nomem clc ret nomem: stc ret BREAK ;** EMM device driver name ; ; The following datum defines the Above Board EMM 8 character ; device driver name that is looked for as part of TYPE 2 ; specific initialization. ; ; This datum is specific to TYPE 2 drivers ; ABOVE_DEV_NAME DB "EMMXXXX0" ;** ABOVE_INIT - Perform /A (TYPE 2) specific initialization ; ; This code performes the driver specific initialization for ; type 2 drivers. ; ; Swap ABOVE_BLKMOV code in for TYPE 1 code at BLKMOV ; Swap ABOVE_RESET code in for TYPE 1 code at RESET_SYSTEM ; Check to make sure EMM Above Board device driver is installed ; by looking for device name relative to INT 67H segment ; address. This is method 2 described on page 36 and 37 ; of the Expanded Memory Manager Programming Specification. ; ; WARNING! If run on a version of DOS where all INT vectors ; are managed by the kernel, or on a system where some ; foreign program (not EMM.SYS) is also using INT 67H, this ; method will fail to find the EMM device driver. ; The reason this method was used rather than the more portable ; method 1 described on pages 33 and 34 of the EMM Programming ; Specification is that the DOS Installable Device Driver ; document makes a statement about which DOS system calls ; may be made in a device initialization routine, and ; OPEN, IOCTL, and CLOSE are not included in the allowed ; set. Adherance to the Installable Device Driver document, ; therefore, excludes the use of method 1. ; ; Check the EMM device status ; Get the EMM map window address and set BASE_ADDR ; Get the available Above Board memory ; Adjust DEV_SIZE to be consistent with the available memory if needed, ; and also round DEV_SIZE up so that it is a multiple of the 16K ; granularity of the Above Board memory. ; Allocate DEV_SIZE worth of Above Board memory and set ABOVE_PID. ; After this point we can use CTRL_IO and/or BLKMOV to ; read/write the memory we have allocated. ; Install the INT 9 and INT 19 code by calling SET_RESET. ; Call DRIVEPARMS to set TERM_ADDR and other drive specific cache parms ; ; SEE ALSO ; INTEL Expanded Memory Manager Programming Specification ; ; ENTRY: ; Invokation line parameter values set. ; EXIT: ; ABOVE_BLKMOV code swapped in at BLKMOV ; ABOVE_RESET code swapped in at RESET_SYSTEM ; CARRY SET ; Error, message already printed. Driver not installed. ; No Above Board memory allocated. ; CARRY CLEAR ; BASE_ADDR set to segment address of Above Board map window ; ABOVE_PID contains PID of allocated above board memory ; DEV_SIZE set to TRUE size ; TERM_ADDR set ; ; USES: ; ALL but DS ; ; Code is specific to TYPE 2 driver ; ABOVE_INIT: ASSUME DS:INT13CODE,ES:NOTHING,SS:NOTHING ; ; Swap above code into place ; PUSH CS POP ES MOV SI,OFFSET ABOVE_CODE MOV DI,OFFSET DRIVE_CODE MOV CX,OFFSET DRIVE_END - OFFSET DRIVE_CODE REP MOVSB MOV SI,OFFSET ABOVE_RESET MOV DI,OFFSET RESET_SYSTEM MOV CX,OFFSET RESET_INCLUDE - OFFSET RESET_SYSTEM REP MOVSB ; ; Check for presence of Above board memory manager ; MOV AX,(Get_Interrupt_Vector SHL 8) OR 67H INT 21H MOV DI,SDEVNAME MOV SI,OFFSET ABOVE_DEV_NAME MOV CX,8 REPE CMPSB JZ GOT_MANAGER MOV DX,OFFSET NO_ABOVE ABOVE_ERR: CALL PRINT STC RET GOT_MANAGER: ; ; Check memory status ; MOV CX,8000H STLOOP: MOV AH,ABOVE_STATUS INT 67H CMP AH,ABOVE_SUCCESSFUL JZ MEM_OK CMP AH,ABOVE_ERROR_BUSY LOOPZ STLOOP ST_ERR: MOV DX,OFFSET BAD_ABOVE JMP ABOVE_ERR MEM_OK: ; ; Get base address of map region and set BASE_ADDR ; MOV AH,ABOVE_GET_SEG INT 67H CMP AH,ABOVE_ERROR_BUSY JZ MEM_OK CMP AH,ABOVE_SUCCESSFUL JNZ ST_ERR MOV WORD PTR [BASE_ADDR],0 MOV WORD PTR [BASE_ADDR + 2],BX ; ; Allocate drive memory ; GET_AVAIL: MOV AH,ABOVE_GET_FREE INT 67H CMP AH,ABOVE_ERROR_BUSY JZ GET_AVAIL CMP AH,ABOVE_SUCCESSFUL JNZ ST_ERR MOV AX,DX ; AX is total 16K pages ; BX is un-allocated 16K pages MOV DX,OFFSET NO_MEM OR AX,AX JZ ABOVE_ERR MOV DX,OFFSET ERRMSG2 ; ; change in allocation strategy new default is all of available pages ; < 8192K. ; ; algorithm: if (free_pages < 8) then error(); ; else { ; if (free_pages > 200h) then free_pages = 200h; ; if (num_arg == 1) then dev_size = free_pages; ; else dev_size = min (dev_size,free_pages) CMP BX,8 ; 128K = 16K * 8 = Min cache size JB ABOVE_ERR cmp bx,0200h ; 8192K = Max cache size jbe ab0$1 ; if less or equal fine mov bx,0200h ; else limit it to 8192K ab0$1: mov cx,4 ; to convert number of pages into no of k shl bx,cl cmp [num_arg],1 ; is numeric argument 1 ( means none ) jne ab0$2 ; cache size has been requested mov [dev_size],bx ; else use all of available cache jmp short ab001 ; ab0$2: cmp [dev_size],bx ; minimum of dev size and bx jb ab001 mov [dev_size],bx ; ab001: mov bx,[dev_size] mov [current_dev_size],bx ; Initialize current device size ; ; BX is K we want to allocate (limited by available K) ; BX is at least 16 ; MOV AX,BX MOV CX,4 ; Convert back to # of 16K pages SHR BX,CL TEST AX,0FH ; Even???? JZ OKAYU ; Yes INC BX ; Gotta round up PUSH BX MOV CX,4 SHL BX,CL MOV [DEV_SIZE],BX ; Correct dev size too by rounding it up to ; next multiple of 16K, no sense wasting ; part of a page. ;A mov [current_dev_size],bx ; Correct current device size also ;A POP BX OKAYU: MOV AH,ABOVE_ALLOC INT 67H CMP AH,ABOVE_ERROR_BUSY JZ OKAYU CMP AH,ABOVE_SUCCESSFUL JZ GOT_ID CMP AH,ABOVE_ERROR_MAP_CNTXT JZ ST_ERRJ CMP AH,ABOVE_ERROR_OUT_OF_PIDS JB ST_ERRJ MOV DX,OFFSET ERRMSG2 JMP ABOVE_ERR ST_ERRJ: JMP ST_ERR GOT_ID: MOV [ABOVE_PID],DX ; ; INSTALL ABOVE RESET handler ; CALL SET_RESET ; ; We are now in good shape. ; CALL DRIVEPARMS JNC RETYYY CALL DISK_ABORT STC RETYYY: RET BREAK ; ; This label defines the start of the code swapped in at DRIVE_CODE ; ABOVE_CODE LABEL WORD ; ; WARNING DANGER!!!!!!! ; ; This code is tranfered over the /E driver code at DRIVE_CODE ; ; ALL jmps etc. must be IP relative. ; ALL data references must be to cells at the FINAL, TRUE location ; (no data cells may be named HERE, must be named up at BLKMOV). ; OFFSET of ABOVE_BLKMOV relative to ABOVE_CODE MUST be the same as ; the OFFSET of BLKMOV relative to DRIVE_CODE. ; SIZE of stuff between ABOVE_CODE and ABOVE_END MUST be less than ; or equal to size of stuff between DRIVE_CODE and DRIVE_END. IF2 IF((OFFSET ABOVE_BLKMOV - OFFSET ABOVE_CODE) NE (OFFSET BLKMOV - OFFSET DRIVE_CODE)) %out ERROR BLKMOV, ABOVE_BLKMOV NOT ALIGNED ENDIF IF((OFFSET ABOVE_END - OFFSET ABOVE_CODE) GT (OFFSET DRIVE_END - OFFSET DRIVE_CODE)) %out ERROR ABOVE CODE TOO BIG ENDIF ENDIF DD ? ; 24 bit address of start of this RAMDRV ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;** ABOVE_BLKMOV - Perform transfer for TYPE 2 driver ; ; This routine is the transfer routine for moving bytes ; to and from the Above Board memory containing the cache. ; ; The Above Board is implemented as 4 16K windows into the Above ; Board memory, giving a total window of 64K wich starts on some ; 16K boundary of the Above Board memory. Given that a DOS I/O ; request is up to 64K bytes starting on some sector boundary, ; the most general I/O picture is: ; ; |------------|------------|------------|------------|------------| ; | Above Brd | Above Brd | Above Brd | Above Brd | Above Brd | ; |Log page n |Log page n+1|Log page n+2|log page n+3|Log page n+4| ; |------------|------------|------------|------------|------------| ; |---|---| | | ; | | |---------------- 64K bytes of sectors -------------| ; Byte | | | ; offset|------------------|------------------------| | ; of first| Number of words in | | ; byte of | first part of I/O that |---|---| ; I/O in | can be performed once Number ; first | logical pages n - n+3 of words ; Log page| are mapped into physical in tail ; | pages 0 - 3 part of I/O ; Location of that have ; first byte to be done ; of sector M, once logical ; the start sector page n+4 is ; of the I/O mapped into ; physical page ; 0 ; ; One or both of "Byte offset of first byte of I/O in first page" and ; "Number of words in tail part of I/O" may be zero depending on the ; size of the I/O and its start offset in the first logical page it is ; possible to map. ; ; WARNING: IF A PRE-EMPTIVE MULTITASKING SYSTEM SCHEDULES A TASK WHICH ; IS USING THE ABOVE BOARD DURING THE TIME THIS DRIVER IS IN THE ; MIDDLE OF PERFORMING AN I/O, THE SYSTEM HAD BETTER MANAGE THE A ; BOARD MAPPING CONTEXT CORRECTLY OR ALL SORTS OF STRANGE UNPLEASANT ; THINGS WILL OCCUR. ; ; SEE ALSO ; INTEL Expanded Memory Manager Programming Specification ; ; ENTRY: ; ES:DI is packet transfer address. ; CX is number of words to transfer. ; DX:AX is 32 bit start byte offset (0 = start of cache) ; BH is 1 for WRITE, 0 for READ ; ; BASE_ADDR set to point to Above Board mapping window in main memory ; This "input" is not the responsibility of the caller. It ; is up to the initialization code to set it up when the ; device is installed ; ; EXIT: ; Carry Clear ; OK, operation performed successfully ; Carry Set ; Error during operation, AL is error number ; ; USES: ; ALL ; ; This routine is specific to TYPE 2 driver ; above_blkmov: assume ds:int13code,es:nothing,ss:nothing ; ; save mapping context and return with error if save fails ; save_mapping_context jnc ab_blk$1 ret ; ; find logical page number, offset of i/o in first page ; ab_blk$1: push cx mov cx,1024*16 ; 16k bytes / page div cx ; dx:ax / 16k --> log page numb in ax ; --> offset of i/o in dx mov si,dx ; transfer offset to si mov dx,ax ; store the page number in dx pop cx ; ; find case and dispatch accordingly ; ; case 0 : user buffer below page map, can use aaron's code ; case 1 : user buffer above page map, can use aaron's code ; case 2 : user buffer totally within page map, use pai's code ; case 3 : user buffer partly in page map partly below, error ; case 4 : user buffer partly in page map partly above, error ; push bx push cx ; ; if( final_user_off < pm_base_addr ) then case 0 ; mov ax,di ; get user buffer initial offset into ax shr ax,1 ; convert to word offset dec cx ; convert word count to 0 based number add ax,cx ; user buffer final word offset shr ax,1 ; convert to segment shr ax,1 ; shr ax,1 ; mov bx,es ; get segment of buffer add ax,bx ; now we have the segment of the user buffer ; with offset < 16 sub ax,word ptr [base_addr+2] ; compare against page map jc aar_cd ; if below page map then execute old code ; ; if( initial_user_off < pm_base_addr ) then error ; mov cx,4 mov bp,di ; get initial offset in bp shr bp,cl ; add bp,bx ; sub bp,word ptr [base_addr +2] jc ab_error ; ; ; if ( initial_user_off >= pm_end_addr ) then case1 ; cmp bp,4*1024 ; jae aar_cd ; ; ; if ( final_addr >= pm_end_addr ) then error ; cmp ax,4*1024 jae ab_error ; ; case 2 ; within_pm: jmp new_code ; user buffer in page map ; so we need to execute new code ab_error: add sp,4 mov al,0bbh ; general failure stc jmp short REST_CONT ; RESTORE CONTEXT!!! aar_cd: pop cx pop bx ; ; Referring back to the diagram given above the following routine is ; to take care of transfer of the most general case. ; What this routine does is break every I/O down into the above parts. ; The first or main part of the I/O is performed by mapping 1 to 4 ; sequential logical pages into the 4 physical pages and executing one ; REP MOVSW. If the tail word count is non-zero then the fith sequential ; logical page is mapped into physical page 0 and another REP MOVSW is ; executed. ; ; METHOD: ; Break I/O down as described above into main piece and tail piece ; Map the appropriate number of sequential pages (up to 4) ; into the page window at BASE_ADDR to set up the main piece ; of the I/O. ; Set appropriate seg and index registers and CX to perform the ; main piece of the I/O into the page window ; REP MOVSW ; IF there is a tail piece ; Map the next logical page into physical page 0 ; Reset the appropriate index register to point at phsical page 0 ; Move tail piece word count into CX ; REP MOVSW ; Restore Above Board page mapping context ; XOR BP,BP ; No tail page PUSH BX ; ; DX is first page #, SI is byte offset of start of I/O in first page ; MOV AX,DX MOV BX,SI SHR BX,1 ; # Words in first 16k page which are not part ; of I/O PUSH CX ADD BX,CX ; # of words we need to map to perform I/O MOV DX,BX AND DX,1FFFH ; DX is number of words to transfer last page ; remainder of div by words in 16K bytes MOV CL,13 ; Div by # words in 16K SHR BX,CL ; BX is number of pages to map (may need round up) OR DX,DX ; Remainder? JZ NO_REM INC BX ; Need one more page NO_REM: MOV CX,BX ; CX is total pages we need to map MOV BX,AX ; BX is first logical page CMP CX,4 ; We can map up to 4 pages JBE NO_TAIL MOV BP,DX ; Words to move in tail page saved in BP DEC CX ; Need second map for the 5th page POP AX SUB AX,DX ; Words to move in first 4 pages is input ; word count minus words in tail page PUSH AX ; Count for first mapping back on stack NO_TAIL: ; Map CX pages MOV DX,[ABOVE_PID] MOV AX,ABOVE_MAP SHL 8 ; Physical page 0 PUSH AX MAP_NEXT: POP AX ; Recover correct AX register PUSH AX PUSH BX PUSH DX INT 67H ; Damn call ABOVE_MAP zaps BX,DX,AX POP DX POP BX OR AH,AH JNZ MAP_ERR1 ; error IF2 IF (ABOVE_SUCCESSFUL) %out ASSUMPTION IN CODE THAT ABOVE_SUCCESSFUL = 0 IS INVALID ENDIF ENDIF NEXT_PAGE: INC BX ; Next logical page POP AX INC AL ; Next physical page PUSH AX LOOP MAP_NEXT POP AX ; Clean stack POP CX ; Word count for first page mapping POP AX ; Operation in AH ; ; BX has # of next logical page (Tail page if BP is non-zero) ; BP has # of words to move in tail page (0 if no tail) ; CX has # of words to move in current mapping ; SI is offset into current mapping of start of I/O ; AH indicates READ or WRITE ; PUSH AX ; Save op for possible second I/O OR AH,AH JZ READ_A ; ; WRITE ; PUSH ES PUSH DI MOV DI,SI ; Start page offset to DI POP SI ; DS:SI is transfer addr POP DS ASSUME DS:NOTHING MOV ES,WORD PTR [BASE_ADDR + 2] ; ES:DI -> start JMP SHORT FIRST_MOVE READ_A: ASSUME DS:INT13CODE MOV DS,WORD PTR [BASE_ADDR + 2] ; DS:SI -> start ASSUME DS:NOTHING FIRST_MOVE: REP MOVSW OR BP,BP ; Tail? JNZ TAIL_IO ; Yup ALL_DONE: POP AX CLC REST_CONT: ; Restore page mapping context PUSH AX ; Save possible error code PUSHF ; And carry state REST_AGN: MOV DX,[ABOVE_PID] MOV AH,ABOVE_RESTORE_MAP_PID INT 67H OR AH,AH JZ ROK IF2 IF (ABOVE_SUCCESSFUL) %out ASSUMPTION IN CODE THAT ABOVE_SUCCESSFUL = 0 IS INVALID ENDIF ENDIF CMP AH,ABOVE_ERROR_BUSY JZ REST_AGN CMP AH,ABOVE_ERROR_NO_CNTXT JZ ROK ; Ignore the invalid PID error POP DX POP DX ; Clean stack MOV AL,0BBH ; General failure STC RET ROK: POPF ; Recover carry state POP AX ; and possible error code RET TAIL_IO: MOV DX,[ABOVE_PID] MAP_AGN: MOV AX,ABOVE_MAP SHL 8 ; map logical page BX to phys page 0 PUSH BX PUSH DX INT 67H ; Damn call ABOVE_MAP zaps BX,DX,AX POP DX POP BX OR AH,AH JNZ MAP_ERR2 ; Error IF2 IF (ABOVE_SUCCESSFUL) %out ASSUMPTION IN CODE THAT ABOVE_SUCCESSFUL = 0 IS INVALID ENDIF ENDIF SECOND_MOVE: POP AX ; Recover Op type PUSH AX OR AH,AH JZ READ_SEC ; ; WRITE ; XOR DI,DI ; ES:DI -> start of tail JMP SHORT SMOVE READ_SEC: XOR SI,SI ; DS:SI -> start of tail SMOVE: MOV CX,BP REP MOVSW JMP ALL_DONE MAP_ERR1: CMP AH,ABOVE_ERROR_BUSY ; Busy? JZ MAP_NEXT ; Yes, wait till not busy (INTs are ON) ADD SP,6 ; Clean stack JMP SHORT DNR_ERR MAP_ERR2: CMP AH,ABOVE_ERROR_BUSY JZ MAP_AGN ADD SP,2 DNR_ERR: MOV AL,0AAH ; Drive not ready STC JMP REST_CONT ; ; ; this code has been written to handle te cases of overlapping usage ; of the above board page frame segment by the cache and user buffer ; assumption: in dos tracks cannot be more than 64 sectors long so ; in the worst case we shall have the user buffer occupying three ; pages is the page frame. we attempt to find the page that is ; available for the cache and use it repeatedly to access the cache ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ; the algorithm is: ; ****************************************************** ; [STEP1: determine the page we can use for the cache] ; ; if (initial_para_offset_user in page 1 or above ) then { ; physical_cache_page = 0; ; cache_segment = above board segment; ; } ; else { ; physical_cache_page = 3; ; cache_segment = above_board_segment + 3*1024; ; } ; ; ****************************************************** ; [STEP2: initial setup] ; ; count = user_count_requested; ; number_to_be_transferred = min ( count, (16K - si) >> 2 ); ; exchange source and destination if necessary; ; ; ******************************************************* ; [STEP3: set up transfer and do it] ; ; count = count - number_to_be_transferred; ; map_page cache_handle,physical_cache_page,logical_cache_page ; mov data ; ; ******************************************************* ; [STEP4: determine if another transfer needed and setup if so] ; ; if ( count == 0 ) then exit; ; if ( operation == read ) then source_offset = 0; ; else dest_offset = 0; ; number_to_be_transferred = min ( count, 8*1024 ); ; logical_page_number++ ; ; ; ******************************************************* ; [STEP5: go to do next block] ; ; goto [STEP3] ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ; new_code: assume ds:int13code,es:nothing,ss:nothing ; ; input parameters: ; ; bp : start para offset of user buffer in physical page frame ; ax : end para offset of user buffer in physical page frame ; di : transfer offset of user buffer ; es : transfer segment of user buffer ; dx : logical page number in cache ; si : offset from start in logical page number ; ; on stack { cx,bx } where cx = number of words, bx = read / write status ; ; [STEP1: finding physical cache page and page frame] ; ; ; assume is physical page 0 ; xor al,al ; use page 0 for cache mov bx,word ptr [base_addr+2] ; ; see if this assumption valid ; cmp bp,1024 ; is initial in page 1 or above jae ab$30 ; if so or assumption is valid ; ; else we have to correct our assumption ; mov al,3 ; use page 3 for cache add bx,3*1024 ; ; ; initialise page frame segment ; ab$30: mov ds,bx ; assume ds:nothing ; ; [STEP2: initialising transfer parameters] ; ; pop bp ; bp will have count of words left to be transferred pop bx ; read / write status push bx ; save it back again push dx ; save this too ; ; initially si offset into logical page, so we can only do 16*1024 - si ; byte transfer ; mov cx,16*1024 sub cx,si shr cx,1 ; convert to word count ; ; number to be transferred is the minimum of this and the user requested ; count ; cmp cx,bp jb ab$31 mov cx,bp ; ab$31: ; ; see if write, then we have to switch source with destination ; or bh,bh je ab$32 ; if read we don't have to do anything ; else we have to switch src_dest_switch ab$32: ; ; set direction flag so that we don't have to do it repeatedly ; cld ; ; [STEP3: set up transfer and do it] ; ab$33: ; ; update count of words still left to be transferred after this ; sub bp,cx ; ; map the logical page in cache to the physical page selected ; mov bx,dx ; get logical page into bx ; al already holds the physical page # map_page jnc ab$34 ; suceeded ? ; ; else report error ; add sp,4 stc jmp short restore_mp ; and go to restore page map ab$34: ; ; succeeded, do the transfer ; rep movsw ; ; ; [STEP4: check if transfer done, if not set up for next block] ; [STEP5: go back to STEP3] ; ; check if done ; or bp,bp ; count 0 je ab$40 ; yes, go to finish up ; ; recover original dx and bx, increment dx and then save both again ; pop dx pop bx inc dx push bx push dx ; ; words to be transferred minimum of count and 8*1024 words ; mov cx,8*1024 ; 8k words in a page cmp cx,bp ; jbe ab$35 ; if below or equal this is what we want ; mov cx,bp ; else we can transfer the whole count ab$35: ; ; see whether cache src or dest and accordingly reset either si or di ; or bh,bh ; read? jne ab$36 ; if write go to modify ; ; read, zero si and go back to step3 ; xor si,si jmp short ab$33 ; to step 3 ab$36: ; ; write, zero di and go back to step3 ; xor di,di jmp short ab$33 ; to step 3 ; ; finishing up we have to restore the page map ; ab$40: add sp,4 clc restore_mp: restore_mapping_context ret DW ? ; SPACE for ABOVE_PID ; ; This label defines the end of the code swapped in at DRIVE_CODE ; ABOVE_END LABEL WORD BREAK ; ; WARNING DANGER!!!!!!! ; ; This code is tranfered over the /E driver code at RESET_SYSTEM ; ; ALL jmps etc. must be IP relative. ; ALL data references must be to cells at the FINAL, TRUE location ; (no data cells may be named HERE, must be named up at RESET_SYSTEM). ; SIZE of stuff between ABOVE_RESET and ABOVE_RESET_END MUST be less than ; or equal to size of stuff between RESET_SYSTEM and RESET_INCLUDE. ; ; NOTE: EACH ABOVE BOARD driver has an INT 19 and 9 handler. This is ; different from /E and RESMEM in which only the first ; driver has an INT 19 and 9 handler. ; IF2 IF((OFFSET ABOVE_RESET_END - OFFSET ABOVE_RESET) GT (OFFSET RESET_INCLUDE - OFFSET RESET_SYSTEM)) %out ERROR ABOVE_RESET CODE TOO BIG ENDIF ENDIF ;** ABOVE_RESET perform TYPE 2 (/A) driver specific reboot code ; ; This code issues an ABOVE_DEALLOC call for the memory ; associated with this particular TYPE 2 cache since the ; system is being re-booted and the driver is "gone". ; ; ENTRY ; NONE ; EXIT ; NONE ; USES ; NONE ; ; This code is specific to TYPE 2 drivers ; ABOVE_RESET: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH AX PUSH DX AGAIN_RESET: MOV DX,[ABOVE_PID] MOV AH,ABOVE_DEALLOC ; Close PID INT 67H CMP AH,ABOVE_ERROR_BUSY JZ AGAIN_RESET POP DX POP AX RET ; ; This label defines the end of the code swapped in at RESET_SYSTEM ; ABOVE_RESET_END LABEL BYTE BREAK ;** Message texts and common data ; ; Init data. This data is disposed of after initialization. ; it is mostly texts of all of the messages ; ; COMMON to TYPE 1 and 2 drivers ; ; THIS IS THE START OF DATA SUBJECT TO TRANSLATION NO_ABOVE db "SMARTDrive : Expanded Memory Manager not present",13,10,"$" BAD_ABOVE db "SMARTDrive : Expanded Memory Status shows error",13,10,"$" BAD_AT db "SMARTDrive : Cannot run on this computer",13,10,"$" NO_MEM db "SMARTDrive : No extended memory available",13,10,"$" ERRMSG1 db "SMARTDrive : Invalid parameter",13,10,"$" ERRMSG2 db "SMARTDrive : Insufficient memory",13,10,"$" INIT_IO_ERR db "SMARTDrive : I/O error accessing cache memory",13,10,"$" NOHARD db "SMARTDrive : No hard drives on system",13,10,"$" BIGTRACK db "SMARTDrive : Too many bytes per track on hard drive",13,10,"$" BADVERMES db 13,10,"SMARTDrive : Incorrect DOS version",13,10,"$" ; ; This is the Int13 header message. ; HEADERMES db 13,10,"Microsoft SMARTDrive Disk Cache v2.10",13,10,"$" ; ; This is the status message used to display INT13 configuration ; it is: ; ; STATMES1STATMES2<# tracks in cache>STATMES3 ; STATMES4 ; ; It is up to translator to move the message text around the numbers ; so that the message is printed correctly when translated ; STATMES1 db " Cache size: $" STATMES1A db "K in Expanded Memory$" STATMES1E db "K in Extended Memory$" STATMES2 db 13,10," Room for $" STATMES3 db " tracks of $" STATMES4 db " sectors each",13,10,13,10,"$" ifdef OMTI omti_msg db " OMTI controller release",13,10,"$" endif ;----------------------------------------------------------------------- ; ; END OF DATA SUBJECT TO TRANSLATION ; IF DEBUG STATMES5 db "Device CS = $" STATMES6 db " decimal",13,10,"$" s5flagmsg db " = S5 flag",13,10,"$" U_msg db " = U Switch", 13,10,'$' ENDIF db "This program is the property of Microsoft Corporation." INT13_END LABEL BYTE INT13CODE ENDS END