装配计算正弦示例。NASM 16 DOS

Assembly calculate sine example. nasm 16 dos

提问人:kkkkk 提问时间:7/6/2014 最后编辑:rkhbkkkkk 更新时间:4/29/2017 访问量:2769

问:

例如,我正在寻找如何使用协处理器计算正弦。 我找到了功能:

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

我想画正弦,我需要 Y in 和 X in 。 X 不会有问题,因为我会为 Y 做循环,但 Y 我有问题。X 将从示例 0 到 350(如像素)。 如何计算它,如果例如sin(30度)为1/2,则像素为Y。 如何四舍五入的结果才能有良好的坐标?axbx


编辑:对不起,但是当我运行您的代码时,它显示的不是正弦,而是 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
集会 纳斯 角度 dos

评论

0赞 user35443 7/6/2014
“我想画正弦”——你是什么意思?正弦波?

答:

1赞 user35443 7/6/2014 #1

如果我理解正确的话,您想在没有任何平移和缩放的情况下绘制正弦波。因此,你可以把 作为你的坐标,你从函数中得到的值就是你的坐标。angleXf(x) = sin xY

; 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

现在假设你想画一个波浪。这意味着在绘制最后一个像素时,(全波)必须完全为真。屏幕宽度为 640 像素,绘图循环中的单个步骤是 。其余的很简单。x == 2*PI radx2*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
0赞 rkhb 7/7/2014 #2

我的两分钱;-)。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
0赞 Dirk Wolfgang Glomp 7/7/2014 #3

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

      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 度值加倍。