Saya memprogram dalam TASM 16bit dengan DOSBox dan inilah edisi hari ini: Menggunakan DOS INT 21h/2Ch Saya bisa mendapatkan seperseratus detik sistem saat ini. Itu bagus dan semuanya... sampai tidak.

Lihat, saya mencari pengukuran waktu setidaknya semi-akurat dalam milidetik, dan saya yakin itu mungkin.

Mengapa kamu bertanya? Lihat INT 15j/86h. Dengan menggunakan interupsi ini, saya dapat menunda program dalam mikrodetik. Jika presisi seperti itu ada, saya yakin mendapatkan milidetik akan seperti berjalan-jalan di taman.

Beberapa ide yang saya miliki: Menggunakan INT 70h yang muncul setiap 1/1024 dari kedua, tetapi saya tidak tahu cara mendengarkan interupsi, saya juga tidak menginginkan sistem waktu yang tidak dapat dibagi dengan 10.

Pertanyaan ini telah menguasai saya sekarang, dan saya gagal menemukan solusi online yang sudah ada.

Salam kenal sebelumnya.

4
Coder's Crux 10 Mei 2021, 13:49

2 jawaban

Jawaban Terbaik

Terima kasih banyak kepada Peter Cordes di komentar untuk menjawab, sekarang saya akan memposting jawabannya kepada siapa pun yang berencana menggunakan kompiler kuno dari 30 tahun yang lalu.

Secara kasar, jam terbaik yang bisa Anda dapatkan di TASM 16bit masih belum cukup untuk akurasi. Untungnya, di TASM Anda dapat "membuka" mode 32bit dengan menggunakan arahan .386 (seperti yang disebutkan di sini).

Kemudian, Anda dapat menggunakan perintah RDTSC (Baca Penghitung Cap Waktu), tetapi satu masalah.. Itu tidak ada di TASM. Fakta bahwa itu tidak ada tidak ada gunanya bagi kita, karena semua perintah dalam TASM (sering disebut mnemonik) hanyalah pengganti OpCode, yang mendefinisikan setiap instruksi yang dapat dijalankan oleh CPU.

Ketika CPU Intel Pentium dirilis, sebuah OpCode untuk RDTSC disertakan, jadi jika Anda memiliki CPU darinya ke atas... Bagus.

Sekarang, bagaimana kita menjalankan instruksi RDTSC jika tidak ada di TASM? (tetapi tidak di CPU kami)

Di TASM, ada instruksi yang disebut db, dan dengannya kita dapat menjalankan OpCode secara langsung.

Seperti yang terlihat di sini, yang perlu kita lakukan untuk menjalankan RDTSC adalah: db 0Fh, 31h.

Dan itu saja! Anda sekarang dapat menjalankan instruksi ini dengan mudah, dan program Anda akan tetap berantakan, tetapi waktunya berantakan!

2
Coder's Crux 12 Mei 2021, 18:56

16

Di bawah DOS dan sistem operasi mode nyata lainnya, penghitung PIT 16-bit dapat dibaca langsung dari port yang relevan dalam dua potongan 8-bit, dan data ini dapat digabungkan dengan penghitung centang yang dikelola perangkat lunak untuk mencapai resolusi milidetik. Pada dasarnya, seseorang menggunakan penghitung centang 48-bit, di mana penghitung perangkat lunak 32-bit yang dikelola oleh BIOS merupakan bit yang paling signifikan, dan penghitung PIT 16-bit merupakan bit yang paling tidak signifikan.

Karena data tidak semua dibaca dalam satu gerakan, ada risiko kondisi balapan yang harus ditangani dengan tepat. Juga, beberapa BIOS digunakan untuk memprogram PIT sebagai generator gelombang persegi daripada penghitung laju sederhana. Meskipun ini tidak mengganggu tugas penambahan centang perangkat lunak, ini mengganggu kombinasi langsung dari register penghitung PIT dengan centang perangkat lunak. Ini memerlukan inisialisasi PIT satu kali untuk memastikan PIT beroperasi dalam mode penghitungan tarif.

Di bawah ini adalah kode perakitan 16-bit, dibungkus sebagai unit Turbo Pascal, yang saya gunakan selama bertahun-tahun untuk pengaturan waktu yang kuat dengan akurasi milidetik. Konversi dari jumlah centang ke milidetik di sini adalah sedikit kotak hitam. Saya kehilangan dokumentasi desain saya untuk itu dan tidak dapat dengan cepat merekonstruksinya dengan cepat sekarang. Seingat saya, perhitungan titik tetap ini memiliki jitter yang cukup kecil sehingga milidetik dapat diukur dengan andal. Konvensi pemanggilan Turbo-Pascal mengharuskan pengembalian hasil integer 32-bit dalam pasangan register DX:AX.

UNIT Time;   { Copyright (c) 1989-1993 Norbert Juffa }

INTERFACE

FUNCTION Clock: LONGINT;             { same as VMS; time in milliseconds }


IMPLEMENTATION

FUNCTION Clock: LONGINT; ASSEMBLER;
ASM
             PUSH    DS              { save caller's data segment }
             MOV     DS, Seg0040     {  access ticker counter }
             MOV     BX, 6Ch         { offset of ticker counter in segm.}
             MOV     DX, 43h         { timer chip control port }
             MOV     AL, 4           { freeze timer 0 }
             PUSHF                   { save caller's int flag setting }
             CLI                     { make reading counter an atomic operation}
             MOV     DI, DS:[BX]     { read BIOS ticker counter }
             MOV     CX, DS:[BX+2]
             STI                     { enable update of ticker counter }
             OUT     DX, AL          { latch timer 0 }
             CLI                     { make reading counter an atomic operation}
             MOV     SI, DS:[BX]     { read BIOS ticker counter }
             MOV     BX, DS:[BX+2]
             IN      AL, 40h         { read latched timer 0 lo-byte }
             MOV     AH, AL          { save lo-byte }
             IN      AL, 40h         { read latched timer 0 hi-byte }
             POPF                    { restore caller's int flag }
             XCHG    AL, AH          { correct order of hi and lo }
             CMP     DI, SI          { ticker counter updated ? }
             JE      @no_update      { no }
             OR      AX, AX          { update before timer freeze ? }
             JNS     @no_update      { no }
             MOV     DI, SI          { use second }
             MOV     CX, BX          {  ticker counter }
@no_update:  NOT     AX              { counter counts down }
             MOV     BX, 36EDh       { load multiplier }
             MUL     BX              { W1 * M }
             MOV     SI, DX          { save W1 * M (hi) }
             MOV     AX, BX          { get M }
             MUL     DI              { W2 * M }
             XCHG    BX, AX          { AX = M, BX = W2 * M (lo) }
             MOV     DI, DX          { DI = W2 * M (hi) }
             ADD     BX, SI          { accumulate }
             ADC     DI, 0           {  result }
             XOR     SI, SI          { load zero }
             MUL     CX              { W3 * M }
             ADD     AX, DI          { accumulate }
             ADC     DX, SI          {  result in DX:AX:BX }
             MOV     DH, DL          { move result }
             MOV     DL, AH          {  from DL:AX:BX }
             MOV     AH, AL          {   to }
             MOV     AL, BH          {    DX:AX:BH }
             MOV     DI, DX          { save result }
             MOV     CX, AX          {  in DI:CX }
             MOV     AX, 25110       { calculate correction }
             MUL     DX              {  factor }
             SUB     CX, DX          { subtract correction }
             SBB     DI, SI          {  factor }
             XCHG    AX, CX          { result back }
             MOV     DX, DI          {  to DX:AX }
             POP     DS              { restore caller's data segment }
END;


BEGIN
   Port [$43] := $34;                { need rate generator, not square wave }
   Port [$40] := 0;                  { generator as programmed by some BIOSes }
   Port [$40] := 0;                  { for timer 0 }
END. { Time }
2
njuffa 12 Mei 2021, 19:50