2009-12-07 24 views
10

Tôi đang cố gắng tạo ra một thủ tục ngủ/trì hoãn trong 16bit MASM Assembly x86 sẽ in một ký tự trên màn hình mỗi 500ms. Từ nghiên cứu tôi đã làm, có vẻ như có ba phương pháp để đạt được điều này - Tôi muốn sử dụng một phương pháp sử dụng các đồng hồ CPU.Làm cách nào để tạo chức năng ngủ trong 16bit MASM Assembly x86?

Xin lưu ý rằng tôi đang chạy Windows XP thông qua VMWare Fusion trên Mac OS X Snow Leopard - Tôi không chắc liệu điều đó có ảnh hưởng gì không.

Có thể ai đó vui lòng chỉ cho tôi đúng hướng hoặc cung cấp một đoạn mã hoạt động mà tôi có thể tinh chỉnh không? Cảm ơn bạn!

Mã tôi tìm thấy được in là 'A' trên màn hình mỗi giây, nhưng không hoạt động (tôi muốn sử dụng phần nghìn giây).

TOP: 
MOV AH,2C 
INT 21 
MOV BH,DH ; DH has current second 
GETSEC:  ; Loops until the current second is not equal to the last, in BH 
MOV AH,2C 
INT 21 
CMP BH,DH ; Here is the comparison to exit the loop and print 'A' 
JNE PRINTA 
JMP GETSEC 
PRINTA: 
MOV AH,02 
MOV DL,41 
INT 21 
JMP TOP 

EDIT: Làm theo lời khuyên của GJ, đây là quy trình làm việc. Chỉ cần gọi nó là

DELAY PROC 
TIMER: 
MOV  AH, 00H 
INT  1AH 
CMP  DX,WAIT_TIME 
JB  TIMER 
ADD  DX,3   ;1-18, where smaller is faster and 18 is close to 1 second 
MOV  WAIT_TIME,DX 
RET 
DELAY ENDP 
+0

bạn đang chạy mã của bạn trong Userland? Trên Windows? – nico

+0

Có. Tôi đang chạy nó trên Windows thông qua một máy ảo, như đã nêu trong bài viết của tôi :) –

+1

Hãy cẩn thận lúc nửa đêm bạn có thể có một vấn đề. Có lẽ là ý tưởng tốt hơn để đọc trực tiếp vị trí bộ nhớ đánh dấu tại 0x0040: 0x0070. Đọc thêm: http://www.merlyn.demon.co.uk/pas-time.htm#RDT –

Trả lời

3

Thực ra bạn có thể sử dụng chức năng ngắt BIOS BIOS 1Ah 00h, 'Đọc số đếm đồng hồ hiện tại'. Hoặc bạn có thể đọc dword tại địa chỉ $ 40: $ 6C nhưng bạn phải đảm bảo đọc nguyên tử. Nó được tăng lên bởi MS-DOS vào khoảng 18.2 Hz.

Để biết thêm thông tin đọc: The DOS Clock

+3

'Liên kết DOS Clock' dẫn tôi đến một trang trống. –

9

Điều này không thể thực hiện trong MASM thuần túy. Tất cả các thủ thuật cũ để thiết lập độ trễ cố định hoạt động dựa trên giả định rằng bạn có toàn quyền điều khiển máy và là luồng chỉ chạy trên CPU, để nếu bạn đợi 500 triệu chu kỳ, chính xác 500,000,000/f giây sẽ trôi qua (đối với CPU ở tần số f); đó sẽ là 500ms cho một bộ xử lý 1GHz.

Bởi vì bạn đang chạy trên hệ điều hành hiện đại, bạn đang chia sẻ CPU với nhiều luồng khác (trong số đó, hạt nhân - bất kể bạn làm gì, bạn không thể ưu tiên hạt nhân!), Vì vậy hãy đợi 500 triệu chu kỳ chỉ trong chủ đề của bạn sẽ có nghĩa là hơn 500 triệu chu kỳ trôi đi trong thế giới thực. Vấn đề này không thể được giải quyết bằng mã vùng không gian người dùng; bạn sẽ cần sự hợp tác của hạt nhân.

Cách thích hợp để giải quyết vấn đề này là tra cứu hàm Win32 API sẽ treo chuỗi của bạn trong một số mili giây cụ thể, sau đó chỉ cần gọi hàm đó. Bạn sẽ có thể làm điều này trực tiếp từ hội đồng, có thể với các đối số bổ sung cho mối liên kết của bạn. Hoặc, có thể có một hệ thống hạt nhân NT gọi để thực hiện chức năng này (tôi có rất ít kinh nghiệm với các cuộc gọi hệ thống NT, và thành thật không biết bảng gọi hệ thống NT trông như thế nào, nhưng chức năng ngủ là thứ tôi có thể mong đợi để xem). Nếu một cuộc gọi hệ thống có sẵn, sau đó phát hành một cuộc gọi hệ thống trực tiếp từ lắp ráp có lẽ là cách nhanh nhất để làm những gì bạn muốn; nó cũng là di động ít nhất (nhưng sau đó, bạn đang viết lắp ráp!).

Sửa: Nhìn vào the NT kernel system call table, có dường như không có bất kỳ cuộc gọi liên quan đến giấc ngủ hoặc nhận được ngày và thời gian (như mã gốc của bạn sử dụng), nhưng có một số hệ thống các cuộc gọi đến thiết lập và giờ truy vấn . Quay trong khi bạn chờ một bộ hẹn giờ để đạt đến độ trễ mong muốn là một hiệu quả, nếu không thích hợp, giải pháp.

+0

trước hết, cảm ơn bạn rất nhiều vì phản hồi phức tạp. Hãy để tôi mở rộng các tùy chọn, sau đó. Làm thế nào để thực hiện cuộc gọi mỗi khoảng thời gian cố định có thể rất từ ​​máy đến máy (tức là tôi không nhớ nó chạy mỗi 500ms trên một máy và mỗi 150ms trên một máy khác). –

+1

Tôi nghĩ rằng bạn có nghĩa là "chu kỳ/f" và không "f/chu kỳ". –

+0

kigurai: Đã sửa lỗi. Thở dài. yuval: Thành thật mà nói, tôi không quen thuộc với các API của NT để biết làm thế nào bạn có thể làm điều đó ra khỏi đỉnh đầu của tôi; Tôi chỉ có thể đề xuất những gì tôi luôn làm trong tình huống này: xem bảng gọi hệ thống và xem những gì bạn có thể xây dựng với các công cụ bạn có! – kquinn

2

Vâng, sau đó. Một phong cách cũ, không liên tục, tiêu thụ điện năng chậm trễ vòng lặp mà sẽ làm cho chủ đề khác chạy chậm xuống sẽ trông như thế:

 delay equ 5000 

top: mov ax, delay 
loopa: mov bx, delay 
loopb: dec bx 
     jnc loopb 
     dec ax 
     jnc loopa 

     mov ah,2 
     mov dl,'A' 
     int 21 
     jmp top 

Việc chậm trễ là bậc hai để hằng số. Nhưng nếu bạn sử dụng vòng lặp chậm trễ này, một nơi nào đó trên thế giới một con mèo con vô tội trẻ sẽ chết.

1

tôi đã không kiểm tra mã này, nhưng khái niệm phải làm việc ... Lưu/khôi phục es đăng ký là không bắt buộc! Kiểm tra mã cẩn thận!

DelayProcedure: 
    push es      //Save es and load new es 
    mov ax, 0040h 
    mov es, ax 
//Pseudo atomic read of 32 bit DOS time tick variable 
PseudoAtomicRead1: 
    mov ax, es:[006ch] 
    mov dx, es:[006eh] 
    cmp ax, es:[006ch] 
    jne PseudoAtomicRead1 
//Add time delay to dx,ax where smaller is faster and 18 is close to 1 second 
    add ax, 3 
    adc dx, 0 
//1800AFh is last DOS time tick value so check day overflow 
    mov cx, ax 
    mov bx, dx 
//Do 32 bit subtract/compare 
    sub cx, 00AFh 
    sbb dx, 0018h 
    jbe DayOverflow 
//Pseudo atomic read of 32 bit DOS time tick variable 
PseudoAtomicRead2: 
    mov cx, es:[006ch] 
    mov bx, es:[006eh] 
    cmp cx, es:[006ch] 
    jne PseudoAtomicRead2 
NotZero: 
//At last do 32 bit compare 
    sub cx, ax 
    sbb bx, dx 
    jae Exit 
//Check again day overflow because task scheduler can overjumps last time ticks 
    inc bx    //If no Day Overflow then bx = 0FFh 
    jz PseudoAtomicRead2 
    jmp Exit 
DayOverflow: 
//Pseudo atomic read of 32 bit DOS time tick variable 
PseudoAtomicRead3: 
    mov ax, es:[006ch] 
    mov dx, es:[006eh] 
    cmp dx, es:[006ch] 
    jne PseudoAtomicRead3 
//At last do 32 bit compare 
    sub ax, cx 
    sbb dx, bx 
    jb PseudoAtomicRead3 
Exit: 
    pop es      //Restore es 
    ret 
0

..Vấn đề với tất cả các ví dụ mã ở trên là chúng sử dụng các hoạt động không chặn. Nếu bạn kiểm tra việc sử dụng CPU trong một thời gian chờ đợi tương đối dài, bạn sẽ thấy nó chạy khoảng 50%. Những gì chúng tôi muốn là sử dụng một số chức năng DOS hoặc BIOS ngăn chặn thực hiện để sử dụng CPU là gần 0%.

..Bắt đầu, BIOS INT 16h, chức năng AH = 1 sẽ xuất hiện trong đầu bạn. Bạn có thể tạo ra một thói quen gọi hàm đó, sau đó chèn một tổ hợp phím vào bộ đệm bàn phím khi hết thời gian. Có rất nhiều vấn đề với ý tưởng đó;), nhưng nó có thể là thức ăn cho sự suy nghĩ. Có khả năng là bạn sẽ viết một số loại trình xử lý ngắt.

..Trong API cửa sổ 32 bit, có chức năng "Ngủ". Tôi cho rằng bạn có thể thunk vào đó.

4

sử dụng INT 15h, chức năng 86h:

Gọi Với: AH = 86h CX: DX = khoảng thời gian trong uS

+0

Đây là câu trả lời hay nhất. – clearlight

Các vấn đề liên quan