提问人:kkkkk 提问时间:7/6/2014 最后编辑:rkhbkkkkk 更新时间:4/29/2017 访问量:2769
装配计算正弦示例。NASM 16 DOS
Assembly calculate sine example. nasm 16 dos
问:
例如,我正在寻找如何使用协处理器计算正弦。 我找到了功能:
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。
如何四舍五入的结果才能有良好的坐标?ax
bx
编辑:对不起,但是当我运行您的代码时,它显示的不是正弦,而是 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
答:
1赞
user35443
7/6/2014
#1
如果我理解正确的话,您想在没有任何平移和缩放的情况下绘制正弦波。因此,你可以把 作为你的坐标,你从函数中得到的值就是你的坐标。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
现在假设你想画一个波浪。这意味着在绘制最后一个像素时,(全波)必须完全为真。屏幕宽度为 640 像素,绘图循环中的单个步骤是 。其余的很简单。x == 2*PI rad
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
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 度值加倍。
评论