Toolkit C per i device driver - listati

DDSEGCOS.ASI 

; DDSEGCOS.ASI - Barninga Z! - 1994
;
; ASSEMBLER INCLUDE FILE PER DEVICE DRIVER TOOLKIT
;
; dichiarazione dei segmenti di codice
;
; Le righe che seguono dichiarano i segmenti di codice del device driver e 
; stabiliscono l'ordine in cui essi devono essere presenti nel file binario. Quasi
; tutte le definizioni sono riprese dallo startup module del TINY MODEL e riportate
; NEL MEDESIMO ORDINE. Alcune di esse non hanno utilita' nel caso dei device driver
; ma devono essere ugualmente riportate per evitare errori di compilazione o di
; runtime.

; Segmento del codice eseguibile

_TEXT segment byte public 'CODE' ; da startup code
_TEXT ends

; Segmento dati inizializzati

_DATA segment word public 'DATA' ; da startup code
_DATA ends

; Segmento dati costanti

_CONST SEGMENT WORD PUBLIC 'CONST' ; da startup code
_CONST ENDS

; Segmenti di riferimento

_CVTSEG SEGMENT WORD PUBLIC 'DATA' ; da startup code
_CVTSEG ENDS

_SCNSEG SEGMENT WORD PUBLIC 'DATA' ; da startup code
_SCNSEG ENDS

; Segmento dati non inizializzati

_BSS segment word public 'BSS'
_BSS ends

; Segmento che inizia al termine del segmento _BSS (e' una definizione dummy che
; consente di identificare con facilita' la fine del segmento _BSS.

_BSSEND SEGMENT BYTE PUBLIC 'BSSEND' ; da startup code
_BSSEND ENDS

; Segmenti definiti per la gestione delle operazioni di inizializzazione e
; terminazione del programma.

_INIT_ SEGMENT WORD PUBLIC 'INITDATA' ; da startup code
_INIT_ ENDS

_INITEND_ SEGMENT BYTE PUBLIC 'INITDATA' ; da startup code
_INITEND_ ENDS

_EXIT_ SEGMENT WORD PUBLIC 'EXITDATA' ; da startup code
_EXIT_ ENDS

_EXITEND_ SEGMENT BYTE PUBLIC 'EXITDATA' ; da startup code
_EXITEND_ ENDS

; Segmento definito appositamente per i device driver. Consente di conoscere con
; facilita' l'indirizzo di fine codice.

_DRVREND segment byte public 'DRVREND'
_DRVREND ends

;------------------------------------------------------------------------------ 

; I segmenti sono tutti quanti inseriti nel gruppo DGROUP, secono lo schema del
; tiny model, che prevede un unico segmento di RAM (64 Kb) per tutti i segmenti
; del sorgente (i loro indirizzi si differenziano nell'offset). La direttiva ASSUME 
; indica all'assemblatore che durante l'esecuzione tutti i registri di segmento 
; verranno inizializzati con l'indirizzo di DGROUP; cio' permette all'assemblatore 
; di calcolare correttamente gli offsets di tutti gli indirizzamenti nel listato.

DGROUP group _TEXT, \
_DATA, \
_CVTSEG, \
_SCNSEG, \
_BSS, \
_BSSEND, \
_INIT_, \
_INITEND_, \
_EXIT_, \
_EXITEND_, \
_DRVREND \

assume cs : DGROUP, \
ds : DGROUP, \
ss : DGROUP, \
es : DGROUP \

;------------------------------------------------------------------------------ 

; Seguono le definizioni di alcune costanti manifeste. Importante e' la STKSIZE,
; che definisce l'ampiezza iniziale dello stack locale del device driver. Circa
; la gestione dello stack vedere la funzione setStack()

STKSIZE equ 512 ; dimensione in bytes dello stack locale
; 512 bytes e' il MINIMO possibile

CMDSIZE equ 128 ; max lunghezza cmd line 

SYSINIT_KB equ 96 ; Kb riservati da Top of Mem per SYSINIT

; Seguono le definizioni delle costanti manifeste per la gestione della status
; word del request header.

; Codici di status da restituire al DOS in OR tra loro. Modificano i bits del
; byte piu' significativo della status word.

S_SUCCESS equ 0000h
S_ERROR equ 8000h ; usare in caso di errore 
S_BUSY equ 0200h
S_DONE equ 0100h

; Codici di ritorno in OR coi precedenti. Indicano lo stato con cui si e' chiuso
; il servizio richiesto dal DOS (byte meno significativo della status word).

E_OK equ 0000h
E_WR_PROTECT equ 0 
E_UNKNOWN_UNIT equ 1
E_NOT_READY equ 2
E_UNKNOWN_CMD equ 3 ; richiesto servizio non implementato
E_CRC equ 4
E_LENGTH equ 5
E_SEEK equ 6
E_UNKNOWN_MEDIA equ 7
E_SEC_NOTFOUND equ 8
E_OUT_OF_PAPER equ 9
E_WRITE equ 10
E_READ equ 11
E_GENERAL equ 12

; Costante manifesta per la gestione dello stack. Ha un fondamentale ruolo
; nell'implementazione del meccanismo che consente al device driver di
; modifcare la dimensione dello stack iniziale in fase di inizializzazione. 
; Vedere setStack().

; SE SI INTENDE MODIFICARE LA GESTIONE DELLO STACK TRA L'ISTRUZIONE CHE IN
; driverInit() COPIA SP IN __savSP1 E QUELLA CHE IN setStack() COPIA SP IN __savSP2,
; LE COSTANTI MANIFESTE CHE SEGUONO DEVONO ESSERE MODIFICATE DI CONSEGUENZA!!

NPA_SP_DIFF equ 8 ; differenza tra SP prima delle PUSH dei parms 
; di main() e SP in ingresso a setStack() nel
; caso di main(void) e senza variabili auto. 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

Lo startup module 

; DDHEADER.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - STARTUP
;
;
; device driver STARTUP MODULE. Da linkare in testa all'object prodotto dal
; compilatore e contenente la reale implementazione C del driver. 

; DDHEADER.ASM implementa il device driver header, e le strategy e interrupt
; routines del device driver. Sono inoltre inserite incluse alcune routines di
; inizializzazione e le dichiarazioni delle variabili globali (alcune delle quali
; non pubblicate e quindi non visibili in C) per il supporto delle librerie
; C, in base allo startup module dei normali programmi. Si noti che i nomi C
; devono ignorare l'underscore (o il primo degli underscore) in testa ai nomi
; assembler.

include DDSEGCOS.ASI ; definizioni dei segmenti!!!

org 0 ; no PSP: e' un device driver

; Dichiarazione dei simboli esterni: sono dichiarate le variabili e le funzioni
; referenziate ma non definite in questo sorgente. Tutte le funzioni sono near
; dal momento che si lavora con il modello TINY.

extrn __stklen : word ; da libreria C: gestione stack.
; Vedere setStack().

; seguono le dichiarazioni delle funzioni che gestiscono i vari servizi del device
; driver. La loro implementazione e' libera, ma il nome nel sorgente C deve essere
; identico a quello qui riportato (salvo l'underscore iniziale). Se il sorgente C
; non implementa una o piu' delle seguenti funzioni, all'eseguibile viene linkata
; la corrispondente dummy function inclusa nella libreria
; Fa eccezione _driverInit() (DDINIT.ASM), che implementa il servizio INIT standard
; e chiama la init() definita dal programmatore nel sorgente C. 

extrn _driverInit : near
extrn _mediaCheck : near ; servizi del driver, da 
extrn _buildBPB : near ; implementare in C. Le 
extrn _inputIOCTL : near ; implementazioni assembler in 
extrn _input : near ; libreria servono solo come 
extrn _inputND : near ; placeholders per quelle non 
extrn _inputStatus : near ; realizzate in C e non fanno che 
extrn _inputFlush : near ; chiamare errorReturn(), definita 
extrn _output : near ; in questo stesso sorgente
extrn _outputVerify : near 
extrn _outputStatus : near 
extrn _outputFlush : near 
extrn _outputIOCTL : near 
extrn _deviceOpen : near 
extrn _deviceClose : near 
extrn _mediaRemove : near 
extrn _outputBusy : near 
extrn _genericIOCTL : near
extrn _getLogicalDev : near
extrn _setLogicalDev : near
extrn _endOfServices : near

;------------------------------------------------------------------------------ 

_TEXT segment ; inizio del segmento _TEXT (codice)

;------------------------------------------------------------------------------ 

; Il codice (segmento _TEXT) - e quindi il file binario - inizia con il
; device driver header. Notare il primo campo (4 bytes) posto a -1 (FFFFFFFFh).
; La device attribute word (secondo campo) e il logical name (ultimo campo)
; sono "per default" posti a 0 e, rispettivamente, 8 spazi. Dovranno essere
; settati dopo il linking, utilizzando la utility DRVSET
; E' fondamentale che il device driver header sia il primo oggetto definito
; nel segmento del codice.

public _DrvHdr ; header del device driver
_DrvHdr dd -1
dw 0 ; DA SETTARE CON DRVSET
dw offset _TEXT:_Strategy
dw offset _TEXT:_Interrupt
db 8 dup(32) ; DA SETTARE CON DRVSET

;----------------------------------------------------------------- 

; Tabella dei servizi del device driver. E' utilizzata dalla interrupt routine
; (listata poco piu' avanti) per individuare la funzione da chiamare a seconda
; del servizio richiesto dal DOS. Il posizionamento della tabella all'inizio del
; modulo forza l'assemblatore a referenziare per prime le funzioni dei servizi
; (solo Strategy() e Interrupt() sono referenziate prima di esse, nello header):
; in tal modo esse sono ricercate per prime nelle librerie (se non definite nel
; sorgente) e linkate all'eseguibile prima di tutte le funzioni di libreria C.
; Cio' permette l'utilizzo di _endOfServices(), come dichiarata al termine della
; tabella stessa.

_FuncTab dw offset _TEXT:_driverInit ; 0
dw offset _TEXT:_mediaCheck ; 1
dw offset _TEXT:_buildBPB ; 2
dw offset _TEXT:_inputIOCTL ; 3
dw offset _TEXT:_input ; 4
dw offset _TEXT:_inputND ; 5
dw offset _TEXT:_inputStatus ; 6
dw offset _TEXT:_inputFlush ; 7
dw offset _TEXT:_output ; 8
dw offset _TEXT:_outputVerify ; 9
dw offset _TEXT:_outputStatus ; 10 A
dw offset _TEXT:_outputFlush ; 11 B
dw offset _TEXT:_outputIOCTL ; 12 C
dw offset _TEXT:_deviceOpen ; 13 D
dw offset _TEXT:_deviceClose ; 14 E
dw offset _TEXT:_mediaRemove ; 15 F
dw offset _TEXT:_outputBusy ; 16 10 
dw offset _TEXT:_unSupported ; 17 11 
dw offset _TEXT:_unSupported ; 18 12 
dw offset _TEXT:_genericIOCTL ; 19 13 
dw offset _TEXT:_unSupported ; 20 14 
dw offset _TEXT:_unSupported ; 21 15 
dw offset _TEXT:_unSupported ; 22 16 
dw offset _TEXT:_getLogicalDev ; 23 17 
dw offset _TEXT:_setLogicalDev ; 24 18 

; E' dichiarata una label (etichetta) il cui indirizzo (offset) indica la fine
; della tabella delle funzioni di servizio.

public __endOfSrvc
__endOfSrvc label word ; off DGROUP:fine f() servizi

; E' dichiarata una funzione dummy inserita in libreria dopo tutte le funzioni
; di servizio: il suo indirizzo indica, nel file binario, la fine del codice
; eseguibile delle funzioni di servizio. Vedere anche endOfServices()

dw offset _TEXT:_endOfServices ; dummy func per segnare
; indirizzo fine ultima
; f() di servizio. 

;----------------------------------------------------------------- 

; L'espressione $-_FuncTab-2 calcola la distanza (in bytes) tra l'inizio della
; tabella dei servizi e l'indirizzo attuale, meno due bytes. In pratica si ottiene
; la dimensione della tabella dei puntatori alle funzioni di servizio e, dal momento
; che ogni puntatore occupa due bytes (tutti puntatori near), __FuncIDX__ puo'
; fungere da indice per individuare la funzione da chiamare a seconda del servizio
; richiesto dal DOS.

__FuncIDX__ dw $ - _FuncTab - 2 ; max indice (word off) in tabella 
; il 2 e' per la f() dummy

; Seguono le definizioni di diverse variabili globali. Solo quelle per le quali e'
; specificata la clausola PUBLIC sono visibili da C. Sono definite anche alcune
; labels (etichette) che servono per gestire indirizzi senza utilizzare veri e
; propri puntatori. Tutte le variabili e labels pubblicate al C sono dichiarate
; nell'include file della libreria toolkit BZDD.H

public _RHptr
_RHptr dd 0 ; puntatore far al Request Header

_DosStkPtr dd 0 ; ptr far a DOS Stack (SS:SP ingresso)

public _DrvStk
_DrvStk db STKSIZE dup(0) ; stack locale del driver

public _DrvStkEnd
_DrvStkEnd label word ; fine dello stack (e' solo una label)

; Alcune delle variabili definite di seguito sono descritte e commentate in un
; paragrafo dedicato 

; Kb mem conv installati (usata nel codice di inizializzazione per l'int 12h). Si
; tratta di un dato raccolto per comodita' del programmatore. 

public __systemMem
__systemMem dw 0

; Di seguito sono definite un'etichetta ed una variabile. La prima rappresenta un
; sinonimo della seconda e possono essere utilizzate da C come se fossero la stessa
; cosa. La variabile contiene l'indirizzo di segmento al quale e' caricato il
; driver (CS); il sinonimo _psp e' definito per analogia con la libreria C, ma 
; il driver non ha un PSP. Questa e' la parte segmento dell'indirizzo al quale
; si trova il device driver header; percio' RHptr vale _psp:0000 o _baseseg:0000.

public __psp
__psp label word

public __baseseg
__baseseg dw 0 ; segmento di base del DevDrv (CS)

; Di seguito sono definite due variabili di comodita' per il programmatore. I device
; driver non hanno un far heap in cui allocare memoria far: _farMemBase e
; _farMemTop sono gli indirizzi far dell'inizio e, rispettivamente, della fine della
; memoria libera oltre il driver. Va tenuto presente che in quell'area di RAM ci
; sono parti di DOS attive: essa puo' percio' essere usata a discrezione, ma con
; prudenza.

public __farMemBase
__farMemBase dd 0 ; puntatore far alla memoria libera 
; oltre il driver in init()

public __farMemTop
__farMemTop dd 0 ; puntatore far alla fine della memoria
; libera oltre il driver in init(). 

; _cmdLine e' una copia della command line del driver in CONFIG.SYS, a partire dal
; carattere che segue "DEVICE=". In pratica e' un array di char.

public __cmdLine
__cmdLine db CMDSIZE dup(0) ; copia locale della command line 

; _cmdArgsN e _cmdArgs equivalgono a argc e argv. Il massimo numero di puntatori che 
; _cmdArgs puo' contenere e' 64, perche' una command line e' al massimo di 128 
; caratteri e ogni argomento occupa almeno 2 bytes (1 carattere + 1 spazio).

public __cmdArgsN
__cmdArgsN dw 0 ; come argc

public __cmdArgs
__cmdArgs db CMDSIZE dup(0) ; array puntat. ad argom. cmd line 

; Definizione di alcuni puntatori per comodita' del puntatore. Gli indirizzi (near)
; sono calcolati mediante labels definite in coda a questo stesso sorgente.

public __endOfCode
__endOfCode dw offset DGROUP:_ecode@ ; offset (CS:) della fine codice

public __endOfData
__endOfData dw offset DGROUP:_edata@ ; offset (CS:) della fine dati

public __endOfDrvr
__endOfDrvr dw offset DGROUP:_edrvr@ ; offset (CS:) fine spazio driver

; Variabili per la gestione dello stack. Lo stack locale DrvStk e' rilocabile
; (vedere setStack()): qualora esso, a discrezione del
; programmatore, venga effettivamente rilocato, lo spazio di STACKDIM bytes occupato
; inizialmente puo' essere riutilizzato come un normale array di char, il cui
; indirizzo (near) e' _freArea. La sua effettiva dimensione e' _freAreaDim. Le altre
; variabili sono utilizzate da setStack() e sono pubblicate al C con finalita' di
; debugging.

public __freArea
__freArea dw 0 ; offset ex-stack per riutilizzo

public __freAreaDim
__freAreaDim dw 0 ; dimensione ex-stack 

public __savSP1
__savSP1 dw 0 ; SP prima di push parms per init()

public __savSP2
__savSP2 dw 0 ; SP all'ingresso di SetStack()

public __newTOS
__newTOS dw 0 ; offset del nuovo Top Of Stack

; Seguono definizioni di variabili date per analogia con lo startup code dei normali
; programmi C.

public __version
__version label word ; versione e revisione DOS

public __osversion
__osversion label word ; versione e revisione DOS

public __osmajor
__osmajor db 0 ; versione DOS

public __osminor
__osminor db 0 ; revisione DOS

public __StartTime
__StartTime dd 0 ; clock ticks allo startup

public _errno
_errno dw 0 ; codice di errore 

public ___MMODEL
___MMODEL dw 0 ; tiny model

public _DGROUP@
_DGROUP@ dw 0 ; segmento del gruppo DGROUP

; Le variabili definite di seguito sono necessarie alle funzioni di libreria C per
; la gestione dello heap (malloc(), etc.). Quelle relative al far heap non sono
; inizializzate in quanto ai device driver non e' mai possibile effettuare
; allocazioni far mediante farmalloc(). Le allocazioni dello heap sono effettuate
; all'interno dello stack, secondo lo schema del modello TINY. 

public ___heapbase
___heapbase dw offset _DrvStk ; inizio near heap 

public ___brklvl
___brklvl dw offset _DrvStk ; attuale fine near heap

public __heapbase
__heapbase dd 0 ; inizio far heap 
;
public __brklvl ;
__brklvl dd 0 ; inizio far heap 
;
public __heaptop ;
__heaptop dd 0 ; fine far heap 

;----------------------------------------------------------------- 

; Inizia qui il codice eseguibile. Siamo sempre nell'ambito del code segment (il che
; e' normale per le routine, un po' meno per le variabili appena definite). 

; Ecco la strategy routine. Essa non fa altro che salvare l'indirizzo del request
; header, passato dal DOS in ES:BX, nel puntatore far RHptr. Si noti che Strategy()
; deve essere una funzione far; inoltre essa non e' pubblicata al C, in quanto si
; tratta di una routine interna al driver, che non deve mai essere chiamata da C.

_Strategy proc far

mov word ptr cs:[_RHptr],bx ; salva ptr al Request Header
mov word ptr cs:[_RHptr+2],es ; prima offset poi segmento

ret

_Strategy endp

;----------------------------------------------------------------- 

; Ed ecco la interrupt routine: come si vede, ha una struttura semplice. Essa
; attiva lo stack locale e salva i registri, dopodiche' scandisce la tabella
; dei servizi: se il servizio e' 0 (inizializzazione) richiama driverInit(),
; definita oltre in questo sorgente, la quale a sua volta chiama la init() del
; sorgente C. Se il servizio non e' definito chiama errorReturn(), definita oltre
; in questo sorgente, passandole E_UNSUPPORTED quale codice di errore, altrimenti
; chiama la funzione dedicata al servizio. Se il sorgente C implementa una funzione
; con quel nome (vedere la tabella in testa a questo sorgente) e' invocata proprio
; quella funzione, altrimenti e' chiamata la corrispondente funzione dummy della
; libreria toolkit. Al termine delle operazioni, Interrupt() ripristina i registri
; e lo stack DOS e termina restituendo il controllo al sistema. Interrupt(), come
; Strategy(), deve essere far e non e' pubblicata al C, che non la puo' invocare.

_Interrupt proc far

; E' bene che il driver utilizzi un proprio stack, per evitare di
; sottarre risorse al DOS: quindi bisogna modificare SS:SP in modo
; che puntino allo stack del driver e non piu' a quello DOS. E' ovvio
; che l'indirizzo dello stack DOS (SS:SP) deve essere salvato (NON
; SULLO STACK STESSO!) per poterlo ripristinare in uscita. Allo 
; scopo e' usata la variabile DosStkPtr.

mov word ptr cs:[_DosStkPtr],sp ; salva SS:SP (ptr a stack DOS) 
mov word ptr cs:[_DosStkPtr+2],ss ; prima offset poi segmento 

; Lo stack locale viene attivato caricando SS:SP con l'indirizzo 
; della fine (lo stack e' usato dall'alto in basso!) dell'area 
; allo scopo riservata. Percio' in SS e' caricato CS (nel modello 
; TINY il segmento di stack e' lo stesso del codice) e in SP e' 
; caricato l'offset della label DrvStkEnd, che indica la fine di
; DrvStk, l'array di STKSIZE bytes dedicato allo scopo.

mov _DGROUP@,cs ; stack pointers settati allo
mov ss,_DGROUP@ ; stack locale (_DGROUP@ e'
mov sp,offset _DrvStkEnd ; una variabile di comodo)

; A questo punto si puo' usare lo stack locale per salvare tutti i
; registri e generare una standard stack frame, cioe' il settaggio 
; di SS, SP e BP secondo le convenzioni C in ingresso alle funzioni. 

push bp ; genera standard stack frame
mov bp,sp

push ax ; ...e salva tutti i registri
push bx ; senza piu' intaccare lo
push cx ; stack dos
push dx
push si
push di
push ds
push es
pushf

; Tutti i registri di segmento sono uguagliati a CS (tiny model); SS
; e' gia' stato settato.

mov bx,cs
mov ds,bx
mov es,bx

; Qui viene esaminato il servizio richiesto ed e' lanciata la funzione
; corrispondente: il numero di servizio, letto ad offset 2 nel request
; header, e' moltiplicato per 2 (i puntatori alle funzioni sono near e
; percio' ciascuno occupa 2 bytes; quindi in tal modo si ottiene 
; direttamente l'offset nella tabella FuncTab del puntatore alla 
; funzione da lanciare). Se il risultato della moltiplicazione e'
; maggiore di _FuncIDX__ (massimo indice), e' chiamata la funzione 
; unSupported() (definita oltre in questo sorgente), altrimenti si
; salta alla label EXECUTE.

push ds
lds si,DGROUP:_RHptr ; DS:SI punta al Request Header 
mov al,[si+2] ; AL = servizio (campo a offset 2)
pop ds
shl al,1 ; per 2 (offsets in _FuncTab sono words)
xor ah,ah ; AX = offset in termini di words
cmp ax,__FuncIDX__ ; ** MAX VALORE DEL COMMAND BYTE x 2 **
jle EXECUTE
call _unSupported
jmp EXITDRIVER
EXECUTE:

; Il servizio richiesto e' lecito: la coppia DS:SI e' caricata con
; l'indirizzo del puntatore alla funzione opportuna e si procede 
; alla chiamata con una tecnica analoga all'indirezione di puntatore 
; a funzione.

mov si,offset _FuncTab
add si,ax ; DS:SI punta al puntat. a funz. in _FuncTab
call word ptr [si] ; chiama funzione void f(void)
EXITDRIVER:

; Alla fine delle operazioni Interrupt() setta a 1 il bit 8 della 
; status word nel request header: si presume che tutte le funzioni 
; dedicate ai servizi restituiscano (e percio' il compilatore lo
; carichera' in AX) il valore della status word stessa. 

or ax,S_DONE ; segnala fine operazioni 
push ds
lds si,DGROUP:_RHptr ; DS:SI punta al Request Header 
mov [si+3],ax ; valorizza lo STATUS di ritorno
pop ds

; Uscita da Interrupt(): tutti i registri sono ripristinati, viene 
; eliminata la standard stack frame ed e' ricaricato in SS:SP 
; l'indirizzo dello stack DOS prelevandolo da DosStkPtr. La funzione 
; termina con una normale RET (far, dato che Interrupt() e' dichiarata 
; tale) e NON con una IRET.

popf ; ripristina tutti i registri 
pop es ; estraendone i valori di ingresso
pop ds ; dallo stack locale
pop di
pop si
pop dx
pop cx
pop bx
pop ax

pop bp ; rispristina BP (standard stack frame)

mov sp,word ptr cs:[_DosStkPtr] ; ripristina SS:SP (ora puntano 
mov ss,word ptr cs:[_DosStkPtr+2] ; nuovamente allo stack DOS) 

ret ; non e' un interrupt! 

_Interrupt endp

;----------------------------------------------------------------- 

; La funzione errorReturn() e' pubblicata al C e deve essere usata per segnalare
; che un servizio e' terminato in errore. Accetta come parametro un intero il cui
; byte meno significativo rappresenta il codice di errore e lo restituisce dopo
; avere settato a 1 il bit 15. Vedere la status word e DDSEGCOS.ASI.

public _errorReturn ; f() per restituzione errore
_errorReturn proc near ; int errorReturn(int errcod); 
; procedura per restituzione codice
push bp ; di errore secondo costanti manifeste
mov bp,sp ; Il parm e' referenziato [BP+4] infatti
mov ax,[bp+4] ; [BP+0] = BP e [BP+2] = IP (per la ret)
or ax,S_ERROR ; setta bit di errore
pop bp

ret

_errorReturn endp

;----------------------------------------------------------------- 

; La funzione unSupported(), pubblicata al C, e' dedicata a restituire lo stato di
; errore per servizio non supportato. Non fa altro che chiamare errorReturn() con
; l'appropriato codice di errore.

public _unSupported ; f() per servizio non supportato
_unSupported proc near ; int unSupported(void);

mov ax,E_UNKNOWN_CMD
push ax
call _errorReturn ; restituisce codice errore 
add sp,2

ret

_unSupported endp

;------------------------------------------------------------------------------ 

_TEXT ends ; Fine del segmento di codice

;------------------------------------------------------------------------------ 

; Labels pubbliche per individuare gli offsets dei segmenti (non possono
; essere dichiarate in DDSEGCOS.ASI) perche' devono essere dichiarate una
; volta soltanto.

;----------------------------------------------------------- 

_DATA segment word public 'DATA'
public _ecode@ ; fine codice (_TEXT)
_ecode@ label byte
_DATA ends

;----------------------------------------------------------- 

_CVTSEG SEGMENT WORD PUBLIC 'DATA' ; da startup code
public __RealCvtVector
__RealCvtVector label word
_CVTSEG ENDS

;----------------------------------------------------------- 

_SCNSEG SEGMENT WORD PUBLIC 'DATA' ; da startup code
public __ScanTodVector
__ScanTodVector label word
_SCNSEG ENDS

;----------------------------------------------------------- 

_BSS segment word public 'BSS' ; da startup code
public _bdata@ ; inizio BSS
_bdata@ label byte 
_BSS ends

;----------------------------------------------------------- 

_BSSEND SEGMENT BYTE PUBLIC 'BSSEND' ; da startup code
public _edata@ ; fine BSS
_edata@ label byte
_BSSEND ENDS

;----------------------------------------------------------- 

_DRVREND segment byte public 'DRVREND'
public _edrvr@ ; fine driver
_edrvr@ label byte
_DRVREND ends

;------------------------------------------------------------------------------ 

end 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

driverInit() 

; DDINIT.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione driverInit() per l'inizializzazione standard del device driver

include ddsegcos.asi

; dichiarazione di init(). Equivale alla main() dei normali programmi C, assente
; nei device driver. E' richiamata dalla interrupt routine quando il DOS
; richiede il servizio 0, cioe' al caricamento del driver. Tutte le operazioni
; di inizializzazione devono percio' essere gestite nella init(), che deve essere
; presente in tutti i device driver scritti utilizzando il toolkit. 
; Ovviamente non deve esserci main().

extrn _init : near ; user defined! E' la "main()" del 
; device driver.

; Dichiarazione della funzione di libreria toolkit setupcmd(), che
; effettua la scansione della command line e genera argc e argv per la init().

extrn _setupcmd : near
; Altri simboli esterni dallo startup module

extrn __version : word
extrn __stklen : word
extrn __baseseg : word
extrn __systemMem : word
extrn __farMemTop : dword
extrn __farMemBase : dword
extrn __StartTime : dword
extrn __cmdArgs : word
extrn __cmdArgsN : word
extrn __savSP1 : word
extrn _DGROUP@ : word

extrn _edrvr@ : byte
extrn _bdata@ : byte
extrn _edata@ : byte

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

; driverInit() e' il vero e proprio startup code del driver. Dal momento che il
; suo indirizzo si trova ad offset 0 nella FuncTab (e' il primo puntatore nella
; tabella) essa e' chiamata da Interrupt() in corrispondenza del servizio 0 e
; procede alla inizializzazione del driver. driverInit() si occupa delle operazioni
; di inizializzazione standardizzate per tutti i driver: al termine di queste 
; chiama init(), che deve essere definita dal programmatore nel sorgente C, la
; quale esegue le operazioni peculiari per quel driver. Il programmatore C deve
; gestire l'inizializzazione dedicata allo specifico driver esclusivamente con
; init().

public _driverInit
_driverInit proc near ; driverInit(): simula parte dello startup code 

mov ah,30h
int 21h ; richiede versione dos
mov __version,ax ; e la salva 

mov __stklen, STKSIZE ; dimensione stack

mov word ptr __baseseg,cs ; segmento di caricamento

; L'algoritmo che segue non trova corrispondenza nello startup code
; dei normali programmi. Viene calcolato EMPIRICAMENTE il confine 
; superiore della memoria libera oltre il driver: in caso di problemi 
; può essere necessario aumentare il valore di SYSINIT_KB, costante
; manifesta definita in DDSEGCOS.ASI, che esprime il numero di Kb
; riservati alla routine SYSINIT del DOS. E' poi
; individuato l'offset della fine dello spazio occupato dal driver e,
; a partire da quello, l'indirizzo far dell'inizio della memoria 
; libera oltre il driver. Lo spazio tra gli indirizzi far _farMemBase 
; e _farMemTop e' a disposizione del driver per qualsiasi utilizzo, 
; purche' limitato alla sola fase di inizializzazione, in quanto 
; quella memoria sara' successivamente usata dal DOS (e non e' 
; comunque gestibile via farmalloc(), etc.).

int 12h ; Kb RAM instal. (AX); <= 640
mov __systemMem,ax ; salva valore restituito
sub ax,SYSINIT_KB ; protegge spazio per SYSINIT
mov cx,6
shl ax,cl ; Kb * 64 = Seg esa
mov word ptr __farMemTop+2,ax ; top della memoria far libera

mov ax,offset DGROUP:_edrvr@ ; offset fine spazio driver
mov cx,4
shr ax,cl ; off / 16 = normalizzazione
inc ax ; annulla arrotondamento 
add ax,word ptr _DGROUP@ ; segmento normalizzato 
mov word ptr __farMemBase+2,ax ; base far free mem e' il primo 
; seg:0000 oltre il driver

; Seguono nuovamente operazioni derivate dal normale startup code. 

mov ah,0
int 1ah ; BIOS time ticks
mov word ptr __StartTime,dx ; per la funzione C clock()
mov word ptr __StartTime+2,cx
or al,al ; midnight flag settato?
jz NOT_MIDNIGHT
push es
mov ax,40h ; setta BIOS midnight flag
mov es,ax ; all'indirizzo 0040:0070
mov bx,70h
mov byte ptr es:[bx],1
pop es

NOT_MIDNIGHT:

mov di,offset DGROUP:_bdata@ ; da startup code: azzera area
mov cx,offset DGROUP:_edata@ ; BSS. (ES = CS = DGROUP)
sub cx,di ; CX = dist. _bdata@-_edata@ 
xor ax,ax
cld
rep stosb

; A questo punto e' chiamata la funzione di libreria toolkit
; setupcmd(), che effettua la scansione della command line
; data in CONFIG.SYS e setta _cmdArgsN e _cmdArgs, equivalenti a argc
; e, rispettivamente, argv.

call _setupcmd ; scansione command line
mov bx,offset __cmdArgs
push bx ; analogo ad argv 
push __cmdArgsN ; analogo ad argc 

mov __savSP1,sp ; per setStack(): deve essere
; l'ultima prima di call _init

; Infine e' chiamata init(): il controllo dell'inizializzazione passa
; al sorgente C.

call _init ; int init(...) USER DEFINED

add sp,4 ; pulizia stack 

; Al termine dell'inizializzazione sono (per prudenza) azzerati i
; puntatori all'inizio e alla fine della memoria far disponibile oltre
; il driver, dal momento che, come sottolineato poco fa, dopo la fase
; di bootstrap essa non e' piu' disponibile.

mov word ptr __farMemBase,0 ; dopo init() non ha piu'
mov word ptr __farMemBase+2,0 ; alcun significato 
mov word ptr __farMemTop,0 ; dopo init() non ha piu'
mov word ptr __farMemTop+2,0 ; alcun significato 

ret ; torna a Interrupt() 

_driverInit endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

Funzioni di gestione dei servizi non implementati 

; DDMEDCHE.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione mediaCheck() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _mediaCheck
_mediaCheck proc near ; int mediaCheck(void); 

call _unSupported
ret

_mediaCheck endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDBUIBPB.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione buildBPB() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _buildBPB
_buildBPB proc near ; int buildBPB(void); 

call _unSupported
ret

_buildBPB endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDINPIOC.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione inputIOCTL() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _inputIOCTL
_inputIOCTL proc near ; int inputIOCTL(void); 

call _unSupported

_inputIOCTL endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDINPUT.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione input() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _input
_input proc near ; int input(void); 

call _unSupported
ret

_input endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDINPND.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione inputND() dummy usata solo se non implementata in C 

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _inputND

_inputND proc near ; int inputND(void); 

call _unSupported
ret

_inputND endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDINPSTA.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione inputStatus() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _inputStatus
_inputStatus proc near ; int inputStatus(void); 

call _unSupported
ret

_inputStatus endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDINPFLU.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione inputFlush() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _inputFlush
_inputFlush proc near ; int inputFlush(void); 

call _unSupported
ret

_inputFlush endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDOUTPUT.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione output() dummy usata solo se non implementata in C 

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _output
_output proc near ; int output(void); 

call _unSupported
ret

_output endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDOUTVER.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione outputVerify() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _outputVerify
_outputVerify proc near ; int outputVerify(void); 

call _unSupported
ret

_outputVerify endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDOUTSTA.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione outputStatus() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _outputStatus
_outputStatus proc near ; int outputStatus(void); 

call _unSupported
ret

_outputStatus endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDOUTFLU.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione outputFlush() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _outputFlush
_outputFlush proc near ; int outputFlush(void); 

call _unSupported
ret

_outputFlush endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDOUTIOC.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione outputIOCTL() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _outputIOCTL
_outputIOCTL proc near ; int outputIOCTL(void); 

call _unSupported
ret

_outputIOCTL endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDDEVOPE.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione deviceOpen() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _deviceOpen
_deviceOpen proc near ; int deviceOpen(void); 

call _unSupported
ret

_deviceOpen endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDDEVCLO.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione deviceClose() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _deviceClose
_deviceClose proc near ; int deviceClose(void); 

call _unSupported
ret

_deviceClose endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDMEDREM.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione mediaRemove() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _mediaRemove
_mediaRemove proc near ; int MediaRemove(void); 

call _unSupported
ret

_mediaRemove endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDOUTBUS.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione outputBusy() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _outputBusy
_outputBusy proc near ; int outputBusy(void); 

call _unSupported
ret

_outputBusy endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDGENIOC.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione genericIOCTL() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _genericIOCTL
_genericIOCTL proc near ; int genericIOCTL(void); 

call _unSupported
ret

_genericIOCTL endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDGETLOG.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione getLogicalDev() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _getLogicalDev
_getLogicalDev proc near ; int getLogicalDev(void); 

call _unSupported
ret

_getLogicalDev endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDSETLOG.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione setLogicalDev() dummy usata solo se non implementata in C

include ddsegcos.asi

extrn _unSupported : near

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _setLogicalDev
_setLogicalDev proc near ; int setLogicalDev(void); 

call _unSupported
ret

_setLogicalDev endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

endOfServices() 

; DDENDOFS.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; funzione endOfServices() dummy usata solo per calcolare l'indirizzo al
; quale termina l'ultima delle f() dummy di servizio. non e' mai eseguita

include ddsegcos.asi

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _endOfServices
_endOfServices proc near

ret

_endOfServices endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

Funzioni di compatibilità con il compilatore C 

; DD_EXPTR.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; procedure dummy che i normali programmi usano per uscire a DOS o
; resettare vettori ed effettuare il flush degli streams: dati 
;

include DDSEGCOS.ASI

extrn _dummy : near ; dummy() e' definita in DDDUMMY.ASM

;------------------------------------------------------------------------------ 

_DATA segment

;------------------------------------------------------------------------------ 

; puntatori a funzioni di cleanup per streams e file

;-------------------------------------------------------------- 

public __exitbuf
__exitbuf dw offset _TEXT:_dummy

public __exitfopen
__exitfopen dw offset _TEXT:_dummy

public __exitopen
__exitopen dw offset _TEXT:_dummy

;------------------------------------------------------------------------------ 

_DATA ends

;------------------------------------------------------------------------------ 

end
; DD_VECT.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;

include DDSEGCOS.ASI

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

_Int0Vector dd 0 ; area locale memorizzazione 
_Int4Vector dd 0 ; vettori per SaveVectors() e
_Int5Vector dd 0 ; _restorezero() 
_Int6Vector dd 0 ;

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDDUMMY.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; procedure dummy che i normali programmi usano per uscire a DOS o
; resettare vettori ed effettuare il flush degli streams
;

include DDSEGCOS.ASI

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

; labels per nomi funzioni di uscita e cleanup: puntano
; tutte a __dummy__proc, che segue solo una RET 

;-------------------------------------------------------------- 

public dummy ; static dummy
dummy label

;-------------------------------------------------------------- 

public _abort ; abort()
_abort label

;-------------------------------------------------------------- 

public _atexit ; atexit()
_atexit label

;-------------------------------------------------------------- 

public _dummy ; static dummy
_dummy label

;-------------------------------------------------------------- 

public _exit ; exit()
_exit label

;-------------------------------------------------------------- 

public _keep ; keep()
_keep label

;-------------------------------------------------------------- 

public __cexit ; _cexit()
__cexit label

;-------------------------------------------------------------- 

public __checknull ; asm
__checknull label

;-------------------------------------------------------------- 

public __cleanup ; asm
__cleanup label

;-------------------------------------------------------------- 

public __c_exit ; _c_exit()
__c_exit label

;-------------------------------------------------------------- 

public __dos_keep ; _dos_keep()
__dos_keep label

;-------------------------------------------------------------- 

public __exit ; _exit()
__exit label

;-------------------------------------------------------------- 

public __terminate ; asm
__terminate label

;-------------------------------------------------------------- 

public ___EXIT ; pascal ___exit() 
___EXIT label

;-------------------------------------------------------------- 

public ___exit ; pascal ___exit() 
___exit label

;------------------------------------------------------------------------------ 

__dummy__proc PROC near ; funzione dummy: esegue solo RET

ret

__dummy__proc ENDP

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end
; DDRESVEC.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; procedure per la gestione dei vettori int 0, 4, 5, 6 e del gestore 
; dell'int 0 (divide by zero). Nei normali programmi al ritorno a DOS
; e' chiamata _restorezero(), che ripristina detti vettori, salvati da
; SaveVectors() e modificati da alcune funzioni di libreria C. Il device
; driver deve invocare esplicitamente SaveVectors() e _restorezero() 
; se necessario.
;

include DDSEGCOS.ASI

extrn _Int0Vector : dword ; definiti in DD_VECT.ASM
extrn _Int4Vector : dword
extrn _Int5Vector : dword
extrn _Int6Vector : dword

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public __restorezero
__restorezero proc near ; void _restorezero(void) 

push ds
mov ax,2500h
lds dx,_Int0Vector
int 21h
pop ds
push ds
mov ax,2504h
lds dx,_Int4Vector
int 21h
pop ds
push ds
mov ax,2505h
lds dx,_Int5Vector
int 21h
pop ds
push ds
mov ax,2506h
lds dx,_Int6Vector
int 21h
pop ds
ret

__restorezero endp

;------------------------------------------------------------------------------ 

_TEXT ENDS

;------------------------------------------------------------------------------ 

END
; DDSAVVEC.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; procedure per la gestione dei vettori int 0, 4, 5, 6 e del gestore 
; dell'int 0 (divide by zero). Nei normali programmi SaveVectors() e'
; chiamata dallo startup code e installa ZeroDivision. Al ritorno a DOS
; e' chiamata _restorezero(), che ripristina i vettori. La ErrorDisplay 
; scrive su stderr un mesaggio di errore. Il device driver deve invocare
; esplicitamente SaveVectors() e _restorezero() se necessario. 
;

include DDSEGCOS.ASI

extrn _Int0Vector : dword ; definiti in DD_VECT.ASM
extrn _Int4Vector : dword
extrn _Int5Vector : dword
extrn _Int6Vector : dword

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

ZDivMsg db 'Divide error', 13, 10 ; messaggio di errore int 0 
ZDivMsgLen equ $ - ZDivMsg


ErrorDisplay proc near ; void pascal ErrorDisplay(void) 

mov ah,040h
mov bx,2
int 021h
ret

ErrorDisplay endp

;-------------------------------------------------------------- 

ZeroDivision proc far ; void far pascal ZeroDivision(void) 

mov cx,ZDivMsgLen
mov dx,offset DGROUP:ZDivMsg
push cs ; modello tiny: DS = CS
pop ds
call ErrorDisplay
mov ax, 3
push ax
ret

ZeroDivision endp

;-------------------------------------------------------------- 

public SaveVectors
SaveVectors proc near ; void pascal SaveVectors(void) 

push ds
mov ax, 3500h
int 021h
mov word ptr _Int0Vector,bx
mov word ptr _Int0Vector+2,es
mov ax,3504h
int 021h
mov word ptr _Int4Vector,bx
mov word ptr _Int4Vector+2,es
mov ax,3505h
int 021h
mov word ptr _Int5Vector,bx
mov word ptr _Int5Vector+2,es
mov ax,3506h
int 021h
mov word ptr _Int6Vector,bx
mov word ptr _Int6Vector+2,es
mov ax,2500h
mov dx,cs
mov ds,dx
mov dx,offset ZeroDivision
int 21h
pop ds
ret

SaveVectors endp

;--------------------------------------------------------------------------- 

_TEXT ENDS

;--------------------------------------------------------------------------- 

END 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

setStack() 

; DDSETSTK.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
; unsigned setStack(unsigned base,unsigned len);
;
; genera un nuovo stack locale per il device driver
;
; unsigned base; offset del (puntatore near al) nuovo stack 
; unsigned len; lunghezza dello stack in bytes
;
; restituisce: la lunghezza effettiva del nuovo stack; puo' essere 
; inferiore a quella richiesta se quest'ultima e'
; dispari o se e' dispri l'offset dello stack (viene
; effettuato allineamento alla word); se il valore
; restituito e' 0 uno dei parametri e' errato e lo
; stack non e' stato generato
;
; note: se utilizzata, e' opportuno che sia la prima f()
; chiamata da init(), inoltre non e' disponibile una
; funzione per ritornare allo stack precedente ed
; eliminare quello generato da setStack(); se il
; nuovo stack viene effettivamente attivato, allora
; l'area statica occupata dallo stack originale del
; driver e' riutilizzabile secondo le necessita' 
; dell'utente: il suo offset (puntatore near) e la
; sua lunghezza in bytes sono disponibili in _freArea
; e _freAreaDim rispettivamente.
;

INCLUDE DDSEGCOS.ASI

; dichiarazione simboli esterni

extrn __savSP1 : word
extrn __savSP2 : word
extrn __newTOS : word
extrn _DrvStk : byte
extrn _DrvStkEnd : byte
extrn ___heapbase : word
extrn ___brklvl : word
extrn __freArea : word
extrn __freAreaDim : word

extrn __stklen : word ; da libreria C

extrn __setupio : near ; e' una funzione di libreria C
; chiamata dallo startup code dei
; normali programmi: prepara le
; strutture statiche di tipo FILE
; per gli streams 

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _setStack
_setStack proc near ; unsigned setStack(unsigned base,unsigned len);

mov __savSP2,sp ; salva SP in ingresso: deve essere la
; prima istruzione di setStack()
push bp ; genera std stack frame per accedere
mov bp,sp ; ai parametri passati da init()
mov dx,word ptr [bp+4] ; DX = offset di inizio del nuovo stack 
mov ax,word ptr [bp+6] ; AX = lunghezza
pop bp ; elimina stack frame per operaz. copia
test dx,1 ; offset base del nuovo stack e' pari?
jz W_ALIGNED_BASE ; si; salta
inc dx ; allinea base stack e heap a word
dec ax ; no: lo spazio e' diminuito di 1 byte!
W_ALIGNED_BASE:
and ax,0FFFEh ; impone lunghezza stack pari
mov cx,offset _DrvStkEnd ; CX = attuale TopOfStack 
sub cx,__savSP2 ; CX = TOS - SP = bytes da copiare
cmp ax,cx ; lungh. (AX) > bytes (CX) ?
jbe ERROR ; no: stack insufficiente; salta
mov bx,dx ; si: BX = offset base nuovo stack
add dx,ax ; DX = off base + lunghezza = nuovo TOS
jnc PARMS_OK ; CARRY = 0: TOS <= FFFFh: OK; salta
ERROR:
xor ax,ax ; segnala errore
jmp EXIT_FUNC
PARMS_OK:
mov __newTOS,dx ; salva nuovo TOS
mov ___heapbase,bx ; base heap = base nuovo stack
mov ___brklvl,bx ; attuale fine heap = base nuovo stack

mov __freArea,offset _DrvStk ; offset ex-stack per riutilizzo 
mov __freAreaDim,STKSIZE ; dimensione vecchio stack

mov bx,__savSP1 ; SP prima di push parametri di init()
sub bx,__savSP2 ; differenza tra i due SP 
cmp bx,NPA_SP_DIFF ; diff se init() non ha parms e var. auto
je BP_ADJUST ; se = basta settare BP e copiare stack
STACK_ADJUST: ; altrimenti prima di copiare: 
mov word ptr [bp],dx ; BP = offset da SS della copia di BP
; PUSHed in init(), cioe' SS:[BP]
; = precedente BP (TOS), ora = __newTOS

mov bx,offset _DrvStkEnd ; BP e' offset rispetto a SS, ora BX =
sub bx,bp ; distanza tra quell'offset e fine stack
mov bp,dx ; BP viene trasformato per puntare allo
sub bp,bx ; stesso offset rispetto a __newTOS (AX)
jmp COPY_STACK ; BP gia' valorizzato
BP_ADJUST:
mov bp,dx ; BP = __newTOS: init(void) e no var auto
COPY_STACK:
push es ; salva ES (DS e' sempre = CS = SS), SI
push si ; e DI. Non copiati perche' estratti da
push di ; stack prima dell'attivazione nuovo stk
std ; copia dall'alto al basso 
mov si,offset _DrvStkEnd ; fine vecchio stack
sub si,2 ; DS:SI -> prima word da copiare (BP DOS)
push ds
pop es ; ES = DS
mov di,dx ; DI = __newTOS
sub di,2 ; ES:DI -> ultima word del nuovo stack
mov bx,cx ; CX contiene ancora numero bytes stack
cli
rep movsb ; copia il contenuto dello stack
sti
pop di ; ripristina i registri salvati
pop si ; estraendoli ancora dal vecchio stack
pop es ; ES era la prima word non copiata
mov sp,dx ; SP = __newTOS
sub sp,bx ; __newTOS - bytes_in_stack = nuovo SP

mov __stklen,ax ; per libreria C
push ax ; usa gia' il nuovo stack 
call __setupio ; ora ha senso: c'e' spazio (da startup)
pop ax ; restit. AX = LEN: nuovo stack attivato!
EXIT_FUNC: 
ret 

_setStack endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

setupcmd() 

; DDSETCMD.ASM - Barninga Z! - 1994
;
; ASSEMBLER SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY
;
;
; void setupcmd(void);
;
; funzione di parsing della command line
;
; genera una copia locale statica della command line e in questa tronca
; opportunamente le stringhe con NULLs, valorizzando un array statico di
; puntatori a char (_cmdArgs) e un intero (_cmdArgsN) che contiene il
; numero degli items presenti sulla cmd line (compreso il nome del driver)
;

include DDSEGCOS.ASI

; dichiarazioni dei simboli esterni

extrn _RHptr : dword
extrn __cmdLine : word
extrn __cmdArgs : word
extrn __cmdArgsN : word

;------------------------------------------------------------------------------ 

_TEXT segment

;------------------------------------------------------------------------------ 

public _setupcmd
_setupcmd proc near ; void setupcmd(void) prepara due parametri 
; per init() analoghi a argc e argv
push si
push di
push ds
lds si,DGROUP:_RHptr ; DS:SI punta a Request Header
lds si,[si+18] ; DS:SI punta a Command Line
mov di,offset __cmdLine ; ES:DI punta a _cmdLine (ES = SS = CS) 
mov cx,CMDSIZE
rep movsb ; crea copia locale della command line
pop ds
mov si,offset __cmdLine ; DS:SI -> _cmdLine (ES = DS = SS = CS)
inc si ; punta al secondo byte (emula LODSB)
mov di,offset __cmdArgs ; ES:DI -> _cmdArgs (ES = DS = SS = CS)
xor dx,dx ; contatore argomenti 
cld ; operazioni stringa: forward
call _setarg ; funz. di servizio: vedere a fine listato
NEXTARG:
NEXTCHAR: ;;;; scansione di un parametro 
lodsb
cmp al,32 
je ENDARG ; blank \
cmp al,9 ; > fine argomento 
je ENDARG ; tab /
cmp al,13
je ENDCMD ; CR \
cmp al,10 ; |
je ENDCMD ; LF > fine command line
cmp al,26 ; |
je ENDCMD ; EOF /
cmp al,34 ; 
je DQUOTES ; DOUBLE QUOTES
jmp NEXTCHAR
ENDARG: ;;;; fine parametro
mov byte ptr [si-1],0 ; termina str. con NULL (SI gia' incr.) 
ENDARG_1: ;;;; scansione spazio tra due parametri
lodsb
cmp al,32 
je ENDARG_1 ; blank \
cmp al,9 ; > separatori argomenti
je ENDARG_1 ; tab /
cmp al,13
je ENDCMD_1 ; CR \
cmp al,10 ; |
je ENDCMD_1 ; LF > fine command line
cmp al,26 ; |
je ENDCMD_1 ; EOF /
cmp al,34 ; 
je DQUOTES ; DOUBLE QUOTES: inizio parametro
call _setarg
jmp NEXTARG
DQUOTES: ;;;; virgolette nella command line
mov byte ptr [si-1],0
inc si ; le virgolette sono scartate: 
call _setarg ; in _setarg AX=SI e DEC AX
dec si ; ripristina il puntatore 
DQUOTES_1:
lodsb
cmp al,13
je ENDCMD ; CR \
cmp al,10 ; |
je ENDCMD ; LF > fine command line
cmp al,26 ; |
je ENDCMD ; EOF /
cmp al,34 ; 
je ENDARG ; DQUOTES: fine parametro 
jmp DQUOTES_1
ENDCMD: ;;;; fine parametro e command line
mov byte ptr [si-1],0 ; termina str. con NULL (SI gia' incr.) 
ENDCMD_1: ;;;; fine command line 
xor ax,ax
stosw ; _cmdArgs[DX] = NULL 
mov __cmdArgsN,dx ; _cmdArgsN = numero argomenti 
pop di
pop si

ret

_setupcmd endp

;----------------------------------------------------------------- 

_setarg proc near ;;;; inizio di un nuovo parametro 
;;;; routine di servizio per setupcmd() 
mov ax,si
dec ax ; SI e' gia' stato incrementato 
stosw ; _cmdArgs[DX] -> argomento 
inc dx ; trovato un altro argomento 

ret

_setarg endp

;------------------------------------------------------------------------------ 

_TEXT ends

;------------------------------------------------------------------------------ 

end 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

discardDriver() 

/*******************************************

DDDISCRD.C - Barninga Z! - 1994 

C SOURCE FILE PER DEVICE DRIVER TOOLKIT - LIBRARY

discardDriver() - comunica al DOS di NON lasciare residente 
il device driver, secondo la procedura
definita nelle specifiche Microsoft.

Sintassi:

void discardDriver(void);

Compilare con

bcc -mt -c dddiscrd.c

*******************************************/
#include "bzdd.h"

void discardDriver(void)
{
extern RequestHeaderFP RHptr; // accesso al request header 
extern DevDrvHeader DrvHdr; // accesso ad device driver header 

DrvHdr.attrib &= 0x7FFF; // trasforma in block device driver 
RHptr->cp.initReq.nUnits = 0; // nessuna unita' supportata 
setResCodeEnd(NOT_RESIDENT); // primo byte libero = indirizzo del driver


OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

BZDD.H 

// BZDD.H - Barninga Z! - 1994
//
// include file per libreria device driver
//

#ifndef __BZDD_H // evita doppia inclusione 
#define __BZDD_H

// necessario includere DOS.H se non ancora incluso

#ifndef __DOS_H
#include <dos.h>
#endif

// COSTANTI MANIFESTE

// codici di ritorno e di stato

#define S_SUCCESS 0x0000
#define S_ERROR 0x8000 // codici di status
#define S_BUSY 0x0200 // in OR tra di loro
#define S_DONE 0x0100

#define E_OK 0 // codici di ritorno in OR coi precedenti
#define E_WR_PROTECT 0 
#define E_UNKNOWN_UNIT 1
#define E_NOT_READY 2
#define E_UNKNOWN_CMD 3
#define E_CRC 4
#define E_LENGTH 5
#define E_SEEK 6
#define E_UNKNOWN_MEDIA 7
#define E_SEC_NOTFOUND 8
#define E_OUT_OF_PAPER 9
#define E_WRITE 10
#define E_READ 11
#define E_GENERAL 12
#define E_RESERVED_1 13
#define E_RESERVED_2 14
#define E_INVALID_DSKCHG 15

// servizi del driver

#define C_INIT 0
#define C_MEDIACHECK 1
#define C_BUILDBPB 2
#define C_INPUTIOCTL 3
#define C_INPUT 4
#define C_INPUTND 5
#define C_INPUTSTATUS 6
#define C_INPUTFLUSH 7
#define C_OUTPUT 8
#define C_OUTPUTVERIFY 9
#define C_OUTPUTSTATUS 10
#define C_OUTPUTFLUSH 11
#define C_OUTPUTIOCTL 12
#define C_DEVICEOPEN 13
#define C_DEVICECLOSE 14
#define C_MEDIAREMOVE 15
#define C_OUTPUTBUSY 16
#define C_GENERICIOCTL 19
#define C_GETLOGICALDEV 23
#define C_SETLOGICALDEV 24

// altre

#define NO_VLABEL "NO NAME" // per dischi senza volume label
#define RB_CHG 0xFF // dischetto sostituito 
#define RB_MAYBE 0 // dischetto sostituito... forse
#define RB_NOTCHG 1 // dischetto non sostituito 

#define NOT_RESIDENT 0 // da passare a setResCodeEnd() se il
// driver non deve rimanere residente.
// setResCodeEnd() e' una macro definita
// in questo sorgente.

// FUNZIONI CORRISPONDENTI AI SERVIZI DEI DRIVER
// servizi del driver, da implementare in C. Le implementazioni assembler in
// libreria servono solo come placeholders per quelle non realizzate in C e
// non fanno che chiamare unSupported() 

#ifdef __cplusplus // per poter usare il toolkit con il C++ 
extern "C" {
#endif

int mediaCheck(void);
int buildBPB(void);
int inputIOCTL(void);
int input(void);
int inputND(void);
int inputStatus(void);
int inputFlush(void);
int output(void);
int outputVerify(void);
int outputStatus(void);
int outputFlush(void);
int outputIOCTL(void);
int deviceOpen(void);
int deviceClose(void);
int mediaRemove(void);
int outputBusy(void);
int genericIOCTL(void);
int getLogicalDev(void);
int setLogicalDev(void);

// ALTRE FUNZIONI

void discardDriver(void); // comunica al DOS di non lasciare residente il driver
int errorReturn(int errNum); // restit. al DOS il codice errNum 
unsigned setStack(unsigned base,unsigned len); // genera un nuovo stack
int unSupported(void); // rest. al DOS l'err E_UNKNOWN_CMD

// FUNZIONI DA STARTUP CODE

void pascal SaveVectors(void); // salva vett. 0 4 5 6 installa handler int 0
void _restorezero(void); // ripristina vettori 0 4 5 6 

// VARIABILI GLOBALI (derivate da startup code o definite liberamente) 

extern int errno; // codice di errore
extern unsigned _version; // versione e revisione DOS
extern unsigned _osversion; // versione e revisione DOS
extern unsigned char _osmajor; // versione DOS
extern unsigned char _osminor; // revisione DOS
extern unsigned long _StartTime; // timer clock ticks al caricamento 
extern unsigned _systemMem; // Kb di memoria convenzionale installati 
extern unsigned _psp; // segmento di caricamento (CS), non vero PSP
extern unsigned _baseseg; // segmento di caricamento (CS) 
extern void huge *_farMemBase; // ptr huge a mem libera dos (varia atomatic.)
extern void huge *_farMemTop; // ptr huge a fine mem libera dos (varia aut.)
extern void *_endOfSrvc; // offset fine funzioni dummy dei servizi 
extern void *_endOfCode; // offset fine codice eseguibile 
extern void *_endOfData; // offset fine spazio dati (= Drvr)
extern void *_endOfDrvr; // offset fine spazio driver 
extern void *_freArea; // offset area statica libera (ex-stack) 
extern unsigned _freAreaDim; // dimensione dell'area statica libera 
extern int _cmdArgsN; // num. parametri cmd line (argc) 
extern char **_cmdArgs; // array ptr param. cmd line (argv)

#ifdef __cplusplus
}
#endif

// typedefs per semplificare le dichiarazioni

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned long DWORD;

// strutture di vario tipo

typedef struct { // struct per Bios Parameter Block 
WORD secsize; // bytes in un settore 
BYTE clusecs; // settori in un cluster 
WORD ressecs; // settori riservati prima della FAT
BYTE fatno; // numero di FATs esistenti 
WORD rootntr; // numero di entries nell root
WORD totsecs; // numero di settori nel disco
BYTE mediadb; // Media Descriptor Byte 
WORD fatsecs; // numero di settori in una copia della FAT
} BPBLK;

// GESTIONE DEL DEVICE DRIVER HEADER

// la struct e la union seguenti possono essere utilizzate per la gestione del campo
// nome logico del device driver header: infatti la union consente di
// utilizzare gli 8 bytes a disposizione come una stringa di caratteri (e' il caso
// dei character device driver) oppure come una struttura blkName, definita come
// 1 byte (numero i unita' supportate) e un campo riservato di 7 bytes (e' il caso
// dei block device driver)

typedef struct { // struttura Nome Logico per Block Device
BYTE nUnits; // unita' supportate 
char breserved[7]; // riservato
} blkName;

typedef union { // union Nome Logico per Dev. Driver Header
char cname[8]; // nome character device
blkName bn; // nome block device 
} h_logName;

// La struct DevDrvHeader ricalca la struttura del device driver header, consentendo
// altresi' l'uso della union h_logName per la gestione del nome logico del device

typedef struct { // struttura per Device Driver Header
void far *nextDrv; // ptr al prossimo device driver
WORD attrib; // device attribute word
WORD stratOff; // offset della Strategy routine
WORD intrOff; // offset della Intterupt routine
h_logName ln; // gestione nome logico del device
} DevDrvHeader;

// GESTIONE DEL REQUEST HEADER

// E' definita una struct per la parte di request header differenziata per ogni
// specifico servizio ed una union che le comprende tutte. Vi e' poi una struct
// che rappresenta la parte fissa del reqest header piu' la union da utilizzare
// per il servizio. In altre parole, i templates di seguito definiti consentono
// di gestire il request header come una struttura (la stessa per tutti i servizi)
// che rappresenta la parte fissa, alla quale ne e' "accodata" una seconda a
// scelta tra quelle definite appositamente per i vari servizi. 

// parti variabili per i diversi servizi

typedef struct { // servzio 0 (init)
BYTE nUnits;
void far *endAddr;
char far *cmdLine;
BYTE firstUnit;
} c_init;

typedef struct { // 1 (media check)
BYTE mdByte;
BYTE retByte;
char far *vLabel;
} c_mediaCheck;

typedef struct { // 2 (build BPB)
BYTE mdByte;
BYTE far *trAddr;
BPBLK *bpb;
} c_buildBPB;

typedef struct { // 3 (input IOCTL)
BYTE mdByte;
BYTE far *trAddr;
WORD itemCnt;
WORD startSec;
char far *vLabel;
} c_inputIOCTL;

typedef struct { // 4 (input)
BYTE mdByte;
BYTE far *trAddr;
WORD itemCnt;
WORD startSec;
char far *vLabel;
} c_input;

typedef struct { // 5 (non destructive input) 
BYTE readCh;
} c_inputND;

typedef struct { // 6 (input status)
BYTE dummy;
} c_inputStatus;

typedef struct { // 7 (flush input buffers) 
BYTE dummy;
} c_inputFlush;

typedef struct { // 8 (output)
BYTE mdByte;
BYTE far *trAddr;
WORD itemCnt;
WORD startSec;
char far *vLabel;
} c_output;

typedef struct { // 9 (output with verify) 
BYTE mdByte;
BYTE far *trAddr;
WORD itemCnt;
WORD startSec;
char far *vLabel;
} c_outputVerify;

typedef struct { // 10 0A (output status) 
BYTE dummy;
} c_outputStatus;

typedef struct { // 11 0B (flush output buffers) 
BYTE dummy;
} c_outputFlush;

typedef struct { // 12 0C (output IOCTL)
BYTE mdByte;
BYTE far *trAddr;
WORD itemCnt;
WORD startSec;
char far *vLabel;
} c_outputIOCTL;

typedef struct { // 13 0D (device open)
BYTE dummy;
} c_deviceOpen;

typedef struct { // 14 0E (device close)
BYTE dummy;
} c_deviceClose;

typedef struct { // 15 0F (is media removable) 
BYTE dummy;
} c_mediaRemove;

typedef struct { // 16 10 (output until busy) 
BYTE mdByte;
BYTE far *trAddr;
WORD itemCnt;
WORD startSec;
char far *vLabel;
} c_outputBusy;

typedef struct { // 19 13 (generic IOCTL interface) 
BYTE category;
BYTE function;
WORD siReg;
WORD diReg;
void far *packet;
} c_genericIOCTL;

typedef struct { // 23 17 (get logical device) 
BYTE dummy;
} c_getLogicalDev;

typedef struct { // 24 18 (set logical device) 
BYTE dummy;
} c_setLogicalDev;

// union raggruppante le struct che descrivono la parte variabile di request
// header per i vari servizi

typedef union {
c_init initReq;
c_mediaCheck mCReq;
c_buildBPB bBReq;
c_inputIOCTL iIReq;
c_input iReq;
c_inputND iNReq;
c_inputStatus iSReq;
c_inputFlush iFReq;
c_output oReq;
c_outputVerify oVReq;
c_outputStatus oSReq;
c_outputFlush oFReq;
c_outputIOCTL oIReq;
c_deviceOpen dOReq;
c_deviceClose dCReq;
c_mediaRemove mRReq;
c_outputBusy oBReq;
c_genericIOCTL gIReq;
c_getLogicalDev gLReq;
c_setLogicalDev sLReq;
} cParms;

// struct rappresentante il request header (5 campi per la parte fissa piu' la
// union per la parte variabile). La typedef consente di definire, per
// comodita', tipi di dato corrispondenti al request header stesso, 
// all'indirizzo near e all'indirizzo far di un request header. 

typedef struct {
BYTE length;
BYTE unitCode;
BYTE command;
WORD status;
BYTE reserved[8];
cParms cp;
} RequestHeader, *RequestHeaderP, far *RequestHeaderFP;

// DICHIARAZIONE DEGLI ITEMS DEFINITI NEL MODULO DI STARTUP (DDHEADER.ASM

extern RequestHeaderFP RHptr; // puntatore al request header 
extern DevDrvHeader DrvHdr; // header del device driver 

// MACRO di comodo

// La macro che segue puo' essere utilizzata per settare nella parte variabile del
// request header (per il servizio 0) l'indirizzo di fine codice residente del
// device driver. Per scaricare il driver dalla memoria evitandone l'installazione
// e' sufficiente passarle 0. Vedere la costante manifesta NOT_RESIDENT sopra
// definita e la funzione discardDriver(), il cui utilizzo sostituisce la
// chiamata setResCodeEnd(NOT_RESIDENT). La setResCodeEnd() fornisce, tra l'altro,
// un esempio di utilizzo delle strutture e della union definite per manipolare il
// request header.

#define setResCodeEnd(off) (RHptr->cp.initReq.endAddr = MK_FP(_CS,off));

#endif // __BZDD_H 

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura... 

DRVSET.C 

/************************************************************ 

DRVSET.C - Barninga Z! - 1994 

Utility per modificare la device attribute word e il logical name nello header
dei device driver. Funziona con qualsiasi device driver (purché non .EXE) 
anche se non realizzato con la libreria toolkit. Lanciare DRVSET con:

drvset [opzioni] nome_di_file

dove:

nome_di_file e' il nome del device driver da modificare 
opzioni puo' essere:
-b o -d o -h e -n

Le opzioni -b, -d e -h sono alternative tra loro e devono essere seguite (senza 
spazi frapposti) dalla nuova device attribute word in binario, decimale e, 
rispettivamente, esadecimale.

L'opzione -n deve essere seguita (senza spazi frapposti) dal nome logico del 
device driver, che viene troncato o completato con blanks, se necessario, e
convertito in maiuscole. Nel caso di block device driver, in luogo del nome
logico deve essere specificato il numero di unita' supportate, racchiuso tra
barre ('/').

I campi dello header sono aggiornati solo previa conferma da parte dell'utente.

Compilato con Borland C++ 3.1

bcc drvset.c parseopt.obj

************************************************************/ 
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>

#include <parseopt.h> // per la gestione della command line 

#define PRG "DRVSET"
#define VER "1.0"
#define YEAR "1994"
#define SWCHAR '-'
#define BLANK ' '
#define UFLAG '/'
#define ILLMSG "Illegal Option"
#define ATTR_MASK 0x17A0 // tutti i bits illeciti nella attrib word
#define BIT_15 0x8000 // character device driver
#define NAMELEN 8
#define MIN_UNITS 1L
#define MAX_UNITS 26L
#define MAX_LINE 128

typedef struct { // struttura gestione device driver header 
long nextDev;
unsigned attrib;
unsigned strategyOff;
unsigned interruptOff;
char name[NAMELEN];
} DEVHDR; // la typedef consente di usare DEVHDR per dichiarare variabili

// prototipi di gestione delle opzioni di command line 

int valid_b(struct OPT *vld,int cnt);
int valid_d(struct OPT *vld,int cnt);
int valid_h(struct OPT *vld,int cnt);
int valid_n(struct OPT *vld,int cnt);
int err_handler(struct OPT *vld,int cnt);
int name_flag(struct OPT *vld,int cnt);

// prototipi delle altre funzioni

int main(int argc,char **argv);
void checkAttrBits(void);
int confirm(char *prompt,char yes,char no);
void displayHeader(DEVHDR *hdr,char *title);
int setDevDrvHdr(char *fname);

DEVHDR DevDrvHdr; // globale per semplicita'; e' la struttura per gestire lo header

const char *helpStr = "\
filename is : the name of the device driver file to be updated\n\ 
option(s) are:\n\n\
One of the following:\n\
-bBinAttr\n\
-dDecAttr\n\
-hHexAttr\n\
where BinAttr, DecAttr and HexAttr are the Device Driver Attribute Word\n\
in binary, decimal or hexadecimal notation.\n\
And/or one of the following:\n\
-nDevDrvName (for character devive driver)\n\
-n/LogUnits/ (for block device driver)\n\
Device Driver Logical Name (will be uppercased and truncated if necessary).\n\
If Block Device Driver, /LogUnits/ specifies the number of Supported\n\ 
Logical Units (slashes must be typed).\n\n\
*** No update done. ***\n\
";

const char *invAttrib = "%s: Invalid Device Driver Attribute Word.\n";

const char *invName = "%s: Invalid Device Driver Logical Name.\n";

const char *invUnits = "%s: Invalid Device Driver Supported Units.\n";

const char *invFile = "%s: Too many filenames specified.\n"; 

const unsigned char nonFNameChars[] = { // caratteri illeciti in nomi file
0x22,0x2A,0x2C,0x2E,0x2F,0x3A,0x3B,0x3C,0x3D,0x3E,0x5B,0x5C,0x5D,0x7C,0x81, 
0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x91,0x93,0x94, 
0x95,0x96,0x97,0x98,0xA0,0xA1,0xA2,0xA3,0xA4,NULL
};

unsigned optCnt; // contatore opzioni: massimo una tra -b, -h, -d
unsigned nameFlag; // nome file specificato?


// FUNZIONI DI CONTROLLO DELLE OPZIONI DELLA COMMAND LINE

#pragma warn -par
#pragma warn -rvl

// La funzione valid_b() effettua i controlli sul parametro specificato per
// l'opzione -b, onde verificare la correttezza dei bits impostati per la device
// attribute word. Il parametro specificato e' un numero BINARIO. Non vengono
// effettuati controlli sulla coerenza reciproca dei bits settati. 

int valid_b(struct OPT *vld,int cnt) // convalida opzione "b" 
{
extern unsigned optCnt;
extern DEVHDR DevDrvHdr;
extern const char *invAttrib;
char *ptr;
register i;

if(optCnt++)
err_handler(NULL,NULL);
for(ptr = vld->arg; *ptr == '0'; ptr++);
if(strlen(ptr) > 16) {
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
strrev(ptr);
for(i = 0; ptr[i]; i++)
switch(ptr[i]) {
case '1':
DevDrvHdr.attrib += (1 << i);
case '0':
break;
default:
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
checkAttrBits();
}

// La funzione valid_d() effettua i controlli sul parametro specificato per
// l'opzione -d, onde verificare la correttezza dei bits impostati per la device
// attribute word. Il parametro specificato e' un numero DECIMALE. Non vengono
// effettuati controlli sulla coerenza reciproca dei bits settati. 

int valid_d(struct OPT *vld,int cnt) // convalida opzione "d" 
{
extern unsigned optCnt;
extern DEVHDR DevDrvHdr;
extern const char *invAttrib;
register temp;

if(optCnt++)
err_handler(NULL,NULL);
if(atol(vld->arg) > UINT_MAX) {
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
if((temp = atoi(vld->arg)) < 0) {
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
DevDrvHdr.attrib = temp;
checkAttrBits();
}

// La funzione valid_h() effettua i controlli sul parametro specificato per
// l'opzione -h, onde verificare la correttezza dei bits impostati per la device
// attribute word. Il parametro specificato e' un numero ESADECIMALE. Non vengono
// effettuati controlli sulla coerenza reciproca dei bits settati. 

int valid_h(struct OPT *vld,int cnt) // convalida opzione "h" 
{
extern unsigned optCnt;
extern DEVHDR DevDrvHdr;
extern const char *invAttrib;
register i;

if(optCnt++)
err_handler(NULL,NULL);
for(i = 0; vld->arg[i] == '0'; i++);
if(strlen(vld->arg+i) > 4) {
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
sscanf(vld->arg+i,"%X",&DevDrvHdr.attrib); 
checkAttrBits();
}

// La valid_n() controlla la validita' del nome logico specificato per il device e
// lo copia nel campo apposito del device driver header, trasformando tutti i
// caratteri in maiuscoli, troncandolo se piu' lungo di 8 caratteri e aggiungendo
// spazi a "tappo" se piu' corto. Nel caso sia usata la sintassi /units/ per
// specificare il numero di unita' supportate (block device driver) controlla la
// correttezza sintattica e la validita' del parametro e lo copia nel primo byte
// del campo. Il controllo tra tipo di parametro e tipo di device e' effettuato
// dalla name_flag(), listata poco sotto.

int valid_n(struct OPT *vld,int cnt) // convalida opzione "n" 
{
extern const unsigned char nonFNameChars[];
extern DEVHDR DevDrvHdr;
extern const char *invName;
extern const char *invUnits;
static int instance;
register i;
long units;
char *ptr;
char line[MAX_LINE];

if(instance++)
err_handler(NULL,NULL); // consentito solo un nome 
if(*vld->arg == UFLAG) { // BLOCK DEVICE DRIVER
if(sscanf(vld->arg+1,"%ld%s",&units,line) != 2) {
fprintf(stderr,invUnits,PRG);
err_handler(NULL,NULL);
}
if((units > MAX_UNITS) || (units < MIN_UNITS)) { 
fprintf(stderr,invUnits,PRG);
err_handler(NULL,NULL);
}
if(strcmp(line,"/")) {
fprintf(stderr,invUnits,PRG);
err_handler(NULL,NULL);
}
DevDrvHdr.name[0] = (unsigned char)units;
}
else { // CHARACTER DEVICE DRIVER
ptr = vld->arg;
if(strpbrk(strupr(ptr),(char *)nonFNameChars)) {
fprintf(stderr,invName,PRG);
err_handler(NULL,NULL);
}
strncpy(DevDrvHdr.name,ptr,NAMELEN);
for(i = strlen(ptr); i < NAMELEN; i++)
DevDrvHdr.name[i] = BLANK; // lunghezza fissa 8 blank padded
}
}

// La err_handler() gestisce il caso di opzione errata

int err_handler(struct OPT *vld,int cnt) // gestione opz. errate 
{
extern const char *helpStr;

fprintf(stderr,"%s: Syntax is:\n%s option[s] filename\n\n%s",PRG,PRG, 
helpStr);
exit(1);
}

// la name_flag() e' chiamata dalla parseopt() una volta per ogni parametro
// non-option incontrato sulla command line, pertanto e' chiamata una sola volta
// se sulla cmd line e' specificato un solo nome di file. Tramite il contatore
// nameFlag verifica di non essere chiamata piu' di una volta. Inoltre essa
// controlla che vi sia coerenza tra il tipo di device driver indicato nella
// device attribute word (bit 15 settato) e il tipo di parametro per l'opzione
// -n (nome logico o numero di unita').

int name_flag(struct OPT *vld,int cnt) // gestione nome file
{
extern DEVHDR DevDrvHdr;
extern unsigned nameFlag;
extern const char *invAttrib;
extern const char *invFile;

if(nameFlag++) {
fprintf(stderr,invFile,PRG);
err_handler(NULL,NULL);
}
if((DevDrvHdr.name[0] < BLANK) && (DevDrvHdr.attrib & BIT_15)) {
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
}


#pragma warn .par
#pragma warn .rvl

// Struttura per la gestione delle opzioni (associa ogni opzione lecita alla
// funzione corrispondente).

struct VOPT valfuncs[] = { 
{'?',err_handler},
{'b',valid_b},
{'d',valid_d},
{'h',valid_h},
{'n',valid_n},
{ERRCHAR,err_handler},
{NULL,name_flag}
};

// stringa di definizione delle opzioni (se seguite dai due punti richiedono un 
// parametro)

const char *optionS = "?b:d:h:n:";

// FUNZIONI: main(), poi tutte le altre in ordine alfabetico

int main(int argc,char **argv)
{
extern unsigned optCnt;
extern unsigned nameFlag;
extern const char *optionS;
extern struct VOPT valfuncs[];
struct OPT *opt;

fprintf(stderr,"%s %s - Set DevDrv Header - Barninga Z! %s. -? help\n\n",
PRG,VER,YEAR);

// main() usa parseopt() per analizzare le opzioni
// specificate sulla command line. Sono queste ad effettuare tutti i controlli
// (vedere sopra).

if(!(opt = parseopt(argc,argv,(char *)optionS,SWCHAR,ILLMSG,ILLMSG, 
valfuncs))) {
perror(PRG);
return(1);
}
if(!optCnt) {
fprintf(stderr,"%s: No option specified.\n",PRG); 
return(1);
}
if(!nameFlag) {
fprintf(stderr,"%s: No filename specified.\n",PRG); 
return(1);
}
return(setDevDrvHdr(opt[opt[0].opt].arg));
}

// Controlla che i bits settatti nella attribute word del device driver header
// non siano tra quelli riservati DOS (che devono essere zero) 

void checkAttrBits(void)
{
if(DevDrvHdr.attrib & ATTR_MASK) {
fprintf(stderr,invAttrib,PRG);
err_handler(NULL,NULL);
}
}

// Chiede conferma all'utente. Dal momento che attende lo standard input, la
// conferma puo' essere letta da un file (con la redirezione '<') contenente
// la lettera 'Y' o 'N' seguita da un CR LF. Cio' risulta utile nei casi in cui
// si voglia automatizzare l'operazione di agiornamento, ad esempio in un
// batch file di compilazione del device driver.

int confirm(char *prompt,char yes,char no)
{
int ch;

fprintf(stderr,prompt,yes,no);
do {
ch = toupper(getch());
} while((ch != yes) && (ch != no));
fprintf(stderr," %c\n\n",ch);
return((ch == yes) ? 1 : 0);
}

// Visualizza i campi dello header

void displayHeader(DEVHDR *hdr,char *title)
{
register i = 0;

fprintf(stdout,"%s\n",title);
fprintf(stdout,"\tNext Device Address: %Fp\n",hdr->nextDev); 
fprintf(stdout,"\tAttribute Word: %04X\n",hdr->attrib); 
fprintf(stdout,"\tStrategy Routine Offset: %04X\n",hdr->strategyOff); 
fprintf(stdout,"\tInterrupt Routine Offset: %04X\n",hdr->interruptOff); 
fprintf(stdout,"\tLogical Name: \""); 
if(*hdr->name < BLANK) {
i = 1;
putch(*hdr->name);
}
for( ; i < NAMELEN; i++)
fputc(hdr->name[i],stdout);
fprintf(stdout,"\"\n\n");
}

// Legge lo header attuale del driver e chiama displayHeader() una prima volta per
// visualizzarne i campi. Successivamente visualizza, sempre tramite displayHeader()
// i campi come saranno scritti nel driver in base ai parametri passati sulla
// command line e attende conferma via confirm().

int setDevDrvHdr(char *fname)
{
extern DEVHDR DevDrvHdr;
DEVHDR fileHdr;
FILE *file;

if(!(file = fopen(fname,"r+b"))) {
perror(PRG);
return(1);
}
if(fread(&fileHdr,sizeof(DEVHDR),1,file) < 1) {
perror(PRG);
return(1);
}
displayHeader(&fileHdr,"Current Header Fields:"); 
fileHdr.attrib = DevDrvHdr.attrib;
strncpy(fileHdr.name,DevDrvHdr.name,NAMELEN);
displayHeader(&fileHdr,"New Header Fields:"); 
if(confirm("Confirm Update (%c/%c)?",'Y','N')) { 
rewind(file);
if(fwrite(&fileHdr,sizeof(DEVHDR),1,file) < 1) {
perror(PRG);
fprintf(stdout,"%s: %s may have been partially modified.\n",PRG, 
strupr(fname));
return(1);
}
fprintf(stdout,"%s: %s updated successfully.\n",PRG,strupr(fname)); 
}
else
fprintf(stdout,"%s: %s not updated.\n",PRG,strupr(fname)); 
return(0);



OK, OK, ne ho avuto abbastanza! Torniamo alla lettura...