;****************************************************************** ;* * ;* AN EXAMPLE OF A PROTECTED MODE DEVICE DRIVER * ;* * ;****************************************************************** ; $title(bpmscg.a86 Bootstrap driver for iSBC 214/215g) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; INTEL CORPORATION PROPRIETARY INFORMATION ; ; TITLE: bpmscg Bootstrap driver for iSBC 214/215G and iSBX 218A ; ; DATE: 3-10-81 ; ; ABSTRACT: This module contains device$init$mscgen and ; device$read$mscgen. ; ; LANGUAGE DEPENDENCIES: Compatible with PL/M LARGE. ; ; MODIFIED: Jan 1984: devicereadmscgen modified to function in ; both protected mode and real mode ; ; Jun 1988: added 9 sectors/track support ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $include(:F1:bs3.inc) ; name bpmscg ; ; ;******************************************************************* ;* * ;* These are the three public names a third stage device driver * ;* MUST present. They must match the corresponding names that * ;* were specified in the device macro for this driver in BS3.A86 * ;* * ;******************************************************************* ; public deviceinitmscgen public devicereadmscgen public data_msc ; extrn far_call : far ; Floppy block$num adjustment ; data_bsmsc segment para public 'DATA' ; extrn delay_constmsc: word ; data_bsmsc ends ; ; code_bsmsc segment para public 'CODE' ; ; Configuration information: ; extrn wakeup_msc: word ; Wakeup port/address extrn device_msc: byte ; Device number: 0-2 extrn drtab_msc: byte ; Device Table ; code_bsmsc ends assume cs: code_bsmsc assume ds: data_bsmsc ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Equates ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; IO_ERROR equ 1501h CMD_CLEAR equ 0 ; clear interrupt command CMD_WAKEUP equ 1 ; wakeup command CMD_RESET equ 2 ; reset command ; FUNCT_INIT equ 0 ; Initialize function FUNCT_READ equ 4 ; Read function ; MAX_RETRIES equ 10 ; Number of times to try a read ; TIMEOUT_COUNT equ 600 ; Board present timeout OP_DELAY equ 180 ; Op complete delay ; VF_AUTO equ 00000001B ; ADCR flags valid VF_DOUBLE_DENSITY equ 00000010B ; double density flag VF_DOUBLE_SIDED equ 00000100B ; double sided flag VF_MINI equ 00001000B ; mini floppy flag VF_NOT_FLOPPY equ 00010000B ; non-floppy flag ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Structures ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; scb struc ; System Control Block ; soc db ? ; Control byte resvd1 db ? ; ccbptr dd ? ; CCB Pointer ; scb ends ; ccb struc ; Channel Control Block ; ccw1 db ? ; Channel Control Word 1 busy1 db ? ; Channel Busy 1 ch1ptr dd ? ; Channel 1 Control Pointer resvd2 dw ? ccw2 db ? ; Channel Control Word 2 busy2 db ? ; Channel Busy 2 ch2ptr dd ? ; Channel 2 Control Pointer ch2pc dw ? ; Channel 2 Task ; ccb ends ; cib struc ; Channel Info Block ; cmd db ? ; Command stat db ? ; Status cmdsem db ? ; Command Semaphore statsem db ? ; Status Semaphore ch1pc dd ? ; Channel 1 Task iopbptr dd ? ; IOPB Pointer resvd3 dd ? ; cib ends ; iopb struc ; IO Parameter Block ; status dw ? ; Transfer status xstatus dw ? ; Extended status actual dd ? ; Actual transfer count device dw ? ; Device code unit db ? ; Unit number funct db ? ; Function modf dw ? ; Modifier cyl dw ? ; Cylinder head db ? ; Head sector db ? ; Sector bufptr_off dw ? ; Buffer Pointer Offset bufptr_base dw ? ; Buffer Pointer Base count_lo dw ? ; Count Low word count_hi dw ? ; Count High word resvd4 dd ? ; iopb ends ; drtab struc ; device Table ; ncyl dw ? ; Number of Cylinders nfsur db ? ; Number of Fixed Surfaces nrsur db ? ; Number of Removable Surfaces nsec db ? ; Number of Sectors secsize dw ? ; Sector Size nalt db ? ; # of alternates (density) ; drtab ends ; scratch_area struc ; count dw ? ; # bytes to xfer act dw ? ; # bytes xferred sectr dw ? ; sector to begin xfer ; scratch_area ends ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Constants and Data ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; data_bsmsc segment ; Static Data data_msc label byte real_ccb label byte ccb <> ; real_cib label byte cib <> ; real_iopb label byte iopb <> ; ; Note: do not change order of data structures from real_drtab ; through label_block - translate_floppy relies on this structure ; real_drtab label byte drtab <> ; flags db ? ; copy of label_flags devgran dw ? ; copy of label_gran ; label_block label byte ; RMX label ; db 384 dup (?) ; from beginning of disk ; db 10 dup (?) ; start of actual label label_flags db ? db 17 dup (?) label_gran dw ? db 18 dup (?) ; label_drtab label byte drtab <> ; db 72 dup (?) ; scratch label byte scratch_area <> ; num_heads db ? ; Number of heads ; trans_flop dd far_call data_bsmsc ends ; code_bsmsc segment ; Constants ; dataseg dw data_bsmsc ; data segment codeseg dw code_bsmsc ; code segment ; init_info label byte ; Initial values ; ccb < 1, 0FFH, real_cib.ch1pc, 0, 1, 0, real_ccb.ch2pc, 4> ; cib < 0, 0, 0, 0, 0, real_iopb, 0> ; drtab_218 label byte ; Single sided FM 8" floppy drtab <77, 0, 1, 26, 128, 0> ; drtab_218A label byte ; 5 1/4" Floppy drtab <80, 0, 1, 16, 128, 0> ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; TITLE: init_io ; Passes command to 215 and waits for result ; ; CALL: call init_io ; jnz error ; ; CALLS: none ; ; DESTROYS: ax, dx ; ; ABSTRACT: Starts 215 operation set up in iopb and ; waits for completion. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; init_io proc near ; pass command to fdc ; push cx mov real_cib.statsem, 0 ; Clear status semaphore mov dx, wakeup_msc ; Output WAKEUP mov al, CMD_WAKEUP out dx, al mov cx, 1 ; delay 10ms push cx call bstime ; cmp real_iopb.funct, FUNCT_INIT jnz setup_1 mov cx, 6000 ; wait 60 seconds if funct jmp short wait_loop ; initializes a drive setup_1: mov cx, 350 ; wait 3.5 secs for other ops ; wait_loop: cmp cx, 0 ; have we timed out yet? je wait_too_long dec cx push cx mov bx, 1 ; delay 10 ms before looking push bx call bstime pop cx ; test real_cib.statsem, 1 ; Wait for status returned jz wait_loop test real_ccb.busy1, 1 ; wait for 8089 to finish, jnz wait_loop ; as well ; mov al, real_cib.stat ; Check returned status mov real_cib.statsem, 0 test al, 40H pop cx ret ; wait_too_long: mov cx, IO_ERROR push cx ; call bserror ; there is no ret from this! int 3 ; init_io endp ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; TITLE: do_init ; Does device initialize operation ; ; CALL: call do_init ; ; CALLS: init_io ; ; ABSTRACT: Moves drtab info pointed to by cs:si to real_drtab ; and sets up iopb for init, then calls init_io. ; Saves all registers except es and di. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; do_init proc near ; push ax ; Save registers push cx push dx push ds mov ds, codeseg ; Move cs:si to real_drtab lea di, real_drtab mov es, dataseg mov cx, SIZE(drtab)/2 rep movs es:WORD PTR [di], ds:WORD PTR [si] pop ds ; Need ds back now mov real_iopb.bufptr_off, offset real_drtab mov real_iopb.bufptr_base, ds mov real_iopb.funct, FUNCT_INIT call init_io ; Do init ; mov ax, 100 ; do a little extra delay push ax ; (220 fudge) call bstime pop dx ; Restore rest of registers pop cx pop ax ret ; do_init endp ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; TITLE: deviceinitmscgen ; ;******************************************************************** ;* * ;* This is the device initialization routine, it is exactly the * ;* same as in the first stage, since will be called only in real * ;* mode. * ;* * ;******************************************************************** ; ; CALL: dev_gran = deviceinitmscgen(unit); ; ; INTERFACE VARIABLES: ; unit unit number (word) ; ; Returns device granularity in bytes or ; zero if device is not ready. ; ; CALLS: init_io ; do_init ; ; ABSTRACT: Resets board, checks for presence of board ; by waiting for response to first channel attention, ; then initializes unit 0 and the selected unit. ; Returns 0 if not ready, drtab_msc.secsize if ; hard disk, or volume label granularity if floppy. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; init_unit equ [bp+6] ; unit number argument ; deviceinitmscgen proc far ; Device Initialize ; push bp ; mark stack mov bp, sp push ds ; get local ds mov ds, dataseg ; mov es, wakeup_msc ; Set up scb mov es:WORD PTR 0, 1 ; set soc mov es:WORD PTR 2, OFFSET real_ccb ; set up ccb pointer mov es:WORD PTR 4, ds ; lea si, init_info ; set up ccb+cib mov ds, codeseg lea di, real_ccb mov es, dataseg mov cx, (SIZE(ccb) + SIZE(cib))/2 rep movs es:WORD PTR [di], ds:WORD PTR [si] mov ds, dataseg ; xor ax, ax mov cx, (SIZE(iopb) + SIZE(drtab))/2 rep stos es:WORD PTR [di] ; Zap iopb and drtab ; mov dx, wakeup_msc ; Send reset mov al, CMD_RESET out dx, al ; mov bx, 2 ; allow a 20 ms reset pulse push bx call bstime mov al, CMD_CLEAR ; Clear interrupts out dx, al ; mov bx, 3 ; allow a 30 ms delay for push bx ; controller to initialize call bstime mov al, CMD_WAKEUP ; First Channel Attention out dx, al ; mov bx, 5 push bx call bstime ; mov cx, TIMEOUT_COUNT ; Time out the busy flag timeout_loop: cmp cx, 0 ; have we timed out yet? je not_ready dec cx push cx mov bx, 1 ; delay 10 ms before looking push bx call bstime pop cx ; test real_ccb.busy1, 1 ; Check for busy goes to 0 jz board_present jmp timeout_loop ; not_ready: xor ax, ax ; Return 0 jmp init_ret ; board_present: mov real_iopb.bufptr_off, OFFSET real_drtab mov real_iopb.bufptr_base, ds call init_io ; Initialize drive 0 ; mov al, device_msc ; Set device xor ah, ah mov real_iopb.device, ax ; mov al, init_unit ; Initialize selected unit mov ah, drtab_msc.nfsur ; number of heads cmp al, 4 ; Check for removable jb init_disk add al, 10H-4 mov ah, drtab_msc.nrsur cmp al, 14H ; Check for floppy jae init_floppy ; init_disk: lea si, drtab_msc ; Get disk device table jmp short init_go ; init_floppy: sub al, 4 mov ah, 1 mov real_iopb.device, 3 ; Default to 5 1/4" for now lea si, drtab_218A ; Get floppy device table ; init_go: mov real_iopb.unit, al ; Set unit mov num_heads, ah ; Set number of heads call do_init ; Do the init from si jnz not_ready ; read_label: mov real_iopb.funct, FUNCT_READ ; Set up read operation cmp real_iopb.device, 3 ; check for floppy jne label_sector inc real_iopb.sector label_sector: mov real_iopb.bufptr_off, offset label_block mov real_iopb.bufptr_base, ds mov real_iopb.count_lo, 512 ; Set count call init_io ; Do the read not_ready_1: jnz not_ready ; cmp real_iopb.device, 3 ; check for winchester je floppy_label cmp word ptr label_drtab, 0 ; see if there is drtab info jz no_label_info ; if not, punt mov real_iopb.bufptr_off, offset label_drtab mov real_iopb.funct, FUNCT_INIT call init_io ; Do re-init jnz not_ready_1 mov al, label_drtab.nsec ; nsec is needed in real_drtab later mov real_drtab.nsec, al mov al, init_unit ; selected unit mov ah, label_drtab.nfsur ; number of heads cmp al, 4 ; Check for removable jb heads_fr_label add al, 10H-4 mov ah, label_drtab.nrsur heads_fr_label: mov num_heads, ah mov ax, label_gran ; Pick device granularity from label jmp set_count ; floppy_label: test label_flags, VF_AUTO ; Check for valid label info jnz get_label_info ; If not, punt (single density) no_label_info: mov ax, real_drtab.secsize ; No label info, get sector size jmp set_count ; and return ; get_label_info: mov ax, label_gran ; Pick up device granularity from label test label_flags, VF_DOUBLE_SIDED ; Double sided? jz check_8inch inc num_heads ; fix number of heads check_8inch: test label_flags, VF_MINI ; Minifloppy? jnz set_count lea si, drtab_218 call do_init mov real_iopb.device, 1 ; change device code to 8 inch mov real_drtab.ncyl, 77 ; set no. cylinders mov real_drtab.nsec, 26 ; set no. sectors ; set_count: mov real_iopb.count_lo, ax ; init_ret: mov devgran, ax ; save copy label_gran mov bl, label_flags mov flags, bl ; save copy of label_flags pop ds pop bp ; clean off stack ret 2 ; and return ; deviceinitmscgen endp ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; TITLE: devicereadmscgen ; ;******************************************************************** ;* * ;* This is the only function that must be changed in order to work * ;* in protected mode. * ;* * ;******************************************************************** ; ; CALL: CALL devicereadmscgen(unit, blk$num, buf$p); ; ; INTERFACE VARIABLES: ; unit unit number (word) ; blk$num block number (dword) ; buf$p buffer pointer ; ; CALLS: init_io ; ; ABSTRACT: Sets up read command and sends to 215. ; Retries several times if there is an error. ; ; Assumes that unit number has not changed since last ; device$init$msc call, which left the unit in this_unit. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; read_unit equ word ptr [bp+14] ; unit number argument blk_num equ dword ptr [bp+10] ; block number buf_p equ [bp+6] ; buffer pointer buf_p_off equ [bp+6] ; buffer pointer offset buf_p_base equ [bp+8] ; buffer pointer base old_cs equ word ptr [bp+4] ; ptr_form struc ; pointer structure off dw ? base dw ? ptr_form ends ; devicereadmscgen proc far ; Device Read push bp ; mark stack mov bp, sp push ds ; get local ds ; ;****************************************************************** ;* * ;* First change to the real mode driver: DS is taken from AX * ;* register right when we enter. * ;* * ;****************************************************************** ; mov ds, ax ; ;****************************************************************** ;* End of first change * ;****************************************************************** ; ;****************************************************************** ;* * ;* Second change: Since the first Stage driver happend to call a * ;* far procedure (TRANSLATE$FLOPPY), and since this proc is called* ;* by other routines (like the iSBC mscg driver), it was linked to* ;* the 3'rd Stage code (named as FAR_CALL) and its address is in * ;* the local variable TRANS_FLOP. Now we check: * ;* if the 286 is in real mode, then the pointer in TRANS_FLOP * ;* (produced by R&L 86) is correct, and the call will be through a* ;* valid pointer. If the 286 is in protected mode, we have a valid* ;* CS of the third stage on stack (the 3rd stage is the program * ;* that calls devicereadmscgen, and since it is a far call we have* ;* a long return adress on top of stack). * ;* So we take it, and write it over the selector of FAR_CALL * ;* (located in offset TRANS_FLOP + 2, in the local data segment). * ;* Now we have a valid selector when we call this far proc. * ;* The same technique may be applied to long pointers of any kind,* ;* provided we know which segment they point to. We just have to * ;* remember that there are only 5 valid selectors we can use in * ;* devicereadmscgen while in protected mode. The following table * ;* describes them, and where to find each of them. * ;* * ;* ____________________________________________________________ * ;* | | | * ;* | SEGMENT NAME | WHERE TO FIND ITS SELECTOR | * ;* |_______________|____________________________________________| * ;* | stack | in SS register | * ;* | data | on stack, was pushed 2 instructions ago | * ;* | code | on stack, can be refered as old_cs | * ;* | code_bsmsc | in CS register | * ;* | data_bsmsc | in DS and AX registers | * ;* |_______________|____________________________________________| * ;* * ;****************************************************************** ; db 0fh,01h,0e0h ; smsw ax and ax, 01h ; see if protected mode jz real ; no, it isn't mov ax, old_cs ; update the pointer to far_call procedure mov word ptr trans_flop + 2, ax; ; ;****************************************************************** ;* End of second change * ;****************************************************************** ; ;****************************************************************** ;* * ;* Third cange: "LES BX, BUF_P" was replaced here. One can't "LES"* ;* a real mode pointer when in protected mode. So the base is * ;* copied to REAL_IOPB.BUFPTR_BASE through BX instead of ES * ;* register. This will do for real and protected mode as well. * ;* * ;****************************************************************** ; real: mov bx, buf_p_base ; Pick up buffer pointer mov real_iopb.bufptr_base, bx ; ;****************************************************************** ;* End of third change * ;****************************************************************** ; mov scratch.count, 0 ; init count mov bx, buf_p_off mov real_iopb.bufptr_off, bx ; read_loop: mov ax,[bp+10] ; Pick up block number mov cx,[bp+12] test label_flags, VF_AUTO ; See if ADCR flags valid jz no_ADCR test label_flags, VF_NOT_FLOPPY ; floppy disk? jnz no_ADCR ; ; change = translate_floppy(blk_num, @real_drtab, @real_iopb.cyl, @scratch) ; push ds push cx push ax ; BEGIN R4 FIX - support 9 sectors/track ; mov bx, offset label_gran ; get ready to index to sectors/track add bx, 24 ; ds:bx points to sectors/track mov cl, ds: byte ptr[bx] mov real_drtab.nfsur, cl ; place sectors/track in nfsur ; ; END R4 FIX ; lea ax, real_drtab push ds push ax lea ax, real_iopb.cyl push ds push ax lea ax, scratch push ds push ax call trans_flop pop ds ; ; ; BEGIN R4 FIX - support 9 sectors/track ; mov real_drtab.nfsur, 0 ; assume nfsur will always be 0 for floppy ; ; END R4 FIX ; or al, al ; characteristics changed? jz do_read ; ; re-init drive for this track ; push real_iopb.bufptr_off push real_iopb.bufptr_base mov real_iopb.bufptr_off, offset real_drtab mov real_iopb.bufptr_base, ds mov real_iopb.funct, FUNCT_INIT call init_io pop real_iopb.bufptr_base pop real_iopb.bufptr_off jmp short do_read no_ADCR: ; ; get track, sector and side ; from block number ; mov dx, cx mov bl, real_drtab.nsec ; Split out sector xor bh, bh div bx push dx xor dx, dx mov bl, num_heads ; Split head and cylinder div bx mov real_iopb.cyl, ax mov real_iopb.head, dl pop ax inc al ; preadjust cmp BYTE PTR real_iopb.device, 1 ; Check for 8" floppy je no_adjust cmp BYTE PTR real_iopb.device, 3 ; Check for 5 1/4" floppy je no_adjust dec al no_adjust: mov real_iopb.sector, al ; do_read: mov cx, MAX_RETRIES mov real_iopb.funct, FUNCT_READ retry_loop: call init_io ; do read command jz check_count ; return if good loop retry_loop ; retry for a while ; ;******************************************************************* ;* * ;* Forth change: The first stage error routine can not be called in* ;* protected mode, so the call is commented out, and we have int 3 * ;* * ;******************************************************************* mov cx, io_error ; error code push cx ; call bserror ; does not return int 3 ; ;******************************************************************* ;* End of forth change * ;******************************************************************* ; ; check_count: cmp scratch.count, 0 ; all bytes xferred? je read_ret jmp read_loop read_ret: pop ds pop bp ; fix stack ret 10 ; return ; devicereadmscgen endp ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; TITLE: bstime ; ; CALLING SEQUENCE: ; CALL bstime (delay) ; ; INTERFACE VARIABLES: ; delay - number of 10 ms periods to delay ; ; ABSTRACT: provides an N * 10 ms delay ; ; ALGORITHM: ; while input delay time > 0 ; while configured constant > 0; ; end ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; delay_time equ [bp+4] bstime proc near push bp mov bp, sp mov cx, delay_time ; get the delay into cx outer: mov bx, delay_constmsc inner: dec bx jnz inner loop outer pop bp ret 2 bstime endp ; code_bsmsc ends ; end