【问题标题】:Assembly calculate sine example. nasm 16 dos装配计算正弦示例。 NASM 16 DOS
【发布时间】:2014-08-27 00:50:11
【问题描述】:

我正在寻找如何使用协处理器计算正弦的示例。 我找到了函数:

CalcSin 
        fld     long [angle]            ; st(0) = angle 
        fsin                            ; st(0) = sin(angle)  (angle is in radians) 
        fstp    long [SinX]             ; SinX = sin(angle) 

我想画正弦,我需要在ax 中输入 Y,在 bx 中输入 X。 X 没问题,因为我会循环,但是 Y 我有问题。 X 将从示例 0 到 350(如像素)。 如果例如 sin(30 度)为 1/2,如何计算它并为像素设置 Y。 如何对结果进行四舍五入以获得良好的坐标?


编辑: 很抱歉,但是当我运行您的代码时,它显示我没有鼻窦,而是 2 行。我不知道我现在做错了什么

    segment .data

    segment .code
..start:
    mov ax, 13h 
    int 10h               ; switch to 320x200 mode

    mov ax, 0a000h        ; The offset to video memory
    mov es, ax            ; We load it to ES through AX,
                          ; because immediate operation
                          ; is not allowed on ES

;;;;;;;;;;;;;;;;;;;;;;

DrawWave:
    mov ebx, y  ; EBX = &y
    mov ecx, 0

    ; let st(1) = 2*PI/640
    fldpi               ; st(0) = PI
    fidiv dword [step]  ; st(0)/160 = 0.009817...
    fldz                ; st(0) = 0.0, st(1) = 0.00045...

    .loop:
        fld st0   ; duplicate the x on the top
        fsin        ; st(0) = sin x

        fimul dword [imgHeight]     ; st(0) = y*240
        fiadd dword [imgHeight]     ; eliminate negative coordinate by translating the wave vertically          
        fistp dword [y]             ; store y to ´y´

        fadd st0, st1           ; add the step value to x, doing the step

        ;draw pixel at [*EAX:ECX]
        push ax
        push bx
        push cx
        call DrawPixel
        pop cx
        pop bx
        pop ax

        inc ecx
        cmp ecx, 320    ; perform 640 steps to draw a single sine wave
        jl .loop

        fstp st0  ;clean up
        fstp st0  ;clean up
    ret

;;;;;;;;;;;;;;;;;;;;;;;;;

    xor ah, ah
    int 16h               ; keyboard (wait for key)

    mov ax, 3
    int 10h               ; go to text mode

    mov ax, 4c00h
    int 21h               ; return to DOS, exit code 0

;;;;;;;;;;;;;;;;;;;;;

; EBX = in &CoordY
; ECX = CoordX  
;DrawPixel:
    ; draw a pixel at [*EBX:ECX]
 ;   ret 
DrawPixel:
    push dx               ; mul changes dx too
    mov ax, cx            ; ax is X coord copy from cx
    mov cx, 320
    mul cx                ; multiply Y (ax) by 320 (one row)
    add ax, bx            ; and add X (bx) (result= dx:ax)
    mov di, ax
    pop dx
    mov dl, 4
    mov [es:di], dl       ; store color/pixel
    ret

    ;CONSTANTS:

step: dw 160        ; 2/320 = 160
imgWidth: dw 320    ; 320px
imgHeight: dw 200/2 ; 200px on half, because Y also gets negative
;VARIABLES:
x: dw 0     ; a tmp place to save X
y: dw 0     ; a tmp place to save Y

【问题讨论】:

  • '我想画正弦' - 你是什么意思?正弦波?
  • 是的。稍后将是正弦波动画。但我想从正弦波开始
  • 我正在寻找答案。
  • 谢谢。当我有我的鼻窦时,我会更容易在上面放字母(我的任务是正弦滚动动画。我已经得到了字母)。

标签: assembly draw nasm angle dos


【解决方案1】:

我的两分钱;-)。 X 是度数,Y 是按比例缩放 (*50) 的正弦辐射。 (隐藏的)X 轴位于第 100 行。

segment stack stack
    resb 0x1000

segment .data
    ; no data

segment .code
..start:
main:
    mov ax, data            ; Initialize DS (needed for .exe-program)
    mov ds, ax
    mov ax, 0x0A000         ; Segment to video memory
    mov es, ax

    mov ax, 13h
    int 10h                 ; switch to 320x200 mode

    mov cx, 0
    .l1:
    push cx                 ; store CX
    call get_sine
    add ax, 100             ; shift Y to position of X-axis (100)
    mov bx, cx
    call vector_to_memory
    mov di, ax
    mov al, 0x0F            ; white
    mov [es:di], al         ; put pixel
    pop cx                  ; restore CX
    inc cx                  ; CX = CX + 1
    cmp cx, 320             ; right boarder reached?
    jne .l1                 ; no, next degree

    xor ah, ah
    int 16h                 ; keyboard (wait for key)
    mov ax, 3
    int 10h                 ; go to text mode
    mov ax, 0x4C00
    int 21h                 ; return to DOS, exit code 0

get_sine:                   ; Args: CX = angle (degree!)
    push cx                 ; = sub sp, 2 (local stack space), mov [sp], cx
    mov bp, sp              ; BP = SP (local stack space) for FPU-accesses
    fild word [bp]          ; ST(0): CX
    fldpi                   ; ST(0)=Pi, ST(1)=CX
    fmulp                   ; ST(0)=Pi*CX
    mov word [bp], 180
    fidiv word [bp]         ; ST(0)=(Pi*CX)/180 (formula for degree to rad)
    fsin                    ; ST(0)=sine (rad)
    mov word [bp], 50       ; Scale the result by 50 (e.g. 0.8 => 40.0)
    fimul word [bp]         ; ST(0)=sine*scale
    fchs                    ; reverse sign because video counts from top to bottom
    fistp word [bp]         ; store integer with rounding to local stack space
    pop ax                  ; AX = local stack space
    ret                     ; Return: AX = Y (signed!)

vector_to_memory:           ; Args: BX = X, AX = Y
    push dx                 ; mul changes dx too
    mov cx, 320             ; video mode width
    mul cx                  ; DX:AX = AX * CX
    add ax, bx              ; left indentation
    pop dx
    ret                     ; Return: AX = offset in memory

【讨论】:

    【解决方案2】:

    另一种方法是创建和使用自己的正弦/余弦表,我们只能将一次存储到文件中,并用于多次加载和使用。以下示例展示了如何创建自己的正弦/余弦表。

          Grad    = 360
          Endtab  = 450
    
    segment .data
    
    SINTAB DB Endtab DUP (?,?,?,?)
    
    TEIL   DW 180, ?
    I      DW 0, 0
    
    TABNAM DB "Sin.tab"
    
    segment .code
    
    START:    mov     ax, data
              mov     ds, ax
              finit
              call TABLE
              mov     dx, TABNAM
              call MAKDAT
              xor     dx, dx
              mov     cx, Endtab*4
              call WRITE
              call CLOSE
              mov     ax, 4C00h
              int   21h
    
    TABLE:    xor     di, di          ; Create sine table
    TAB:      fldpi
              fimul  DWORD[I]
              fidiv  DWORD[TEIL]      ; by 180(INT)
              fsin
              fstp   DWORD[di]
              inc    WORD[I]
              add     di, 4
              cmp    WORD[I], Endtab
              jnz TAB
              ret
    
    MAKDAT:   mov     ah, 3Ch
              xor     cx, cx
              int  21h       ; we hope that no error occur
              mov     bx, ax ; ....but better insert a handling for
              ret
    
    WRITE:    mov     ah, 40h
              int   21h       ; ....also here
              ret
    
    CLOSE:    mov     ah, 3Eh
              int   21h
              ret
    

    还可以创建和使用 整数 正弦/余弦表。但是它的处理有点不同,因为正弦/余弦值是相乘的,所以我们必须将我们想要计算的值也乘以相同的乘数,最后我们必须对结果进行除法。

          Grad    = 360 * 2
          Endtab  = 450 * 2
          Foktor  = 10000h   ; for to replace/shift the floating point of the value
    
    segment .data
    
    SINTAB DB Endtab DUP (?,?,?,?)
    TEIL   DW Grad/2, ?
    I      DW 0, 0
    FAKT   DD Foktor
    TABNAM DB "SinInt.tab", 0
    
    segment .code
    
    START:    ; same main-routine
    
    TABLE:    xor     di, di     ; subroutine for to create an integer table
    TAB:      fldpi
              fimul  DWORD[I]
              fidiv  DWORD[TEIL]
              fsin
              fimul  DWORD[FAKT]
              fistp  DWORD[di]
              inc    WORD[I]
              add     di, 4
              cmp    WORD[I], Endtab
              jnz TAB
              ret
    
    ; same subroutines for create, write and store file
    

    为了获得更高的计算精度(例如更高的屏幕分辨率以防止孔洞),我们可以简单地将循环计数器的 360 度值加倍。

    【讨论】:

    • 感谢您的回答和 rkhb。如果我有足够的声望点,我会给你箭头。以我的方式,我也会计算一次并使用正弦表,但在学校里他们希望我知道如何使用协处理器。
    【解决方案3】:

    如果我对您的理解正确,您想绘制一个没有任何平移和缩放的正弦波。因此,您可以将angle 作为您的X 坐标,而您从函数f(x) = sin x 获得的值就是您的Y 坐标。

    ; EAX = in &CooordX
    ; EBX = out &CoordY
    SinX:
        fld     qword [eax]           ; st(0) = angle 
        fsin                         ; st(0) = sin(angle)
        fstp    qword [ebx]           ; *ebx = sin(angle) 
        ret
    

    现在假设您要绘制一个波浪。这意味着在绘制最后一个像素时,x == 2*PI rad(全波)必须为真。对于 640 像素宽的屏幕,x 在绘图循环中的单个步骤是2*PI/640 = 0.009817。剩下的很简单。

    ;CONSTANTS:
    step: dw 320        ; 2/640 = 320, omitted PI
    imgWidth: dw 640    ; 640px
    imgHeight: dw 480/2 ; 480px on half, because Y also gets negative
    
    ;VARIABLES:
    y: dw 0     ; a tmp place to save Y
    
    DrawWave:
        mov ebx, y  ; EBX = &y
        mov ecx, 0
    
        ; let st(1) = 2*PI/640
        fldpi               ; st(0) = PI
        fidiv dword [step]  ; st(0)/320 = 0.009817...
        fldz                ; st(0) = 0.0, st(1) = 0.009817...
    
        .loop:
            fld st(0)   ; duplicate the x on the top
            fsin        ; st(0) = sin x
    
            fimul dword [imgHeight]     ; st(0) = y*240
            fiadd dword [imgHeight]     ; eliminate negative coordinate by translating the wave vertically          
            fistp dword [y]             ; store y to ´y´
    
            fadd st(0), st(1)           ; add the step value to x, doing the step
    
            ;draw pixel at [*EAX:ECX]
            call DrawPixel
    
            inc ecx
            cmp ecx, 640    ; perform 640 steps to draw a single sine wave
            jl .loop
    
            fstp st(0)  ;clean up
            fstp st(0)  ;clean up
        ret
    
    ; EBX = in &CoordY
    ; ECX = CoordX  
    DrawPixel:
        ; draw a pixel at [*EBX:ECX]
        ret
    

    【讨论】:

    • 我已经编辑了我的问题。我看不到任何结果。我使用 NASM 16 DOS 和模式 320x200
    • 这只是一种用于绘制正弦波的算法,而不是用于绘制实际像素。您必须编辑 DrawPixel 以在调用时在指定位置显示像素。
    • 我以为 X 和 Y 在 ; EBX = Y ; ECX = X 所以我写了显示像素的程序(我以前用过这个),结束我后来在 16 位上更改的所有程序
    • 您的评论在哪里?它消失了。我很抱歉我的英语,但我还在学习,它不是我的母语。我不确定我是否可以去聊天,因为我是新用户,没有太多积分。
    • 请在您的个人资料中添加一些联系信息(电子邮件、Skype)。
    猜你喜欢
    • 2016-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    相关资源
    最近更新 更多