2012-07-20 91 views
0

我有一本書叫Michael of Abrash的大會禪。據我所知,所有的代碼清單都在x86彙編中。我下載了WinASM和MASM,所以我可以組裝並鏈接這些列表。但是,這些列表並不適用 - 事實上,我根本無法讓他們工作。例如,這裏是首批上市代碼:我救它作爲listing1.asmWinASM和禪宗大會的問題


; 
; *** Listing 2-1 *** 
; 
; The precision Zen timer (PZTIMER.ASM) 
; 
; Uses the 8253 timer to time the performance of code that takes 
; less than about 54 milliseconds to execute, with a resolution 
; of better than 10 microseconds. 
; 
; By Michael Abrash 4/26/89 
; 
; Externally callable routines: 
; 
; ZTimerOn: Starts the Zen timer, with interrupts disabled. 
; 
; ZTimerOff: Stops the Zen timer, saves the timer count, 
; times the overhead code, and restores interrupts to the 
; state they were in when ZTimerOn was called. 
; 
; ZTimerReport: Prints the net time that passed between starting 
; and stopping the timer. 
; 
; Note: If longer than about 54 ms passes between ZTimerOn and 
; ZTimerOff calls, the timer turns over and the count is 
; inaccurate. When this happens, an error message is displayed 
; instead of a count. The long-period Zen timer should be used 
; in such cases. 
; 
; Note: Interrupts *MUST* be left off between calls to ZTimerOn 
; and ZTimerOff for accurate timing and for detection of 
; timer overflow. 
; 
; Note: These routines can introduce slight inaccuracies into the 
; system clock count for each code section timed even if 
; timer 0 doesn't overflow. If timer 0 does overflow, the 
; system clock can become slow by virtually any amount of 
; time, since the system clock can't advance while the 
; precison timer is timing. Consequently, it's a good idea 
; to reboot at the end of each timing session. (The 
; battery-backed clock, if any, is not affected by the Zen 
; timer.) 
; 
; All registers, and all flags except the interrupt flag, are 
; preserved by all routines. Interrupts are enabled and then disabled 
; by ZTimerOn, and are restored by ZTimerOff to the state they were 
; in when ZTimerOn was called. 
; 

Code segment word public 'CODE' 
    assume cs:Code, ds:nothing 
    public ZTimerOn, ZTimerOff, ZTimerReport 

; 
; Base address of the 8253 timer chip. 
; 
BASE_8253  equ 40h 
; 
; The address of the timer 0 count registers in the 8253. 
; 
TIMER_0_8253  equ BASE_8253 + 0 
; 
; The address of the mode register in the 8253. 
; 
MODE_8253  equ BASE_8253 + 3 
; 
; The address of Operation Command Word 3 in the 8259 Programmable 
; Interrupt Controller (PIC) (write only, and writable only when 
; bit 4 of the byte written to this address is 0 and bit 3 is 1). 
; 
OCW3   equ 20h 
; 
; The address of the Interrupt Request register in the 8259 PIC 
; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0 
; of OCW3 = 0). 
; 
IRR   equ 20h 
; 
; Macro to emulate a POPF instruction in order to fix the bug in some 
; 80286 chips which allows interrupts to occur during a POPF even when 
; interrupts remain disabled. 
; 
MPOPF macro 
    local p1, p2 
    jmp short p2 
p1: iret   ;jump to pushed address & pop flags 
p2: push cs  ;construct far return address to 
    call p1  ; the next instruction 
    endm 

; 
; Macro to delay briefly to ensure that enough time has elapsed 
; between successive I/O accesses so that the device being accessed 
; can respond to both accesses even on a very fast PC. 
; 
DELAY macro 
    jmp $+2 
    jmp $+2 
    jmp $+2 
    endm 

OriginalFlags  db ? ;storage for upper byte of 
        ; FLAGS register when 
        ; ZTimerOn called 
TimedCount  dw ? ;timer 0 count when the timer 
        ; is stopped 
ReferenceCount  dw ? ;number of counts required to 
        ; execute timer overhead code 
OverflowFlag  db ? ;used to indicate whether the 
        ; timer overflowed during the 
        ; timing interval 
; 
; String printed to report results. 
; 
OutputStr label byte 
     db 0dh, 0ah, 'Timed count: ', 5 dup (?) 
ASCIICountEnd label byte 
     db ' microseconds', 0dh, 0ah 
     db '$' 
; 
; String printed to report timer overflow. 
; 
OverflowStr label byte 
    db 0dh, 0ah 
    db '****************************************************' 
    db 0dh, 0ah 
    db '* The timer overflowed, so the interval timed was *' 
    db 0dh, 0ah 
    db '* too long for the precision timer to measure.  *' 
    db 0dh, 0ah 
    db '* Please perform the timing test again with the *' 
    db 0dh, 0ah 
    db '* long-period timer.        *' 
    db 0dh, 0ah 
    db '****************************************************' 
    db 0dh, 0ah 
    db '$' 

;******************************************************************** 
;* Routine called to start timing.     * 
;******************************************************************** 

ZTimerOn proc near 

; 
; Save the context of the program being timed. 
; 
    push ax 
    pushf 
    pop ax   ;get flags so we can keep 
        ; interrupts off when leaving 
        ; this routine 
    mov cs:[OriginalFlags],ah ;remember the state of the 
        ; Interrupt flag 
    and ah,0fdh   ;set pushed interrupt flag 
        ; to 0 
    push ax 
; 
; Turn on interrupts, so the timer interrupt can occur if it's 
; pending. 
; 
    sti 
; 
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause 
; linear counting rather than count-by-two counting. Also 
; leaves the 8253 waiting for the initial timer 0 count to 
; be loaded. 
; 
    mov al,00110100b  ;mode 2 
    out MODE_8253,al 
; 
; Set the timer count to 0, so we know we won't get another 
; timer interrupt right away. 
; Note: this introduces an inaccuracy of up to 54 ms in the system 
; clock count each time it is executed. 
; 
    DELAY 
    sub al,al 
    out TIMER_0_8253,al  ;lsb 
    DELAY 
    out TIMER_0_8253,al  ;msb 
; 
; Wait before clearing interrupts to allow the interrupt generated 
; when switching from mode 3 to mode 2 to be recognized. The delay 
; must be at least 210 ns long to allow time for that interrupt to 
; occur. Here, 10 jumps are used for the delay to ensure that the 
; delay time will be more than long enough even on a very fast PC. 
; 
    rept 10 
    jmp $+2 
    endm 
; 
; Disable interrupts to get an accurate count. 
; 
    cli 
; 
; Set the timer count to 0 again to start the timing interval. 
; 
    mov al,00110100b  ;set up to load initial 
    out MODE_8253,al  ; timer count 
    DELAY 
    sub al,al 
    out TIMER_0_8253,al  ;load count lsb 
    DELAY 
    out TIMER_0_8253,al  ;load count msb 
; 
; Restore the context and return. 
; 
    MPOPF    ;keeps interrupts off 
    pop ax 
    ret 

ZTimerOn endp 

;******************************************************************** 
;* Routine called to stop timing and get count.    * 
;******************************************************************** 

ZTimerOff proc near 

; 
; Save the context of the program being timed. 
; 
    push ax 
    push cx 
    pushf 
; 
; Latch the count. 
; 
    mov al,00000000b  ;latch timer 0 
    out MODE_8253,al 
; 
; See if the timer has overflowed by checking the 8259 for a pending 
; timer interrupt. 
; 
    mov al,00001010b  ;OCW3, set up to read 
    out OCW3,al   ; Interrupt Request register 
    DELAY 
    in al,IRR   ;read Interrupt Request 
        ; register 
    and al,1   ;set AL to 1 if IRQ0 (the 
        ; timer interrupt) is pending 
    mov cs:[OverflowFlag],al ;store the timer overflow 
        ; status 
; 
; Allow interrupts to happen again. 
; 
    sti 
; 
; Read out the count we latched earlier. 
; 
    in al,TIMER_0_8253  ;least significant byte 
    DELAY 
    mov ah,al 
    in al,TIMER_0_8253  ;most significant byte 
    xchg ah,al 
    neg ax   ;convert from countdown 
        ; remaining to elapsed 
        ; count 
    mov cs:[TimedCount],ax 
; Time a zero-length code fragment, to get a reference for how 
; much overhead this routine has. Time it 16 times and average it, 
; for accuracy, rounding the result. 
; 
    mov cs:[ReferenceCount],0 
    mov cx,16 
    cli    ;interrupts off to allow a 
        ; precise reference count 
RefLoop: 
    call ReferenceZTimerOn 
    call ReferenceZTimerOff 
    loop RefLoop 
    sti 
    add cs:[ReferenceCount],8 ;total + (0.5 * 16) 
    mov cl,4 
    shr cs:[ReferenceCount],cl ;(total)/16 + 0.5 
; 
; Restore original interrupt state. 
; 
    pop ax   ;retrieve flags when called 
    mov ch,cs:[OriginalFlags] ;get back the original upper 
        ; byte of the FLAGS register 
    and ch,not 0fdh  ;only care about original 
        ; interrupt flag... 
    and ah,0fdh   ;...keep all other flags in 
        ; their current condition 
    or ah,ch   ;make flags word with original 
        ; interrupt flag 
    push ax   ;prepare flags to be popped 
; 
; Restore the context of the program being timed and return to it. 
; 
    MPOPF    ;restore the flags with the 
        ; original interrupt state 
    pop cx 
    pop ax 
    ret 

ZTimerOff endp 

; 
; Called by ZTimerOff to start timer for overhead measurements. 
; 

ReferenceZTimerOn proc near 
; 
; Save the context of the program being timed. 
; 
    push ax 
    pushf  ;interrupts are already off 
; 
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause 
; linear counting rather than count-by-two counting. 
; 
    mov al,00110100b ;set up to load 
    out MODE_8253,al ; initial timer count 
    DELAY 
; 
; Set the timer count to 0. 
; 
    sub al,al 
    out TIMER_0_8253,al ;load count lsb 
    DELAY 
    out TIMER_0_8253,al ;load count msb 
; 
; Restore the context of the program being timed and return to it. 
; 
    MPOPF 
    pop ax 
    ret 

ReferenceZTimerOn endp 

; 
; Called by ZTimerOff to stop timer and add result to ReferenceCount 
; for overhead measurements. 
; 

ReferenceZTimerOff proc near 
; 
; Save the context of the program being timed. 
; 
    push ax 
    push cx 
    pushf 
; 
; Latch the count and read it. 
; 
    mov al,00000000b  ;latch timer 0 
    out MODE_8253,al 
    DELAY 
    in al,TIMER_0_8253  ;lsb 
    DELAY 
    mov ah,al 
    in al,TIMER_0_8253  ;msb 
    xchg ah,al 
    neg ax   ;convert from countdown 
        ; remaining to amount 
        ; counted down 
    add cs:[ReferenceCount],ax 
; 
; Restore the context of the program being timed and return to it. 
; 
    MPOPF 
    pop cx 
    pop ax 
    ret 

ReferenceZTimerOff endp 

;******************************************************************** 
;* Routine called to report timing results.    * 
;******************************************************************** 

ZTimerReport proc near 

    pushf 
    push ax 
    push bx 
    push cx 
    push dx 
    push si 
    push ds 
; 
    push cs ;DOS functions require that DS point 
    pop ds ; to text to be displayed on the screen 
    assume ds:Code 
; 
; Check for timer 0 overflow. 
; 
    cmp [OverflowFlag],0 
    jz PrintGoodCount 
    mov dx,offset OverflowStr 
    mov ah,9 
    int 21h 
    jmp short EndZTimerReport 
; 
; Convert net count to decimal ASCII in microseconds. 
; 
PrintGoodCount: 
    mov ax,[TimedCount] 
    sub ax,[ReferenceCount] 
    mov si,offset ASCIICountEnd - 1 
; 
; Convert count to microseconds by multiplying by .8381. 
; 
    mov dx,8381 
    mul dx 
    mov bx,10000 
    div bx  ;* .8381 = * 8381/10000 
; 
; Convert time in microseconds to 5 decimal ASCII digits. 
; 
    mov bx,10 
    mov cx,5 
CTSLoop: 
    sub dx,dx 
    div bx 
    add dl,'0' 
    mov [si],dl 
    dec si 
    loop CTSLoop 
; 
; Print the results. 
; 
    mov ah,9 
    mov dx,offset OutputStr 
    int 21h 
; 
EndZTimerReport: 
    pop ds 
    pop si 
    pop dx 
    pop cx 
    pop bx 
    pop ax 
    MPOPF 
    ret 

ZTimerReport endp 

Code ends 
    end 

。當我做出WinASM一個新項目,並選擇使用標準EXE,其他的EXE或控制檯應用程序作爲我的項目類型,我得到下面的輸出,當我做全力以赴(它是相同的,當我彙編和鏈接seperately):


C:\masm32\bin\ML /c /coff /Cp /nologo /I"C:\masm32\include" "C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.asm" 

Assembling: C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.asm 

C:\masm32\bin\Link @"C:\Users\Lincoln\Desktop\WinAsm\Projects\link.war" 

Microsoft (R) Incremental Linker Version 5.12.8078 
Copyright (C) Microsoft Corp 1992-1998. All rights reserved. 

/SUBSYSTEM:WINDOWS /RELEASE /VERSION:4.0 "/LIBPATH:C:\masm32\lib" "C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.obj" "/OUT:C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.exe" 
listing1.obj : fatal error LNK1190: invalid fixup found, type 0x0001 

Make finished. 1 error(s) occured. 

當我組裝,並將其鏈接作爲一個DOS項目,我得到下面的輸出:


C:\masm32\bin\ML /c /I"C:\masm32\include" "C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.asm" 

Microsoft (R) Macro Assembler Version 6.14.8444 
Copyright (C) Microsoft Corp 1981-1997. All rights reserved. 

Assembling: C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.asm 

C:\masm32\bin\Link16 @"C:\Users\Lincoln\Desktop\WinAsm\Projects\link.war" 


Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 1994 
Copyright (C) Microsoft Corp 1984-1993. All rights reserved. 

Object Modules [.obj]: C:\Users\Lincoln\Desktop\WinAsm\Projects\listing1.objj 
LINK : warning L4021: no stack segment 
LINK : warning L4038: program has no starting address 

Make finished. 2 error(s) occured. 

我不知道是什麼錯誤意味着,或者它爲什麼引起的。這本書「大會之禪」已經有二十多年的歷史,所以它的設計和編寫是爲了在一個稍微不同的處理器上運行,但我的印象是大多數處理器完全向後兼容,所以我認爲這不是一個問題。無論如何,有沒有人知道是什麼導致這些錯誤信息?很明顯,他們是鏈接器錯誤,它的組裝很好,所以這給了我一個想法,即鏈接器MASM帶來了一些不太正確的東西 - 我應該下載一個新的還是什麼?

順便說一句,我在一個32位的操作系統,而不是64位的一個,和我運行Windows 7.如果您有關於我的電腦有任何問題,我使用要做到這一點,隨意問。

+0

你試過nasm嗎?很好的書btw,理解它出來時已經過時了。最重要的不是關於編程,而是關於8088或8086的細微差別,而是如何「思考」這個問題,然後再次猜測自己,嘗試一些瘋狂的事情並對它們進行計時(並且明白你可以用計時器來犯錯誤所以即使是第二次猜測)。 – 2012-07-20 02:50:06

+0

我沒有,但我會嘗試NASM。是的,即使我無法獲得列表,我也很期待閱讀這本書。 – 2012-07-20 02:59:08

+0

我試圖與NASM組裝,並得到一些奇怪的錯誤;如果你熟悉它,也許你提供幫助,dwelch:http://stackoverflow.com/questions/11572307/nasm-error-parsing-instruction-expected – 2012-07-20 03:10:15

回答

2

的問題是,你的彙編文件不包含一個完整的程序,但只被設計單獨的程序,以從你的主程序調用。你可以將這個程序集文件編譯成一個目標文件,然後你可以鏈接到你的主程序。

鏈接器抱怨了兩件事:首先,你的程序沒有聲明一個堆棧段 - 參見here的隨機例子,說明如何做到這一點(請參閱segment para stack指令)。其次,你的程序沒有起始地址 - 你通常會使用start:標籤來提供。在C中的近似比喻將不具有main()函數。

我建議找一個簡單的程序模板,讓你開始 - 「禪」假設你已經知道x86彙編編程的基礎知識。例如,here是16位DOS的「Hello World」版本。

順便說一句,您發佈的程序將僅在16位MS-DOS環境中的PC上運行時,因爲它們直接訪問8253計時器芯片工作。

+0

非常感謝,很好地回答了我的問題。 – 2012-07-20 14:06:40

+0

@LincolnB很高興聽到它!順便說一句,建議您應該通過點擊「打勾」圖標「接受」最有用的答案 - 更多信息:http://stackoverflow.com/faq/#howtoask – 2012-07-23 08:35:04