Newsgroup: it.comp.lang.pascal
Titolo: Chi mi scrive un procedura in assembler?

Ciao,
io di assembler con ci capisco niente ma mi servirebbe una procedura
che ne sostituisca una in pascal per migliorare un programma che ho
gia' fatto da tempo.
La procedura dovrebbe prelevare, ad intervalli regolari, il contenuto
dell'indirizzo $3fe (com1+6) e riempire un blocco di memoria diciamo
di 1000byte (magari poi questo valore gli si da come parametro per
renderlo piu' versatile). Il tempo con cui la procedura deve
memorizzare i campioni e' anch'esso un parametro di typo word che
rappresenta l'intervallo in microsecondi.
Il blocco di memoria che la procedura deve riempire me lo alloco dal
pascal e a questa gli passo (come preferite) il puntatore al blocco o
l'indirizzo di partenza.
In pratica la procedura doprebbe essere di questo tipo:
seg,off   indirizzo del blocco di memoria da riempire,
size   e' la dimensione i bytes del blocco;
w     e' la distanza in microsecondi tra i campioni.
procedure  campiona(seg, off, size, w : word);
var	i, r : word;
begin
	r := w - tempo_istruzioni;
	for  i := 1 to size do begin
	     indirizzo(seg : off + i - 1)  :=  port[$3fe];
	     pausa( r )
	end
end;
da tradurre completamente in assembler.
Cosa mi dite?
Visto che ci sono vi chiedo un chiarimento.
C'e' una corrispondenza tra MEM e PORT?
In pratica per scrivere sulla parallela posso fare PORT[$378]:=valore.
Come faccio ad accedere a quell'indirizzo usando MEM?
Saluti,  Fabio.
fabi...@rocketmail.com

Do un po' piu' di infomazioni.
Il programma gia' fatto e funzionante e' un oscilloscopio digitale che
legge i dati dalla porta seriale com 1.
I dati che poi vengono visualizzati sul video vengono letti con una
procedura che e' piu' o meno questa:
v : array [1..1000] of byte  (* array dichiarata globale *)
procedure leggi(n,w:word);
var	i,j:integer;
begin
	for i:=1 to n do begin
	   v[i]:=port[$3fe];
	   for j:=1 do w do w:=w
	end
end;
Il problema e' che questa procedura, anche se funzionante, non e'
proprio il massimo. E', per esempio, difficile (in pratica
impossibile) determinare con precisione l'effettiva frequenza di
campionamento, e le sue prestazioni dipendono molto dal PC su cui sta
girando.
Come si puo' vedere il metodo per distanziare i campioni e' alquando
casareccio e grossolano.
Con il mio PC 486-DX4 -100Mhz risco a prelevare campioni con una
frequenza 350Khz circa. La For interna mi esegue un ritardo, che va
dai  3 microsecondi (w=1) in su, in base alle mi esigenze. La DELAY
non va proprio bene perche fornisce ritardi di millisecondi.
Ciao,   Fabio
fabi...@rocketmail.com

 	
Beh, non ho tempo per andare a controllare, comunque quel rep stosb non 
mi sembra molto utile. Se non ricordo male, con quello che hai scritto, il prog 
ti riempie la zona di memoria sempre con lo stesso valore (AL) per CX volte.
Da quello che ho capito io, si dovrebbe utilizzare un loop [etichetta] al posto 
del rep stosb.
Il tutto dovrebbe apparire (+ o - :)) come segue:
 	   mov	 ax,segmento della variabile array
 	   mov	 es,ax
    	   mov	 di,offset della variabile array
          cld
 	   mov  cx,numero di byte da leggere
       1: in   al, 03feh
          stosb
 	   call delay
 	   loop 1
Per il delay userei qualcosa del tipo:
   Mov Ax, N  (N = millisecondi di attesa)
   Mov Cx, 3E8h
   Mul Cx          
   Mov Cx, Dx
   Mov Dx, Ax
   Mov Ah, 86h
   Int 15h
Bye, Mobius



 non va proprio bene perche fornisce ritardi di millisecondi.
Vediamo se ho capito ... l'unico problema e' creare il delay adeguato, quindi 
non serve per forza riscrivere il tutto in assembler, giusto ?
Bye, Mobius

    Int 15h
Mi sono appena accorto che il ritardo doveva essere in microsecondi, quindi cio' 
che ho scritto non va certo bene ...
Riproviamo :)
INT 15
AH = 86h
CX:DX = interval in microseconds
Return: CF clear if successful (wait interval elapsed)
 	 CF set on error or AH=83h wait already in progress
Bye, Mobius

Bye, Mobius
Scusa ma mi puoi scrivere bene tutto partendo da PROCEDURE fino a END
Grazie,  Fabio.
fabi...@rocketmail.com

...


 wrote:

Non mi funziona per ritardi piccoli.
Per esempio se faccio:
	for i:=1 to 10000 do
	     new_delay(100)
ho un ritardo di 1 secondo, mentre se faccio:
	for i:=1 to 100000 do
	   new_delay(10)
ho un ritardo molto maggiore di un secodo.
Cosa si puo' fare?
Grazie,  Fabio
fabi...@rocketmail.com

...











Questo mi interessa.
Che precisione potrei avere?
Io la delay la potrei fare con una for del tipo
	for i:=1 to w do
	   x:=x
ma poi dovrei sapere con precisione quando e' durata.
Se avessi la precisione di almeno un microsecondo mi andrebbe
benissimo.
Dimmi dimmi
Grazie e ciao,
fabio
fabi...@rocketmail.com

 Cosa si puo' fare?
Mmm ... in effetti anche a me da dei risulati sballatissimi quando faccio un for 
con all'interno un delay piccolo. Inizialmente pensavo fosse a causa di quelle 
90000 chiamate alla procedura in piu' che vengono effettuate nel secondo caso, 
ma poi ho fatto un po' di prove ed ho scoperto che il problema non sta li. In 
ogni caso tu sei gia fortunato che il primo caso ti da esattamente 1 secondo di 
attesa, a me anche con quel ciclo mi ritorna un tempo di attesa MOLTO + alto.
Bah, appena ho un attimo di tempo ci medito un po' su :)
Bye, Mobius

Bye, Mobius
Infatti.
Io ho trovato che una Delay(100) dura meno di una Delay(10).
Ma???
Ciao,  Fabio
fabi...@rocketmail.com






 Che precisione potrei avere?
Sperando di non sparare buffonate ... ho fatto i conti in velocita' :)
Il timer lo puoi programmare per generare un interrupt con una frequanza che 
varia da 18.2 Hz (che e' quella di default) a 1.194 MHz. Ovviamente devi tener 
conto di quanto tempo serve per eseguire la procedura che viene richiamata con 
l'interrupt, perche' se e' troppo lenta rispetta alla frequenza del timer ti 
sballa tutti i conti che hai fatto :)
In ogni caso, utilizzando la frequenza massima (e supponendo che si riesca ad 
utilizzarla bene, non ho mai provato :)) potresti richiamare la tua procedura 
ogni 840 nanosecondi, il che credo possa bastarti :)
Bye, Mobius

Ciao,
Con una precisione di 1/20 di secondo non ci faccio niente.
Considera che fino ad adesso utilizzato il time del dos che fornisce
la precisioe di 1/100 di secondo.
A me serve una precisione migliore. Diciamo 1 microsecondo forse anche
10 microsecondi ma se andiamo oltre 1 millisecondo allora sto come
prima.
Ciao,  Fabio
fabi...@rocketmail.com



A me serve determirare la durata di un evendo che dura circa 1 secondo
con una precisione diciamo di un microsecondo.
Ciao,  Fabio
fabi...@rocketmail.com

 con una precisione diciamo di un microsecondo.
Allora, vorrei un chiarimento perche' forse non ho capito molto bene la 
situazione. Mi sono riletto la procedura che avevi postato all'inizio, e da 
quella io avevo capito che volevi leggere la porta seriale ad intervalli 
regolari distanziati da un certo tempo T. Il tuo problema era quindi calcolare 
con precisione quel tempo T, e quindi creare un delay che sostituisse il ciclo 
for interno, giusto ?
A questo punto, dato che il timer ti permete gia' di suo di richiamare ad una 
certa frequenza la tua procedura, il delay non serve piu' (anzi NON deve esserci 
piu').
Quindi ora non capisco quale sia l'evento di cui si deve determinare la durata.
Mi saro' perso qualcosa ? :)
Bye, Mobius



Allora,
Visto che fare delay con valori di ritardo di 1 MIRCOsecondo o 10
MICROsecondi non ci si riesce avevo pensato di tornare al vecchio
metodo.
Io di solito faccio letture di blocchi di 1000 campioni.
Se io faccio un ritardo con
	for i:=1 to w do x:=x
con W =1 questa, sul mio PC (dipende dalla potenza del processore), mi
dura circa un paio di microsecondi.
Quindi se faccio:
	w:=2;
	for j:=1 to 1000 do begin
		<lettura valore dalla seriale>;
		for i:=1 to w do x:=x
	end;
e riesco a sapere con precisione quando tempo ho impiegato per
eseguire il blocco di istruzioni, dividendo poi per 1000 riesco ad
ottere la distanza tra un campione e l'altro.
Con questo sistema pero' non posso impostare a priori la distanza tra
un campione e l'altro, ma so' comunque quello che sta facendo. Non e'
la stessa cosa pero' va bene uguale.
Io per adesso utilizzando la funzione GetTime posso misurare eventi
con la precisione di 1/100 di secondo.
Quindi io ripeto l'esecuzione del blocco piu' volte in modo da
aumentare la durate dell'evento portandolo alla durata di circa un
secondo e poi misuro. Il problema e' che comunque la precisione non e'
buona ed ottengo un errore che e' circa del 10%. Troppo.
Capito?
Ciao,  Fabio
fabi...@rocketmail.com

 Capito?
Io si, forse tu non hai capito cio' che intendevo.
Suppongo comunque d'essermi spiegato male, riprovo :)
La mia idea era la seguente:
1) Crei una procedura del tipo:
Procedure ReadPort;
Interrupt;
Begin
  <Lettura di UN valore dalla seriale>
  <Memorizzazione di tale valore>
End;
2) Riprogrammi il timer per generare un interrupt con una certa frequenza
compresa fra 18.2 Hz e 1.194 MHz.
3) Informi il timer di richiamare, ogni volta che genera il suo interrupt, la 
procedura ReadPort.
Fine, il delay non serve, l'interrupt viene generato alla frequenza che vuoi 
indipendentemente dal computer, la precisione dovrebbe essere ottimale.
Mi sembra la soluzione migliore, poi vedi tu :)
Bye, Mobius



Per me e' OK.
Se TU sei capace a farlo per va benissimo. Io pero' gli Interrupt non
li so usare.
Se mi puoi scrivere l'intera procedura mi fai un piacere
(non c'e fretta)
Ricordati che a me serve una procedura che mi prelevi dalla porta 1000
campioni con una frequenza F data input.
Grazie e ciao,
Fabio
fabi...@rocketmail.com

 Se mi puoi scrivere l'intera procedura mi fai un piacere
Se aspetti un paio di settimane te la scrivo volentieri. Non che sia poi cosi' 
difficile (credo), piu' che altro per mancanza di tempo ...
Bye, Mobius

Salve, ti propongo una soluzione per il tuo problema :)
Il seguente programma richiama la provedura P con una frequenza
determinata dalla procedura SetFreq.
Non l'ho testato molto, quindi non so se abbia bisogno di sistemazioni.
Non so nemmeno come possa funzionare a frequenze molto elevate,
poiche' bisogna considerare che fra una chiamata e l'altra della
procedura P, il computer deve avere il tempo di eseguire il codice
inserito in P (che comunque nel mio caso e' sicuramente un'istruzione
molto lenta).
Un'altro paio di considerazioni, la frequenza deve essere compresa
(a livello teorico) fra 18.20676Hz e 1193180 Hz. Tieni pero' conto
che non si puo' ottenere esattamente la frequenza voluta a
causa dell'istruzione (1193180 Div F).
Prova comunque a testarlo un po' (modificando opportunamente P) e
poi fammi sapere i risultati :)
Ah gia', un'ultima cosa, l'ho provato solo sotto DOS :)
Program Timer;
Uses Dos, Crt;
Var SavePointer: Pointer;
{$F+}
Procedure P;
Interrupt;
Begin
   Write('.');
End;
{$F-}
Procedure SetFreq(F: Longint);
Var W: Word;
Begin
   W := 1193180 Div F;
   Asm
   Mov Al, 36h
   Out 43h, Al
   Mov Bx, W
   Mov Al, Bl
   Out 40h, Al
   Mov Al, Bh
   Out 40h, Al
   End
End;
Procedure SetFreqDefault;
Begin
   Asm
   Mov Al, 36h
   Out 43h, Al
   Xor Bx, Bx
   Mov Al, Bl
   Out 40h, Al
   Mov Al, Bh
   Out 40h, Al
   End
End;
Begin
   ClrScr;
   GetIntVec($1C, SavePointer);
   SetIntVec($1C, @P);
   SetFreq(100000);
   Repeat Until KeyPressed;
   SetFreqDefault;
   SetIntVec($1C, SavePointer)
End.

-cut-
Scusa il ritardo con cui replico ma ho avuto poco tempo questa
settimana.
Ho riadattato il programma per le mie esigenze, cioe' riempire
un'array con 1000 campioni prelevati con una data frequenza.
Program Timer;
Uses Dos, Crt;
type	Blocco=array[1..1000] of byte;
var	v,vv:blocco;
Var SavePointer: Pointer;
    i:integer;
    vv:blocco;
{$F+}
Procedure P;
Interrupt;
Begin
   if i<1000 then begin
        inc(i);
        vv[i]:=port[$3fe]
   end
End;
{$F-}


procedure LeggiBlocco(var v:blocco; f:integer);
Begin
   GetIntVec($1C, SavePointer);
   SetIntVec($1C, @P);
   SetFreq(f);
   i:=0;
   while i<1000 do begin
   end;
   SetFreqDefault;
   SetIntVec($1C, SavePointer);
   v:=vv
end;
begin
	repeat       
	    LeggiBlocco(v,1000)
	    <visualizza campioni>;
	until <fine>
end.
Il marchingegno sembra funzionare ma non permette comunque di arrivare
alla frequenza di 1Mhz perche' a quei livelli la procedura P non viene
piu' richiamata alla frequanza impostata ma molto piu' lentamente. Il
limite massimo che ho riscontrato sul mio PC e' di circa 500Khz. La
versione con la delay che impostava ritardi in microsecondi non
arrivava neanche a 100Khz.
Non ho avuto tempo per fare verifiche per vedere se la frequenza
impostata si effettivamente quella che poi il PC realizza.
Comunque c'e' un grosso problema. La STABILITA'.
Dopo qualche cambiamento della frequanza di campionamento il PC si
pianta. Cosi' non puo' andare.
Io penso che la suluzione piu' semplice sia quella di effetturare i
ritardi tramite una for del tipo
       for i:=1 to w do x:=x
ma poi tu conosci un sistema per determinare con la precisione di un
microsecondo (o poco piu') quanto e' durata l'esecuzione di un blocco
di istruzioni? Esiste in pratica un orologio interno molto priciso?
P.S.  Il sistema di porter richiamare una procedura con una certa
cadenza e' veramente carino e sicuramente mi risultera' utile in
qualche prossima applicazione.
Ciao e grazie
		fabio
fabi...@rocketmail.com

 di istruzioni? Esiste in pratica un orologio interno molto priciso?
Mmm ... boh :)
Ci penso ...
Bye, Mobius

Peccato :(
No.
Come gia detto, il programma legge dalla com1 un segnale e lo
visualizza sul video. All'inizio dell'esecuzione la frequnza e'
preimpostata per esempio a 10Khz ma poi pigiando + o - e' possibile
incrementarla o diminuirla a piacere. Con il + campiono al doppio
della frequenza precedente, con il - alla meta'.
Dopo un po' che cambio la frequenza si pianta tutto.
Ciao,  Fabio
fabi...@rocketmail.com

 Dopo un po' che cambio la frequenza si pianta tutto.
Se vuoi mandarmi il programma (il codice ovviamente) ci do un'occhiata 
volentieri ... :)
Bye, Mobius