In questo esempio presentiamo un programma TSR che consente di scrivere
in un file specificato dall'utente il contenuto del video (in modo testo)
quando vengano premuti contemporaneamente i due tasti di shift. Il testo
del buffer video è aggiunto ai dati eventualmente già presenti
nel file indicato; al termine di ogni riga (e prima di quella iniziale)
è aggiunta una sequenza CR LF; in coda al buffer è
inserito un carattere ASCII 12 (FormFeed o salto pagina), rendendo
in tal modo il tutto particolarmente idoneo al successivo editing mediante
programmi di videoscrittura.
/********************
Barninga_Z! - 1991
SHFVWRIT.C - TSR che scrive su un file il buffer video CGA, EGA,
VGA in modo testo formattato mediante l'aggiunta di
CR+LF in testa e a fine riga, e di FF in coda. Il nome
del file deve essere specificato sulla command line.
I bit del byte attributo (eccetto il bit del blink)
sono invertiti per segnalare lo svolgimento
dell'operazione e sono nuovamente invertiti al termine
della scrittura su file (effettuata in append). Se
l'applicazione interrotta "ridisegna" una parte del
video durante la scrittura, i bit dei bytes attibuto
di tale porzione del video non vengono invertiti al
termine dell'operazione. Il TSR si attiva premendo
contemporaneamente i due SHIFT.
Compilato sotto TURBO C++ 1.01
tcc -O -d -rd -k- -Tm2 shfvwrit.c
********************/
#pragma inline
#pragma option -k-
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#define PRG "SHFVWRIT"
#define YEAR "1991"
#define _BLANK_ ((char)32)
#define _FF_ ((char)12)
#define _LF_ ((char)10)
#define _CR_ ((char)13)
#define _NROW_ 25
#define _NCOL_ 80
#define _VBYTES_ _NROW_*_NCOL_
#define _NCR_ (_NROW_+1)
#define _NLF_ _NCR_
#define _NFF_ 1
#define _BUFDIM_ (_VBYTES_+_NCR_+_NLF_+_NFF_)
#define _MONO_VIDADDR_ ((char far *)0xB0000000L)
#define _COLR_VIDADDR_ ((char far *)0xB8000000L)
#define _SHFMASK_ ((char)3)
#define _MASK_ ((char)127)
#define _TSR_TEST_ 0x97 /* shfvwrit e' residente ? */
#define _TSR_YES_ 0xABFE /* risposta = si, e' residente */
#define _PSPENVOFF_ 0x2C /* off del seg ptr all'env.in psp */
#define _FNMLEN_ 80 /* lungh. max path con NULL finale */
#define int09h ((void (interrupt *)())(*((long *)TSRdata))) /*dd*/
#define int28h ((void (interrupt *)())(*(((long *)TSRdata)+1))) /*dd*/
#define int2Fh ((void (interrupt *)())(*(((long *)TSRdata)+2))) /*dd*/
#define fnameptr ((char far *)(*(((long *)TSRdata)+3))) /*dd*/
#define strptr ((char far *)(*(((long *)TSRdata)+4))) /*dd*/
#define shfflag (*(((int *)TSRdata)+10)) /*dw*/
#define exeflag (*(((int *)TSRdata)+11)) /*dw*/
#define shfstat (*((char far *)(*(((int *)TSRdata)+12)))) /*dw*/
#define vidstr (((char *)TSRdata)+26) /*arr*/
#define vidatr (((char *)TSRdata)+26+_BUFDIM_) /*arr*/
#define fname (((char far *)TSRdata)+26+_BUFDIM_+_VBYTES_) /*arr*/
#define int09h_asm TSRdata
#define int28h_asm TSRdata+4
#define int2Fh_asm TSRdata+8
#define fnameptr_asm TSRdata+12
#define strptr_asm TSRdata+16
#define clear_stack() asm {pop di;pop si;pop ds;pop es;pop dx;pop cx;pop bx;pop ax;}
void TSRdata(void); /* consente l'uso del nome prima della definiz. */
void filewrit(void)
{
strptr = vidstr;
asm {
push ds;
mov ah,0x5B;
xor cx,cx; /* attributo normale */
lds dx,dword ptr fnameptr_asm;
int 0x21; /* tenta di aprire un nuovo file */
pop ds;
mov bx,ax;
cmp ax,0x50; /* se ax=50h il file esiste gia' */
jne _OPENED;
push ds;
mov ax,0x3D01;
lds dx,dword ptr fnameptr_asm;
int 0x21; /* il file esiste: puo' essere aperto */
pop ds;
mov bx,ax;
mov ax,0x4202;
xor cx,cx;
xor dx,dx;
int 0x21; /* va a EOF per effettuare l'append */
}
_OPENED:
asm {
mov cx,_BUFDIM_;
mov ah,0x40;
push ds;
lds dx,dword ptr strptr_asm;
int 0x21; /* scrive il buffer nel file */
pop ds;
mov ah,0x3E;
int 0x21; /* chiude il file */
}
}
void vidstrset(void)
{
register j, i;
char far *vidptr, far *vidstop;
char *atrptr;
atrptr = vidatr;
asm {
push bp;
push sp;
mov ah,0x0F;
int 0x10;
pop sp;
pop bp;
}
switch(_AL) {
case 2:
case 3:
vidptr = _COLR_VIDADDR_;
break;
case 7:
vidptr = _MONO_VIDADDR_;
break;
default:
return;
}
vidstop = (vidptr += (_BH*_VBYTES_));
strptr = vidstr;
*strptr++ = _CR_;
*strptr++ = _LF_;
for(i = 0;i < _NROW_;i++) {
for(j = 0;j < _NCOL_;j++,vidptr++) {
*strptr++ = (*vidptr) ? *vidptr : _BLANK_;
*atrptr++ = (*(++vidptr) ^= _MASK_);
}
*strptr++ = _CR_;
*strptr++ = _LF_;
}
*strptr++ = _FF_;
filewrit();
for(;vidptr > vidstop;vidptr--)
*vidptr = (*(--vidptr) == (*(--atrptr) ^= _MASK_)) ? *vidptr :
*atrptr;
}
void interrupt newint28h(void)
{
if(shfflag & (!exeflag)) {
exeflag = 1;
vidstrset();
shfflag = exeflag = 0;
}
clear_stack(); /* macro per uscire da funzione interrupt */
asm jmp dword ptr int28h_asm;
}
void interrupt newint09h(void)
{
if(((shfstat & _SHFMASK_) == _SHFMASK_) && (!(shfflag | exeflag)))
shfflag = 1;
clear_stack(); /* macro per uscire da funzione interrupt */
asm jmp dword ptr int09h_asm;
}
void far newint2Fh(void) /* non interrupt: non serve salvare regs */
{
asm {
cmp al,0;
jne _CHAIN;
cmp ah,_TSR_TEST_;
je _ANSWER;
}
_CHAIN:
asm jmp dword ptr int2Fh_asm;
_ANSWER:
asm {
mov ax,_TSR_YES_;
iret;
}
}
void TSRdata(void)
{
asm {
dd 0; /* ptr int 09h */
dd 0; /* ptr int 28h */
dd 0; /* ptr int 2Fh */
dd 0; /* ptr al nome del file */
dd 0; /* ptr all'array contenente copia del video */
dw 0; /* flag richiesta popup */
dw 0; /* flag popup attivo */
dw 0x417; /* ptr shift status byte */
db _BUFDIM_ dup (0); /* buffer dati dal video al file */
db _VBYTES_ dup (0); /* buffer bytes-attributo dal video */
db _FNMLEN_ dup (0); /* pathname del file di output */
}
}
int tsrtest(void)
{
asm {
mov ah,_TSR_TEST_;
xor al,al;
int 0x2F;
cmp ax,_TSR_YES_;
je _RESIDENT;
xor ax,ax;
}
_RESIDENT:
return(_AX);
}
void release_env(void)
{
asm {
mov ax,_psp; /* tramite AX... */
mov es,ax; /* ...carica in ES l'ind. di segmento del PSP */
mov bx,_PSPENVOFF_; /* ES:BX punta all'indirizzo dell'Env. */
mov ax,es:[bx]; /* salva in AX l'ind di segmento dell'Env. */
mov es,ax; /* per poi caricarlo in ES */
mov ah,0x49; /* libera il blocco di memoria tramite il DOS */
int 0x21; /* int 21h servizio 49h */
}
}
void install(void)
{
asm cli;
int09h = getvect(0x09);
int28h = getvect(0x28);
int2Fh = getvect(0x2F);
setvect(0x09,newint09h);
setvect(0x28,newint28h);
setvect(0x2F,(void (interrupt *)())newint2Fh);
asm sti;
release_env();
_DX = FP_SEG(fnameptr)-_psp+1+((FP_OFF(fnameptr)+_FNMLEN_) >> 4);
asm {
mov ax,0x3100;
int 0x21;
}
}
int filetest(char *argv1)
{
register i;
fnameptr = fname;
for(i = 0; argv1[i]; i++)
fname[i] = argv1[i];
asm {
push ds;
lds si,dword ptr fnameptr_asm;
les di,dword ptr fnameptr_asm;
mov ah,0x60; /* ricava il pathname completo */
int 0x21; /* ATTENZIONE: e' un servizio DOS non documentato!! */
lds dx,dword ptr fnameptr_asm;
xor cx,cx; /* attributo normale */
mov ah,0x5B;
int 0x21; /* tenta di aprire un nuovo file */
jnc _OPENED_NEW; /* file aperto: il nome e' ok */
cmp ax,0x50; /* se AX=50h il file esiste: nome ok */
je _FNAME_OK;
jmp _EXITFUNC:
}
_OPENED_NEW:
asm {
mov bx,ax;
mov ah,0x3E;
int 0x21; /* chiude il file */
5 mov ah,0x41;
int 0x21; /* e lo cancella */
}
_FNAME_OK:
asm xor ax,ax;
_EXITFUNC:
asm pop ds;
return(_AX);
}
void main(int argc,char **argv)
{
(void)puts(PRG" - Barninga_Z! - Torino - "YEAR".");
if(argc != 2)
(void)puts(PRG": sintassi: shfvwrit [d:][path]file[.ext]");
else
if(tsrtest())
(void)puts(PRG": già residente in RAM.");
else
if(filetest(argv[1]))
(void)puts(
PRG": impossibile aprire il file specificato.");
else {
(void)puts(
PRG": per attivare premere LeftShift e RightShift.");
install();
}
}
La struttura del programma non è particolarmente complessa. La main()
controlla che sia stato fornito, via command line, un nome di file per
l'output: in caso negativo l'elaborazione viene interrotta. La funzione
tsrtest() verifica l'eventuale presenza in RAM del TSR,
utilizzando l' int2Fh. Se il programma non
è già residente, filetest() controlla la validità
del nome di file specificato dall'utente: il servizio60h
dell'int21h è utilizzato per ricavarne il pathname completo,
onde evitare che variazioni dei default relativi a drive e directory di
lavoro determinino la scrittura dei dati in luoghi imprevisti. La main()
invoca poi install(), che completa la fase di installazione: essa,
in primo luogo, salva i vettori degli interrupt utilizzati dal programma
e sostituisce ad essi quelli dei nuovi gestori;
in seguito invoca release_env(), che libera la RAM occupata dall'environment
fornito dal DOS al TSR, dal momento che questo
non ne fa uso. Infine install()calcola
il numero di paragrafi che devono rimanere residenti e installaSHFVWRIT mediante l'int 21h, servizio 31h, "cuore" della
funzione di libreria keep(),
qui invocato direttamente per rendere il codice più compatto.
Tutti i dati globali necessari al programma sono gestiti nello spazio ad
essi riservato, all'interno del code segment, dalla funzione
jollyTSRdata(), che chiude il gruppo delle routine residenti.
Le macro atte a facilitare i riferimenti a detti dati sono definite, nel
sorgente, al termine delle direttive #define relative alle costanti
manifeste. Disorientati?
La funzione newint2Fh() è il nuovo gestore dell' int2Fh:
essa è dichiarata far in quanto la semplicità della
sua struttura rende inutile il salvataggio automatico
dei registri. Il concatenamento al gestore originale (nel caso in cui
SHFVWRIT non sia ancora installato) è effettuato mediante
una istruzione JMP; il ritorno al processo chiamante (SHFVWRIT
installato) mediante una IRET. In entrambi i casi è indispensabile
ripristinare lo stack quale esso appare al momento dell'ingresso nella
funzione: a seconda della versione del compilatore può essere necessario,
a tal fine, effettuare una POP del registro BP.
La funzione newint09h() è il nuovo gestore dell' int09h:
esso si occupa semplicemente di analizzare il byte di stato degli shift
per intercettare lo hotkey. Se l'utente preme i due shift e, al tempo stesso,
il TSR non è attivo (exeflag = 0) viene posto
a 1 il flag indicante la richiesta di popup, cioè di attivazione.
Anche in questo caso il concatenamento al gestore originale è effettuato
con una JMP; lo stack è ripristinato
dalla macro clear_stack(), precedentemente definita. La newint09h()
è dichiarata interrupt per sicurezza.
Essa, infatti, gestisce un interrupt hardware che viene invocato da un
evento asincrono; inoltre contiene quasi esclusivamente codice C, con la
conseguenza che non è possibile sapere a priori quali registri vengano
da essa modificati. In questo caso appare prudente (e comodo) lasciare
al compilatore il compito di salvare in modo opportuno i registri della
CPU.
La newint28h() gestisce l' int28h:
essa è pertanto invocata dal DOS quando questo è in attesa
di un input dalla tastiera. Se è stato richiesto il popup e il TSR
non è attivo, viene posto a 1 il flag che ne indica l'attivazione
ed è invocata vidstrset(). Al rientro sono azzerati i flag
e viene concatenato il gestore originale. Anche newint28h() è
scritta quasi interamente in linguaggio C, pertanto è dichiarata
interrupt.
La funzione vidstrset() pilota le operazioni di lettura del buffer
video e di preparazione alla scrittura nel file. Essa verifica la modalità
video attuale mediante il servizio 0Fh dell' int10h:
se è grafica o testo a 40 colonne il controllo è restituito
a newint28h() senza intraprendere alcuna azione, altrimenti viene
opportunamente determinato l'indirizzo del buffer video.
Mediante indirezioni e incrementi
di puntatori, il testo contenuto nel buffer è copiato nello spazio
ad esso riservato nella TSRdata(), inserendo al tempo stesso i
caratteri necessari per dare al testo il formato voluto; inoltre gli ASCII
0 sono trasformati in ASCII 32 (spazio). I byte attributo del
buffer, e dunque i colori del testo visualizzato, dopo essere stati anch'essi
salvati in un array collocato nella TSRdata(), sono modificati
tramite un'operazione di XOR con un byte che
funge da maschera: lo scopo è segnalare visivamente che SHFVWRIT
è in azione. Al termine di queste operazioni è invocata filewrit()
e al rientro da questa una nuova operazione di XOR,
identica alla precedente, ripristina i colori originali del testo nelle
sole aree di video non modificate da altre applicazioni durante l'attività
di filewrit().
Quest'ultima si occupa delle operazioni di output nel file specificato
dall'utente: dal momento che il testo deve essere aggiunto al contenuto
del file, è necessario che esso sia creato se non esiste, ma non
distrutto se è già presente. Il servizio 5Bh dell'int 21h
tenta l'apertura di un nuovo file: se questo esiste l'operazione fallisce
(il servizio 3Ch ne avrebbe troncato a 0 la lunghezza) e il file
può essere aperto con il servizio 3Dh. Dopo l'operazione di scrittura
il file viene chiuso: in tal modo il DOS aggiorna correttamente FAT e directory.
La filewrit() è scritta quasi per intero in assmbly inline
non solo per ragioni di velocità e compattezza del codice, ma anche
(e soprattutto) per evitare l'uso di funzioni
di libreria[1] e, di conseguenza, i problemi legati al loro utilizzo
nella parte residente dei TSR.
Concludiamo il commento al codice di SHFVWRIT evidenziandone le
carenze, dovute alla necessità di presentare un esempio di facile
comprensione. Le routine residenti non segnalano all'utente il verificarsi
di eventuali errori (modalità video non prevista, disco pieno, etc.)
e mancano gestori per gli int 23h e 1Bh
(CTRLC, CTRLBREAK) e per l' int24h
(errore critico), la gestione dei quali è quindi lasciata all'applicazione
interrotta. Il limite più evidente è però l'utilizzo
dell' int28h come punto di attivazione: detto
interrupt è infatti generato dal DOS nei loop interni alle funzioni
01h0Ch dell'int 21h, cioè, in prima approssimazione, quando
esso è in attesa di un input da tastiera. Se l'applicazione interrotta
non si avvale del DOS per gestire la tastiera ma, piuttosto, del BIOS (
int16h), newint09() può ancora
registrare nell'apposito flag la richiesta di popup, ma questo non avviene
sino al termine dell'applicazione stessa.
Presentiamo di seguito il listato del programma VIDEOCAP, che
rappresenta una evoluzione di SHFVWRIT. In particolare VIDEOCAP
utilizza ancora quale punto di attivazione l' int28h,
ma incorpora un gestore dell' int16h (newint16h()),
il quale simula con un loop sul servizio 01h/11h le richieste di servizio
00h/10h; in tale loop viene invocato un int28h
(se non sono in corso servizi DOS) consentendo così l'attivazione
anche sotto programmi che gestiscono la tastiera via int16h.
La routine di copia del buffer video individua il modo video attuale (ammessi
testo 80 colonne colore o mono), il numero di righe video attuali e la
pagina corrente, risultando così più flessibile e completa
di quella incorporata da SHFVWRIT. L'offset della pagina video
corrente nel buffer video è determinato moltiplicando 160 (80 2)
per il numero di righe correnti e sommando un correttivo (individuato empiricamente)
descritto in una tabella hard-coded, alla quale è riservato spazio
dalla funzione fittizia rowFactors().
Si noti che le funzioni fittizie sono dichiarate
DUMMY, così come il tipo di parametro richiesto. L'identificatore
di tipo DUMMY è, in realtà, definito e reso equivalente
a void dalla riga
typedef void DUMMY;
Si tratta dunque di uno stratagemma volto ad aumentare la leggibilità
del programma (lo specificatore typedef
definisce sinonimi per i tipi di dato).
VIDEOCAP, a differenza di SHFVWRIT, impiega una funzione
fittizia per ogni variabile globale residente: in tal modo è più
semplice referenziarle mediante operazioni di cast e si evitano alcune
#define.
Anche la modalità in cui il programma segnala di avere eseguito
il proprio compito è radicalmente diverso: invece di modificare
il colore dei caratteri a video durante l'operazione, VIDEOCAP
emette un breve beep a 2000 Hz non appena chiuso il file. La funzione beep2000Hz()
esemplifica come pilotare l'altoparlante del PC in una routine residente.
/******************************************************************************
VIDEOCAP.C - Barninga_Z! - 09/11/92
Utility TSR per salvataggio del video su file. Invocare con un nome
di file per installare in memoria. Il dump su file si ottiene
premendo contemporaneamente i due tasti di shift. Per disinstallare
invocare con un asterisco come parametro sulla command line.
Compilato sotto BORLAND C++ 3.1:
bcc -Tm2 -O -d -rd -k- videocap.c
******************************************************************************/
#pragma inline
#pragma warn -pia
#include <stdio.h> // la #include <io.h> deve essere DOPO tutte le
#include <dos.h> // funzioni DUMMY contenenti asm ... dup(...)
#include <string.h> // perche' in io.h e' definita int dup(int)
#define PRG "VIDEOCAP"
#define VER "1.0"
#define YEAR "1992"
#define CRLF 0A0Dh // backwords
#define UNINSTALL '*' // opzione disinstallazione
#define BLANK 32
#define FORMFEED 12
#define _NCOL_ 80
#define _MAXROW_ 50
#define _BUFDIM_ ((_NCOL_*_MAXROW_)+(2*_MAXROW_)+3) // b*h+b*CRLF+CRLFFF
#define _MONO_V_SEG_ 0B000h
#define _COLOR_V_SEG_ 0B800h
#define _SHFMASK_ 3
#define _TSR_TEST_ 0xA1 // videocap e' residente ?
#define _TSR_YES_ 0xFF16 // risposta = si, e' residente
#define _FNAMELEN_ 81 // lungh. max pathname compreso NULL finale
#define BADCHARS ";,:|><" // caratteri illeciti nei nomi di file
typedef void DUMMY;
int pathname(char *path,char *src,char *badchrs);
char far *getInDOSaddr(void);
DUMMY resPSP(DUMMY)
{
asm dw 0;
}
DUMMY inDosFlagPtr(DUMMY)
{
asm dd 0;
}
DUMMY old09h(DUMMY)
{
asm dd 0;
}
DUMMY old16h(DUMMY)
{
asm dd 0;
}
DUMMY old28h(DUMMY)
{
asm dd 0;
}
DUMMY old2Fh(DUMMY)
{
asm dd 0;
}
DUMMY opReq(DUMMY)
{
asm db 0;
}
DUMMY inOp(DUMMY)
{
asm db 0;
}
DUMMY rowFactors(DUMMY) // fattori di offset per pagine video su VGA
{
asm db 48, 12; // fattore offset, numero righe (AL,AH) da
asm db 48, 25; // utilizzare per calcolare l'offset della
asm db 112, 29; // pagina attiva nel buffer video
asm db 16, 43;
asm db 96, 50;
asm db 0, 0; // tappo (segnala la fine della tabella)
}
DUMMY fileName(DUMMY)
{
asm db _FNAMELEN_ dup(0);
}
DUMMY bufVid(DUMMY)
{
asm db _BUFDIM_ dup(BLANK);
}
#include <io.h> // definisce dup(int); va incluso DOPO tutte le asm XX dup(Y)
void beep2000Hz(void)
{
asm in al,61h; // prepara PC speaker
asm or al,3;
asm out 61h,al;
asm mov al,0B6h;
asm out 43h,al;
asm mov al,054h;
asm out 42h,al;
asm mov al,2;
asm out 42h,al; // suona a 2000 Hz
asm mov cx,0FFFFh;
DELAY:
asm jmp $ + 2;
asm loop DELAY;
asm in al,61h;
asm and al,0FCh;
asm out 61h,al; // esclude PC speaker
}
void writebufVid(int rows)
{
asm push ds;
asm xor cx,cx; // attributo normale
asm mov ax,seg fileName;
asm mov ds,ax;
asm mov dx,offset fileName;
asm mov ah,0x5B;
asm int 0x21; // tenta di aprire un nuovo file
asm pop ds;
asm mov bx,ax;
asm cmp ax,0x50; // se ax=50h il file esiste gia'
asm jne OPENED;
asm push ds;
asm mov ax,seg fileName;
asm mov ds,ax;
asm mov dx,offset fileName;
asm mov ax,0x3D01;
asm int 0x21; // il file esiste: puo' essere aperto
asm pop ds;
asm mov bx,ax;
asm mov ax,0x4202;
asm xor cx,cx;
asm xor dx,dx;
asm int 0x21; // va a EOF per effettuare l'append
OPENED:
asm push ds;
asm mov ax,_NCOL_;
asm mov cx,rows;
asm mul cl; // AX = AL*CL (colonne * righe)
asm push ax;
asm mov ax,2;
asm mul cl; // AX = AL*CL (righe*2; spazio CRLF)
asm pop cx;
asm add cx,ax; // CX = spazio totale righe * colonne
asm add cx,3; // CX = spazio totale con CRLF 1^ riga + FF finale
asm mov ax,seg bufVid;
asm mov ds,ax;
asm mov dx,offset bufVid;
asm mov ah,0x40;
asm int 0x21; // scrive il buffer nel file
asm pop ds;
asm mov ah,0x3E;
asm int 0x21; // chiude il file
asm call _beep2000Hz;
}
int getOffsetByRow(void)
{
asm push ds;
asm mov ax,seg rowFactors;
asm mov ds,ax;
asm mov si,offset rowFactors;
NEXT_FACTOR:
asm lodsw;
asm cmp ax,0;
asm je END_OF_TABLE; // non trovato n.righe in tabella -> offset = 0
asm cmp ah,cl;
asm jne NEXT_FACTOR;
asm xor ah,ah;
asm mov bx,2;
asm mul bx; // raddoppia offset per contare attributi video
END_OF_TABLE:
asm pop ds;
return(_AX); // AX = offset correttivo
}
int setbufVid(void)
{
asm push ds;
asm xor dl,dl; // valore restituito: righe
asm mov ax,_COLOR_V_SEG_; // video se modo video ok;
asm mov ds,ax; // altrimenti 0
asm mov ah,0Fh;
asm int 10h;
asm push bx; // BH = pagina video attiva
asm cmp al,2;
asm je GETROWS;
asm cmp al,3;
asm je GETROWS;
asm cmp al,7;
asm je MONO;
asm pop bx;
asm jmp EXIT_FUNC;
MONO:
asm mov ax,_MONO_V_SEG_;
asm mov ds,ax;
GETROWS:
asm mov ax,1130h;
asm xor bh,bh;
asm push bp;
asm int 10h;
asm pop bp;
asm inc dl; // numero righe display
asm xor ch,ch;
asm mov cl,dl;
asm pop bx;
asm mov bl,bh;
asm xor bh,bh;
asm mov ax,_NCOL_ * 2;
asm push dx;
asm mul cx;
asm mul bx; // AX = offset in buf. video della pagina attiva
asm push ax;
asm push bx; // salva AX e BX
asm call _getOffsetByRow; // restituisce AX = offset correttivo
asm pop bx; // BX = num. pag.
asm mul bx; // offset * num. pagina; AX = totale correttivo
asm pop bx; // BX = offset base
asm add ax,bx; // AX = offset totale in buf. video
asm pop dx;
asm mov si,ax; // DS:SI -> video
asm mov ax,seg bufVid;
asm mov es,ax;
asm mov di,offset bufVid; // ES:DI -> buffer
NEXTROW:
asm mov word ptr es:[di],CRLF;
asm add di,2;
asm push cx;
asm mov cx,_NCOL_;
ROWCOPY:
asm lodsb;
asm cmp al,0; // NULL -> BLANK
asm jne NOT_NULL;
asm mov al,BLANK;
NOT_NULL:
asm stosb;
asm inc si; // salta attributo
asm loop ROWCOPY;
asm pop cx;
asm loop NEXTROW;
asm mov word ptr es:[di],CRLF;
asm add di,2;
asm mov byte ptr es:[di],FORMFEED;
EXIT_FUNC:
asm pop ds;
return(_DL);
}
void far new09h(void)
{
asm push ax;
asm push bx;
asm push ds;
asm cmp byte ptr opReq,0;
asm jne EXIT_INT;
asm cmp byte ptr inOp,0;
asm jne EXIT_INT;
asm xor ax,ax;
asm mov ds,ax;
asm mov bx,0417h; // indir. shift status byte
asm mov al,byte ptr [bx];
asm and al,_SHFMASK_;
asm cmp al,_SHFMASK_;
asm jne EXIT_INT;
asm mov byte ptr opReq,1;
EXIT_INT:
asm pop ds;
asm pop bx;
asm pop ax;
asm jmp dword ptr old09h;
}
void far new16h(void)
{
asm sti;
asm cmp ah,0;
asm je SERV_0;
asm cmp ah,10h;
asm je SERV_0;
asm cmp ah,1;
asm je SERV_1;
asm cmp ah,11h;
asm je SERV_1:
asm jmp EXIT_INT;
SERV_1:
asm int 28h;
asm jmp EXIT_INT;
SERV_0:
asm push dx;
asm mov dx,ax;
asm inc dh;
LOOP_0:
asm mov ax,dx;
asm cli;
asm pushf;
asm call dword ptr old16h;
asm jnz KEY_READY;
asm push ds;
asm push bx;
asm lds bx,dword ptr inDosFlagPtr;
asm cmp byte ptr [bx],0;
asm pop bx;
asm pop ds;
asm jne LOOP_0;
asm sti;
asm int 28h;
asm jmp LOOP_0;
KEY_READY:
asm mov ax,dx;
asm dec ah;
asm pop dx;
EXIT_INT:
asm jmp dword ptr old16h;
}
void interrupt new28h(void)
{
asm cmp byte ptr opReq,0;
asm je CALL_OLDINT;
asm cmp byte ptr inOp,0;
asm jne CALL_OLDINT;
asm mov byte ptr inOp,1;
asm call _setbufVid;
asm cmp ax,0;
asm je DONE;
asm push ax;
asm call _writebufVid;
asm pop cx;
DONE:
asm mov byte ptr inOp,0;
asm mov byte ptr opReq,0;
CALL_OLDINT:
asm pushf;
asm call dword ptr old28h;
}
void s2Funinstall(void)
{
asm push ds;
asm xor ax,ax;
asm mov es,ax;
asm mov ax,seg old09h;
asm mov ds,ax;
asm mov si,offset old09h;
asm mov di,0x09 * 4;
asm mov cx,2;
asm rep movsw;
asm mov ax,seg old16h;
asm mov ds,ax;
asm mov si,offset old16h;
asm mov di,0x16 * 4;
asm mov cx,2;
asm rep movsw;
asm mov ax,seg old28h;
asm mov ds,ax;
asm mov si,offset old28h;
asm mov di,0x28 * 4;
asm mov cx,2;
asm rep movsw;
asm mov ax,seg old2Fh;
asm mov ds,ax;
asm mov si,offset old2Fh;
asm mov di,0x2F * 4;
asm mov cx,2;
asm rep movsw;
asm pop ds;
}
void far new2Fh(void)
{
asm cmp ah,_TSR_TEST_;
asm je NEXT0;
asm jmp CHAIN_INT;
NEXT0:
asm cmp al,0;
asm jne NEXT1;
asm mov ax,_TSR_YES_;
asm jmp EXIT_INT;
NEXT1:
asm cmp al,UNINSTALL;
asm jne NEXT2;
asm call _s2Funinstall;
asm mov ax,word ptr resPSP;
asm jmp EXIT_INT;
NEXT2:
CHAIN_INT:
asm jmp dword ptr old2Fh;
EXIT_INT:
asm iret;
}
void releaseEnv(void)
{
extern unsigned _envseg;
if(freemem(_envseg))
puts(PRG": impossibile liberare l'environment.");
}
void install(void)
{
extern unsigned _psp;
(char far *)*(long far *)inDosFlagPtr = getInDOSaddr();
*(unsigned far *)resPSP = _psp;
releaseEnv();
asm cli;
(void(interrupt *)(void))*((long far *)old09h) = getvect(0x09);
(void(interrupt *)(void))*((long far *)old16h) = getvect(0x16);
(void(interrupt *)(void))*((long far *)old28h) = getvect(0x28);
(void(interrupt *)(void))*((long far *)old2Fh) = getvect(0x2F);
setvect(0x09,(void (interrupt *)(void))new09h);
setvect(0x16,(void (interrupt *)(void))new16h);
setvect(0x28,new28h);
setvect(0x2F,(void (interrupt *)(void))new2Fh);
asm sti;
puts(PRG": per attivare premere LShift e RShift. "PRG" * disinstalla.");
keep(0,FP_SEG(releaseEnv) + (FP_OFF(releaseEnv) / 16) - _psp + 1);
}
int tsrtest(void)
{
_AL = 0;
_AH = _TSR_TEST_;
geninterrupt(0x2F);
return(_AX == _TSR_YES_);
}
int filetest(char *fname)
{
FILE *testFile;
int newFile;
char path[_FNAMELEN_];
if(pathname(path,fname,BADCHARS))
return(1);
newFile = access(path,0);
if(!(testFile = fopen(path,"a")))
return(1);
fclose(testFile);
_fstrcpy((char far *)fileName,(char far *)path);
if(newFile)
return(unlink(path));
return(0);
}
void uninstall(void)
{
_AH = _TSR_TEST_;
_AL = UNINSTALL;
geninterrupt(0x2F);
if(freemem(_AX))
puts(PRG": impossibile liberare la memoria allocata.");
else
puts(PRG": disinstallato. Vettori ripristinati e RAM liberata.");
}
void main(int argc,char **argv)
{
puts(PRG" "VER" - Barninga_Z! - Torino - "YEAR".");
if(argc != 2)
puts(PRG": sintassi: "PRG" [d:][path]file[.ext] | *");
else
if(tsrtest())
if(*argv[1] == UNINSTALL)
uninstall();
else
puts(PRG": già residente in RAM.");
else
if(filetest(argv[1]))
puts(PRG": impossibile aprire il file specificato.");
else
install();
}
VIDEOCAP chiama due funzioni delle quali, per brvità, il
codice comprende esclusivamente i prototipi: si tratta di pathname(),
utilizzata per ricavare il path completo del file fornito come parametro
sulla riga di comando, e di getInDOSaddr(),
che restituisce l'indirizzo dell'InDOS
Flag.
Infine, si noti che lo header file IO.H è incluso dopo
la definizione di tutte le funzioni fittizie, per evitare che il compilatore
interpreti come chiamate alla funzione dup(), in esso dichiarata,
le direttive assemblyDUP utilizzate
per riservare spazio alle variabili globali residenti .
OK, andiamo avanti a leggere il libro...