Home > OS >  Graph drawing in YASM 8086
Graph drawing in YASM 8086

Time:12-08

I don't know if anyone will be able to help me, but I will try to explain my problem as clearly, as possible. I am learning 8086 FPU with YASM. I want to draw y = cos(x^2 x 1) This is how this graph looks like. I am doing it in DosBox, and I am emulating 8086 processor.

My problem: How to normalize graph's(red) ratio, so it could be readable in DosBox.

So far, I managed to draw a coordinate plane. And I think I managed to draw this graph, but the ratio is too small to check if it is really good. This is how it looks so far GRAPH IMAGE](https://i.stack.imgur.com/0Hy6X.jpg).

I am using FPU, to calculate Y coordinates. I am using stack to calculate Y coordinate. X coordinate will go from 320 to 0 (with dec si), as you can see in my code.

In this code, I am trying to calculate Y (di) every with different X (si). And put the pixel in the spot, that it has to be in.

;------------------------------------------------------------------------
%include 'yasmmac.inc'  
org 100h               

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .text                   ; Code starts here

   startas:         
      call procSetGraphicsMode

;;;;;;;;;;;;;;;;;;;;;;;;; COORDINATE PLANE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      mov di, 200
      mov si, 160
      .vertical:
         mov cl, 15 
         call procPutPixel 
         dec di
         jnz .vertical

      mov si, 320
      .horizontal:        
         mov di, 100
         mov cl, 15        ; 15 = white color
         call procPutPixel
         dec si
         jnz .horizontal

      ; y = di
      ; x = si
      mov si, 320
      mov di, 100

;;;;;;;;;;;;;;;;;;;;;;;; GRAPH ;;;;;;;;;;;;;;;;;;;;;;;;
      .loop:
         mov [value1], si
         mov [value2], si
         finit
         fild dword [value1]
         fild dword [value1]
         fild dword [value3]   ; move to stack 1
         fmul dword [value1]
         fadd st0, st1        ; add x in stack head
         fadd st0, st3        ; add 1 in stack head
         fcos                 ; cos(x^2   x   1)     
         frndint              ; round
         fistp word [y]       ; Load rounded answer to [y] variable
         add di, [y]          ; Add [y] to di
         mov cl, 4            ; 4 = Red color
         call procPutPixel
         dec si
         jnz .loop

;;;;;;;;;;;;;;;;;;;;;;;; WAIT FOR ESC ;;;;;;;;;;;;;;;;;;;;;;;;
      call procWaitForEsc 
      exit
      

%include 'yasmlib.asm'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .data                   ; data

     value1: 
     dd  0.0
     
     value2:
     dd  0.0

     value3:
     dd 1.0

     xc: 
     dw 160
     yc:
     dw 100
          
     x: 
     dw  0
     y:
     dw 0
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .bss    

yasmlib.asm If it helps

CodePudding user response:

The axes are the easy part

Since you are working on a graphics screen that has a resolution of 320 x 200, the X coordinates range from 0 to 319 and the Y coordinates range from 0 to 199. Your code erroneously outputs in X's of 320 and y's of 200 and you don't use the X's of 0 or the Y's of 0.

     xor  di, di     ; Y
.vertical:
     mov  si, 160    ; X
     mov  cl, 15     ; Color
     call procPutPixel
     inc  di
     cmp  di, 199    ; MaxY
     jbe  .vertical

     xor  si, si     ; X
.horizontal:        
     mov  di, 100    ; Y
     mov  cl, 15     ; Color
     call procPutPixel
     inc  si
     cmp  si, 319    ; MaxX
     jbe  .horizontal

  ; y = di
  ; x = si
  mov si, 320
  mov di, 100

The data definitions

value1: 
dd  0.0
value2:
dd  0.0
value3:
dd 1.0

Your FPU instructions are dealing with values stored in memory, but are doing so in incompatible ways! eg. value1 can't be both an integer dword and an single precision float at the same time. You primed this variable with the value from the 16-bit integer register SI, so deal with it as a 16-bit integer.

valueX:  dw 0

The calculation

finit (better use the non-waiting fninit) is best held outside of the loop.
frndint is redundant since fistp word [y] will automatically do the rounding for you.
fmul st0 can square the value from st0.
fld1 can load the value 1 to the FPU stack. No need for a memory based variable and also no need to reload it for every X.

    xor   si, si          ; X
    fninit
    fld1                  ; CONST 1
.loop:
    mov   [valueX], si    ; X
    fild  word [valueX]   ; st0 = X
    fmul  st0             ; st0 = X^2
    fiadd word [valueX]   ; st0 = X^2   X
    fadd  st0, st1        ; st0 = X^2   X   1
    fcos                  ; st0 = cos(X^2   X   1)         [-1, 1]
    ...
    fistp word [y]        ; {-1,0, 1}
    mov   di, 100         ; Y
    add   di, [y]         ; -> Y={99,100,101}
    mov   cl, 4           ; Color
    call  procPutPixel
    inc   si
    cmp   si, 319
    jbe   .loop
    fstp                   ; Clean FPU stack, removing the CONST 1

Getting the output readable

The cosine value that the FPU delivers ranges from -1 to 1, but storing this value in the integer memory variable y will produce but 3 discrete values -1, 0, and 1.
What is needed is scaling the cosine by some factor like say 60.
Add this to the data:

scaleY:  dw 60

and at the ellipses you add fimul word [scaleY], producing:

    fcos                  ; st0 = cos(X^2   X   1)         [-1, 1]
    fimul word [scaleY]   ; st0 = cos(X^2   X   1) * 60
    fistp word [y]        ; [-60, 60]
    mov   di, 100         ; Y
    add   di, [y]         ; -> Y=[40,160]
  • Related