![]() MeepMeep!L'ultima volta che ci siamo letti, abbiamo imparato a far emettere al nostro speaker i simpatici versi che credevamo fosse in grado di emettere solo una SoundBlaster. Oggi renderemo quei versi ancora più HiFi, ad un punto tale che molti di voi si chiederanno perchè mai hanno inventato le schede sonore...di Francesco Sileno
PIT e ClockCome prima cosa, vediamo in che modo utilizzare il timer 0, l'unico dotato di IRQ, per rendere la frequenza di riproduzione CPU-indipendent. E' immediata la necessità di riprogrammarlo ad una nuova velocità; il problema è che cambiando il timer 0 modifichiamo la anche velocità con cui il clock avanza: a parte l'orario di sistema sfasato, questo potrebbe confondere altri programmi che usano delle temporizzazioni. E comunque non è cosa buona a farsi.Facciamo quindi un paio di considerazioni:
[2]Ovviamente le approssimazioni sono molto... approssimate.
Tutto ovvio no? PROC New_INT8 FAR pushf [...] add [word ptr cs:clock_ticks],036h adc [word ptr cs:clock_ticks+2],0 cmp [cs:clock_ticks],10000h jae N_INT8_call_old push ax mov al,20h out 20h,al pop ax popf iret N_INT8_call_old: sub [word ptr cs:clock_ticks+2],1 sbb [word ptr cs:clock_ticks],0 popf jmp [dword ptr cs:old_INT8_handler] old_INT8_handler dd 0 clock_ticks dd 0 ENDP New_INT8Poichè l'INT8 è collegato ad un dispositivo hardware, prima di uscirne dobbiamo segnalare al PIT di aver compiuto il nostro dovere: a questo servono le istruzioni di out sulla porta 0x20, istruzioni che dobbiamo dare noi a meno che non sia arrivato il momento di chiamare il vecchio handler, che sicuramente provvederà per conto suo. Se invece di azzerare il contatore, gli sottraiamo il valore 0x10000, evitiamo di perdere dei tick residui che saltano fuori per motivi di approssimazione: così facendo raggiungiamo una precisione maggiore nel chiamare il vecchio handler, e questo non è affatto male. Facendo bene i calcoli, vedrete che arrivati al JMP finale, lo stack e i lag si troveranno esattamente come si trovavano appena entrati nella procedura: il vecchio handler non si accorgerà nemmeno che ora è alle dipendenze di un player. WAV HeaderLa struttura dell'header dei WAV l'ho estratta dalla PCGPE (PC Game Programming Encyclopedia), altro pacco utile e freeware reperibile su internet. Tuttavia ho dovuto correggere un paio di campi, raddoppiandone la dimensione: non avendo altre informazioni, posso supporre che l'autore abbia sbagliato, in ogni caso i MIEI file WAV sono tutti diversi da come lui li intende. In formato struttura assembler, ecco come appare:STRUC WAV_Header rID dd ? ; "RIFF" ; RIFF Header rLen dd ? wID dd ? ; "WAVE" ; WAVE Header fID dd ? ; "fmt " ; - Format Block fLen dd ? wFormatTag dw ? nChannels dw ? nSamplePerSec dd ? ; WAS WORD!?! nAvgBytesPerSec dd ? ; WAS WORD!?! nBlockAlign dw ? FormatSpecific dw ? dID dd ? ; "data" ; - Data Block dLen dd ? ENDS WAV_HeaderDi tutto ciò, quello che interessa noi è il campo dLen, ovvero la lunghezza del campionamento in byte. In più, possiamo usare wID per verificare che il file sia un WAV.
NOTA: Al lavoro!Questa volta ho sperimentato la modalità IDEAL del TASM, con conseguenti modifiche di keyword e sintassi di indirizzamento. Nel sorgente, tutte le righe che hanno come commento:; <-- IDEAL (...)hanno una di queste modifiche, e tra parentesi è indicata la sintassi da dover usare quando non si lavora in modo IDEAL. Consiglio questa modalità ai chi usa il TASM, per la sua 'rigidezza' che a volte può evitare errori, soprattutto quando si tratta di lavorare con puntatori e indici. Notate come abbia spostato tutte le variabili usate dal nuovo handler all'interno dell'handler stesso, usando poi CS come segmento di indirizzamento? Pare inutile, e forse a ben guardare in questo caso lo e'; tuttavia è bene cominciare a fare in questo modo, poichè una routine di interrupt che viene chiamata indipendentemente dal nostro programma, lo puo' essere in qualsiasi momento. E se il nostro programma è un eseguibile multi-segmento, o meglio ancora un TSR, l'unico registro di segmento su cui possiamo fare affidamento per ritrovare i nostri dati è CS. Come promesso la volta scorsa, nell'analisi della riga di comando, teniamo conto anche delle tabulazioni. Il metodo rimane tuttavia limitato ad un solo argomento, ma è facile modificarlo per poter 'catturare' uno qualsiasi degli eventuali argomenti. Per non offendere la vostra intelligenza, lascio a voi tale compito<grin>.
Possiamo provare a dare un occhiata più approfondita all'MCB, per
capire come si libera la memoria necessaria... orbene, come vi spiegavo la
volta scorsa, quando voi fare partire un eseguibile, il dos gli riserva
tutta la memoria disponibile fino al 640esimo Kb. Solo che la memoria
allocata in questo modo è accessibile solo lavorando direttamente coi
registri segmento e sperando di non andare a rompere le scatole a nessuno.
Per poter usare le funzioni DOS di allocazione/disallocazione memoria,
dobbiamo prima liberarne un po'.
Tutto il resto è normale routine (se mi perdonate lo quallido gioco di parole). ConclusioniTutto quanto detto fin ora funziona benissimo con il DOS puro... non è così con le shell di altri sistemi operativi, sopratutto se multitask. Questo perch` il sistema di temporizzazioni salta, e non potrebbe essere altrimenti con uno scheduler sotto che decide per quanti centesimi di secondo il programma deve girare.A dire il vero, da me il DOSEMU di LINUX si incanta completamente (niente che un telnet dall'altro PC non possa risolvere - avete idea di quanto sia gustoso dare un comando stile 'kill dos'?), ma poichè lo fa spesso, non credo sia colpa del mio innocente programmino.
Il mio carissimo amico Fernando qualche giorno fa mi ha chiesto a cosa
potrebbe mai servire riprodurre suoni su speaker, quando ormai una scheda
sonora a 16bit costi una miseria. |
|||
suonatamente, Cthulhu |
|||
HelpPC 2.10 Quick Reference Utility Copyright 1991 David Jurgens MCB - DOS Memory Control Block Format Offset Size Description 00 byte 'M' 4Dh member of a MCB chain, (not last) 'Z' 5Ah indicates last entry in MCB chain other values cause "Memory Allocation Failure" on exit 01 word PSP segment address of MCB owner (Process Id) possible values: 0 = free 8 = Allocated by DOS before first user pgm loaded other = Process Id/PSP segment address of owner 03 word number of paras related to this MCB (excluding MCB) 05 11bytes reserved 08 8bytes ASCII program name, NULL terminated if less than max length (DOS 4.x+) 10 nbytes first byte of actual allocated memory block - to find the first MCB in the chain, use INT 21,52 - DOS 3.1+ the first memory block contains the DOS data segment ie., installable drivers, buffers, etc - DOS 4.x the first memory block is divided into subsegments, with their own memory control blocks; offset 0000h is the first - the 'M' and 'Z' are said to represent Mark Zbikowski - the MCB chain is often referred to as a linked list, but technically isn't DOS 4.x Initial Data Segment Subsegment Control Blocks: Offset Size Description 00 byte subsegment type 'D' device driver 'E' device driver appendage 'I' Installable File System driver 'F' FILES= control block storage area (for FILES>5) 'X' FCBS= control block storage area, if present 'C' BUFFERS EMS workspace area if BUFFERS /X is used 'B' BUFFERS= storage area 'L' LASTDRIVE= current directory structure array 'S' STACKS= code/data area, if present (see below) 01 word paragraph of subsegment start 03 word subsegment size in paragraphs 05 3bytes unused 08 types "D" and "I", filename of driver loaded driver - see INT 21,48 INT 21,49 INT 21,4A Esc or Alt-X to exit MCB Home/PgUp/PgDn/End[Torna all'articolo]
Francesco Sileno è reperibile su Internet tramite la redazione
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |