WinASM 和 Zen of Assembly 的问题 [英] Problems with WinASM and Zen of Assembly

查看:17
本文介绍了WinASM 和 Zen of Assembly 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一本迈克尔·亚布拉什 (Michael Abrash) 写的书,名为《装配之禅》.据我所知,所有代码清单都在 x86 程序集中.我下载了 WinASM 和 MASM,以便我可以组装和链接这些列表.然而,这些列表并不是开箱即用的——事实上,我根本无法让它们工作.例如,这里是第一个列表的代码:

I have a book called Zen of Assembly by Michael Abrash. All the code listings, as far as I know, are in x86 Assembly. I downloaded WinASM and MASM so I could assemble and link these listings. However, the listings don't work out of the box - in fact, I can't get them to work at all. For example, here is the code for the first listing:

;
; *** 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

<小时>

我把它保存为listing1.asm.当我在 WinASM 中创建一个新项目并选择标准 EXE、其他 EXE 或控制台应用程序作为我的项目类型时,当我执行 Go All 时我得到以下输出(这与我单独组装和链接时相同):<小时>

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 项目时,我得到以下输出:


And when I assemble and link it as a DOS Project, I get the following output:

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.

<小时>

我不确定这个错误是什么意思,或者是什么原因造成的.Zen of Assembly 这本书已有 20 多年的历史,因此它的设计和编写是为了在稍微不同的处理器上运行,但我的印象是大多数处理器都完全向后兼容,所以我认为这不是问题.无论如何,有谁知道是什么导致了这些错误消息?显然它们是链接器错误,它组装得很好,所以这让我觉得 MASM 附带的链接器有些不对劲——我应该下载一个新的还是什么?


I'm not sure what the error means, or why it is caused. The book, Zen of Assembly, is more than twenty years old so it was designed and written to be run on a slightly different processor but I was under the impression most processors are completely backwards compatible so I thought that wouldn't be an issue. Anyways, does anyone know what's causing these error messages? Obviously they're linker errors, it's assembling fine, so that gives me the idea that something isn't quite right with the linker MASM came with - should I download a new one or something?

顺便说一下,我使用的是 32 位操作系统,而不是 64 位操作系统,而且我运行的是 Windows 7.如果您对我用来执行此操作的计算机有任何疑问,请随时提问.

By the way, I'm on a 32 bit OS, not a 64 bit one, and I'm running Windows 7. If you have any questions about my computer I'm using to do this, feel free to ask.

推荐答案

问题是您的程序集文件不包含完整的程序,而只包含旨在从主程序中调用的个别例程.您可以将该程序集文件编译为一个目标文件,然后将该文件链接到您的主程序.

The problem is that your assembly file does not contain a complete program, but only individual routines that are designed to be called from your main program. You would compile this assembly file to an object file that you would then link to your main program.

链接器抱怨两件事:首先,您的程序没有声明堆栈段——请参阅here 一个随机示例,展示了如何执行此操作(请参阅 segment para stack 指令).其次,你的程序没有起始地址——你通常会使用 start: 标签来提供它.在 C 中对此的近似类比是没有 main() 函数.

The linker is complaining about two things: First, that your program does not declare a stack segment -- see here for a random example that shows how to do this (see the segment para stack directive). Second, your program has no starting address -- you would typically provide this using a start: label. An approximate analogy to this in C would be not having a main() function.

我建议您找到一个简单的程序模板来帮助您入门——Zen"假设您已经了解 x86 汇编编程的基础知识.例如,这里是适用于 16 位 DOS 的Hello World"版本.

I would suggest finding a simple program template to get you started -- "Zen" assumes that you already know the basics of x86 assembly programming. For example, here's a version of "Hello World" for 16-bit DOS.

顺便说一句,您发布的例程仅在 16 位 MS-DOS 环境中的 PC 上运行时才有效,因为它们直接访问 8253 定时器芯片.

By the way, the routines you posted will only work when running on a PC in a 16-bit MS-DOS environment because they directly access the 8253 timer chip.

这篇关于WinASM 和 Zen of Assembly 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆