logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak zmierzyć czas między znakami w DOS przy użyciu przerwań szeregowych?

viki 29 Gru 2005 11:10 2413 10
  • #1 2125774
    viki
    Poziom 16  
    Posty: 262
    Pomógł: 11
    Ocena: 3
    Piszę program pod dosa do monitorowania transmisji szeregowej. Potrzebuję zmierzyć czas pomiędzy dwoma znakami. Chciałbym to zrobić w funkcji obsługi przerwania od portu szeregowego po odebraniu znaku. Problem w tym, że przerwanie 21h - 2c jest chyba za wolne. Czy nie można tików procesora lub milisekund odczytać gdzieś z pamięci? Jak można zrobić to inaczej? Może RTC? Może nie w przerwaniu?
  • Pomocny post
    #2 2125837
    bis
    Poziom 21  
    Posty: 274
    Pomógł: 54
    Ocena: 3
    Standardowo nie masz zegara o potrzebnej rozdzielczości. Ja bym zrobił tak: napisał własną obsługę przerwania timera systemowego (nie RTC) aby uzyskać licznik do pomiaru z zadana dokładnością, podłożył ją pod tę standardową, podkręcił częstotliwość przerwań (standardowo jest to ok 18 Hz) do takiej jak potrzeba. Wtedy w przerwaniach od transmisji używał bym tego licznika do wzmiankowanego pomiaru. Trzeba jeszcze pamiętać że jeżeli nie zablokujesz całego DOS'a (np. wypisywania na ekranie czy sprawdzania klawiatury, zapisy do pliku) to niektóre pomiary będą obarczone sporym błędem (część odwołań dosowych, na pewien czas, blokuje przerwania).
  • Pomocny post
    #3 2125842
    Sam Sung
    Poziom 33  
    Posty: 2039
    Pomógł: 227
    Ocena: 611
    Stan zegara systemowego można odczytać z adresu 0000:046Ch. Jest on inkrementowany co 1 takt zegara, czyli standardowo co ok. 55 ms.
    Co do taktów procesora - można je odczytać instrukcją RDTSC (dostępna chyba od 486DX w wersji "S")
  • #4 2128885
    spros
    Poziom 17  
    Posty: 186
    Pomógł: 16
    Ocena: 26
    RDTSC dostepna jest chyba od p2
    o ile sie nie myle to jej kod maszynowy:
    dw 31h, 0fh
  • #5 2129330
    viki
    Poziom 16  
    Posty: 262
    Pomógł: 11
    Ocena: 3
    rdtsc zwraca mi często wartości ujemne, no chyba że robie coś nie tak. Mam celka 466. Tak wygląda program:

    #include <dos.h>
    #include <stdio.h>
    #include <mem.h>
    
    typedef struct
    {
    	unsigned lo;
    	unsigned hi;
    }stim;
    
    int main(void)
    {
    	volatile stim t_beg,t_end;
    	volatile unsigned ul,uh,i;
    	volatile unsigned long lbeg,lend,ldif;
    	char buf[100];
    
    	clrscr();
    	for(i=10;i;i--)
    	{
    	 _asm{
    		 db 0x0f,0x31
    		 mov word ptr t_beg.lo,ax
    		 mov word ptr t_beg.hi,dx
    	 }
    	 delay(2);
    	 _asm{
    		 db 0x0f,0x31
    		 mov word ptr t_end.lo,ax
    		 mov word ptr t_end.hi,dx
       }
    	 memcpy(&lbeg,&t_beg,4);
    	 memcpy(&lend,&t_end,4);
    	 ldif = lend-lbeg;
    	 printf("ldif= %lu\n",ldif);
      }
    	return 0;
    }


    A tak wyniki:

    
    ldif= 43469
    ldif= 4294923666
    ldif= 22472
    ldif= 21785
    ldif= 4294922497
    ldif= 21193
    ldif= 20973
    ldif= 4294922719
    ldif= 21218
    ldif= 21916
    


    przy delayu 1 ldif ~= 12000
    przy delayu 2 ldif ~= 20000
    przy delayu 3 ldif ~= 30000
    przy delayu 4 ldif ~= 40000
    przy delayu 5 ldif ~= 50000

    Zauważyłem, że pod dosem (bez znaczenia czy jest emm386 czy nie) i windą wyniki są podobne

    problem w tym że powyżej (tzn delay>5) to mam już tylko wartości ujemne

    Skłaniam się wiec teraz do propozycji bisa i od razu mam proźbę o jakieś przykłady
  • Pomocny post
    #6 2129516
    bis
    Poziom 21  
    Posty: 274
    Pomógł: 54
    Ocena: 3
    To rdtsc jest znaczne lepsze, bo chyba u Ciebie działa (ale jest znak zapytania w których 486 Intel to wprowadził (nieudokumentowane), oficjalnie jest chyba dopiero od Pentium).

    Może robisz błąd wielkości zwracanego wyniku. RDTSC zwraca 64 bitowy wyni, dwa słowa 32 bitowe EDX:EAX, a ty wczytujesz kawałki 16 bitowe DX:AX.I to może być powodem twojego problemu. Pamiętaj że musisz operować całą 64 bitową liczbą (trzeba własną matematykę na odejmowanie napisać)

    popatrz tutaj:
    www.midnightbeach.com/jon/pubs/rdtsc.htm
  • #7 2129780
    spros
    Poziom 17  
    Posty: 186
    Pomógł: 16
    Ocena: 26
    ups. najprostrzy sposob.
    przeciez transmisja szeregowa idzie przez przerwanie (08h?) z zegara. podmienic robic cokolwiek (zliczac interwaly) i oddac sterowanie do poprzedniej (oryginalnej) funkcji. czestotliwosc transmisji jest programowalna - wiadomo jaki mnoznik trzeba ustawic. wymnozyc mnoznik przez ilosc interwalow i po klopocie.
    rdtsc tez jest dobre ale trzeba cale edx::eax uwzglednic no i trzeba wiedziec ile taktow schodzi na ms, oraz jaki jest narzut zwiazany z obsluga tego mechanizmu. bo dla malych czasow % moze byc duzy. co widac na przykladzie 1ms.
    nie wiem czy wyniki sa prawidlowe bo licznik jest inkrementowany co takt. nie wazne ile rozkazow zostanie wykonanych. tak wiec powinien teoretycznie dac 450.000.000 /s czyli 450.000 na milisekunde (delay 1). zatem bardziej prawdopodobne jest to ze wyniki sa nieprawidlowe a to co otrzymujesz jest tylko przypadkowo zbierzne. ja bym to sprawdzil.
    pozdrawiam zycze szampanskiej zabawy w sylwestrowa noc.

    - nie programuj w swieta : bug sie rodzi :)
  • #8 2129940
    viki
    Poziom 16  
    Posty: 262
    Pomógł: 11
    Ocena: 3
    Poprawiłem programik wg linka od bisa i działa super. Wielkie dzięki. Jeśli ktoś chce programik po poprawkach to dajcie znać. Zamieszcze go w tym wątku.
  • #9 2138928
    viki
    Poziom 16  
    Posty: 262
    Pomógł: 11
    Ocena: 3
    Programik pod winde. Kompilowany fasmem:
    
    format PE GUI 4.0 
    entry start 
    
    include 'win32a.inc' 
    
    ID_DIALOG          = 10  
    ID_FREQ1           = 101 
    ID_MSG             = 201 
    
    section '.data' data readable writeable 
    
      CPUSpeed dd 123 
      bf    db '%6lu MHz',0  
      output rb 20 
    
    section '.code' code readable executable 
    
    start: 
    
            invoke  GetModuleHandle,0 
            invoke  DialogBoxParam,eax,ID_DIALOG,HWND_DESKTOP,DialogProc,0 
            or      eax,eax 
            jz      exit 
    exit: 
            invoke  ExitProcess,0 
    
    proc DialogProc hwnddlg,msg,wparam,lparam 
            push    ebx esi edi 
            cmp     [msg],WM_INITDIALOG 
            je      wminitdialog 
            cmp     [msg],WM_COMMAND 
            je      wmcommand 
            cmp     [msg],WM_CLOSE 
            je      wmclose 
            xor     eax,eax 
            jmp     finish 
      wminitdialog: 
            jmp     processed 
    wmcommand: 
            cmp     [wparam],BN_CLICKED shl 16 + IDCANCEL 
            je      wmclose 
            cmp     [wparam],BN_CLICKED shl 16 + IDOK 
            jne     processed 
    ;obsBuga ok 
        invoke SleepEx, 100, 0 
        rdtsc 
        push edx 
        push eax 
        invoke SleepEx, 1000, 0 
        rdtsc 
        pop ecx 
        sub eax, ecx 
        pop ecx
        sbb edx, ecx 
        mov ecx, 1000000 
        div ecx 
        mov [CPUSpeed], eax 
      invoke  wsprintf,output,bf,[CPUSpeed]  
      invoke  SetDlgItemText,[hwnddlg],ID_FREQ1,output 
      jmp processed 
    
    wmclose: 
            invoke  EndDialog,[hwnddlg],0 
    processed: 
            mov     eax,1 
    finish: 
            pop     edi esi ebx 
            ret 
    endp 
    
    section '.idata' import data readable writeable 
    
      library kernel,'KERNEL32.DLL',\ 
              user,'USER32.DLL' 
    
    ;  library  user,'USER32.DLL' 
    
    
      import kernel,\ 
             GetModuleHandle,'GetModuleHandleA',\ 
             ExitProcess,'ExitProcess',\ 
             UpdateWindow,'UpdateWindow',\ 
             SleepEx,'SleepEx' 
    
      import user,\ 
             DialogBoxParam,'DialogBoxParamA',\ 
             SetDlgItemText,'SetDlgItemTextA',\ 
             wsprintf,'wsprintfA',\  
             EndDialog,'EndDialog' 
    
    section '.rsrc' resource data readable 
      RT_MANIFEST = 24
      
      directory RT_DIALOG,dialogs,RT_MANIFEST,manifests
      
      resource dialogs,ID_DIALOG,LANG_ENGLISH+SUBLANG_DEFAULT,demonstration 
      resource manifests,1,LANG_ENGLISH+SUBLANG_DEFAULT,man
      
      dialog demonstration,'CPU Frequency meter',0,0,250,50,WS_CAPTION+WS_POPUP+WS_SYSMENU+DS_MODALFRAME 
        dialogitem 'STATIC','Frequency:',-1   ,5 ,10,35,10,WS_VISIBLE 
        dialogitem 'STATIC',' ',ID_FREQ1,45,10,55,10,WS_VISIBLE;+WS_BORDER 
        dialogitem 'BUTTON','Find',IDOK,195,10,50,14,WS_VISIBLE+WS_TABSTOP+BS_DEFPUSHBUTTON 
        dialogitem 'BUTTON','Exit',IDCANCEL,195,25,50,14,WS_VISIBLE+WS_TABSTOP+BS_PUSHBUTTON 
      enddialog 
    
      resdata man 
             db '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',13,10 
             db '<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">',13,10 
             db '<assemblyIdentity name="x.x.x" processorArchitecture="x86" version="5.1.0.0" type="win32"/> ',13,10 
             db '<description>no</description>',13,10 
             db '<dependency>',13,10 
             db '<dependentAssembly>',13,10 
             db '<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*" />',13,10 
             db '</dependentAssembly>',13,10 
             db '</dependency>',13,10 
             db '</assembly>',13,10 
      endres
    

    Poniżej plik exe spakowany zipem
    Załączniki:
    • CPUSPEED.zip (1.1 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #10 2142068
    viki
    Poziom 16  
    Posty: 262
    Pomógł: 11
    Ocena: 3
    Programik pod win98 i xp działa ok i zajmuje tylko 3kb. Pod NT i 2000 wyświetla prawidłowy wynik i po chwili mam msgboxa. Może ma ktoś pomysł jak to poprawić?
    Załączniki:
    • Jak zmierzyć czas między znakami w DOS przy użyciu przerwań szeregowych? blad.gif (9.39 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #11 2144994
    viki
    Poziom 16  
    Posty: 262
    Pomógł: 11
    Ocena: 3
    Poprawiłem programik. Użyłem makra invoke zamiast cinvoke w wywołaniu funkcji wsprintf. Poniżej zamieszczam poprawionego exeka
    Załączniki:
    • CPUSPEED.zip (1.1 KB) Musisz być zalogowany, aby pobrać ten załącznik.

Podsumowanie tematu

LABEL_AI_GENERATED
W dyskusji omówiono metody pomiaru czasu między znakami w transmisji szeregowej pod DOS, realizowanej w obsłudze przerwania portu szeregowego. Standardowe przerwanie 21h-2Ch jest zbyt wolne do precyzyjnego pomiaru. Proponowano podmianę obsługi przerwania timera systemowego (standardowo ~18 Hz) na własną, z podkręconą częstotliwością, aby uzyskać licznik o wyższej rozdzielczości do pomiaru interwałów czasowych. Zegar systemowy można odczytać spod adresu 0000:046Ch, gdzie licznik inkrementuje się co około 55 ms. Do pomiaru taktów procesora sugerowano użycie instrukcji RDTSC, dostępnej od procesorów Pentium (lub nieoficjalnie od 486DX), która zwraca 64-bitowy licznik cykli CPU w rejestrach EDX:EAX. Problemy z interpretacją wyników RDTSC wynikały z błędnego odczytu 16-bitowych fragmentów zamiast pełnych 32-bitowych rejestrów i konieczności obsługi 64-bitowej arytmetyki. Wskazano na potrzebę uwzględnienia narzutu obsługi przerwań i dokładnego skalowania wyników do czasu rzeczywistego. Zaproponowano także alternatywę polegającą na zliczaniu interwałów w przerwaniu od transmisji szeregowej, wykorzystując programowalną częstotliwość transmisji. W dalszej części dyskusji pojawiły się przykładowe implementacje programów mierzących częstotliwość CPU i czas między zdarzeniami, zarówno pod DOS, jak i Windows (Win98, XP), z uwzględnieniem problemów kompatybilności i poprawności wywołań funkcji systemowych.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA