Author Topic: Experimenting with O2 assembler  (Read 8374 times)

0 Members and 1 Guest are viewing this topic.

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #15 on: March 04, 2016, 12:38:11 AM »
This is my solution for using global variables and EBX in a sub:

Code: [Select]
include "asm.inc"
window 400,300,1

'global
int data[6]

string outp <= {
 "Square of input is ",
 "Cube of input is ",
 "Cube of input times 25 is ",
 "Quotient of cube/100 is ",
 "Remainder of cube/100 is ",
 "The negation of the remainder is "
}


sub do_math(int num)
    'local
    int datas[6]

    pushad                    ' save registers
    mov     eax, num
    xor     edx, edx
       
    imul    eax               ; edx:eax = eax * eax       
    mov     datas[0], eax
    mov     ebx, eax          ; save answer in ebx
    imul    ebx, num          ; ebx *= [num]
    mov     eax,ebx
    mov     datas[1], eax

    imul    ecx,ebx, 25       ; ecx =ebx*25
    mov     datas[2], ecx

    mov     eax,ebx
    mov     ecx, 100          ; can't divide by immediate value
    div     ecx               ; edx:eax / ecx
    mov     ecx, eax          ; save quotient into ecx
    mov     datas[3], ecx

    mov     eax, edx
    mov     datas[4], eax

    neg     edx               ; negate the remainder
    mov     datas[5], edx

    popad

    'store global
    for x=0 to 5
        data[x]=datas[x]
    next   

end sub


do_math(15)


for x=0 to 5
   text  10, (20+x*20) ,10, outp[x]
   text 280, (20+x*20) ,10, data[x]
next

backcolor 0,0,255
text 10,160, 20, "OKAY!"

waitkey
winEnd

There might be a better approach, but for me it is important to know that it is possible to use ebx with global variables in a sub also.

Roland

.

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #16 on: March 04, 2016, 01:29:25 AM »
Hi Peter,

regarding your example with bananas and apples: I used function instead of sub (because of the return statements) and then everything worked ok.

Roland

Code: [Select]
include "asm.inc"
window 640,480,1

function test1() as int
    push ebx
    mov  ebx,200
    mov  eax,ebx
    pop  ebx
    return eax
end function

function test2(sys a, b) as int
    push ebx
    mov  ebx,a
    add  ebx,b
    mov  eax,ebx
    pop  ebx
    return eax
end function

text 20,10,12, test1() + " bananas"
text 20,26,12, test2(50,50) & " apples"

waitkey
winEnd
« Last Edit: March 04, 2016, 04:46:31 AM by Arnold »

Peter

  • Guest
Re: Experimenting with O2 assembler
« Reply #17 on: March 04, 2016, 04:13:01 AM »
Hi Roland,

Is a bit strange, the functions end with (end sub) !

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #18 on: March 04, 2016, 04:49:03 AM »
Hi Peter,

yes, this was my mistake. But Oxygenbasic is very forgiving sometimes. Nevertheless I have changed this.

Roland

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #19 on: March 05, 2016, 03:28:35 AM »
Hello,

this is the prime example of Chapter 2.4 in the book "PC Assembly Language" which among other things deals with control structures in assembler.
Here is the code of the algorithm ported from C to OxygenBasic (can be compared with \examples\Math\Eratosthenes.o2bas):

Code: OxygenBasic
  1. ;
  2. ; file: prime.o2bas
  3. ; This program calculates prime numbers
  4. ;
  5. ; Code ported from C to OxygenBasic:
  6.  
  7. #include "$/inc/console.inc"
  8. ;
  9. sub main()
  10.  
  11.   int guess          /* current guess for prime      */
  12.   int factor         /* possible factor of guess     */
  13.   int limit          /* find primes up to this value */
  14.  
  15.   print("Find primes up to: ")
  16.   limit = val (input) : if limit < 1 then return
  17.  
  18.   print "2" & chr(9)    /* treat first two primes as special case */
  19.   print "3" & chr(9)
  20.  
  21.   guess = 5        /* initial guess */
  22.   while  guess <= limit
  23.     /* look for a factor of guess */
  24.     factor = 3
  25.     while (factor*factor) < guess and mod(guess, factor) != 0
  26.       factor += 2;
  27.     end while
  28.     if mod(guess, factor) != 0 then
  29.       print guess & chr(9)
  30.     end if      
  31.     guess += 2    /* only look at odd numbers */
  32.   end while
  33.  
  34. end sub
  35.  
  36. main
  37.  
  38. printl "Enter ..." : waitkey
  39.  

The same algorithm done with Oxygen Assembly:

Code: [Select]

;
; file: prime1.o2bas
; This program calculates prime numbers
;
; using assembly in OxygenBasic

#include "$/inc/console.inc"

;segment .data
zstring Message = "Find primes up to: "


;segment .bss
int Limit                         ; find primes up to this limit
int Guess                         ; the current guess for prime

 
;segment .text
;        global  _asm_main

_asm_main:
        print Message
        Limit = val (Input) : if Limit < 1 then goto end_while_limit
        print "2" & chr(9) & "3" & chr(9)  ; treat first two primes as special case

        mov     Guess, 5             ; Guess = 5;

while_limit:                         ; while ( Guess <= Limit )
        mov     eax, Guess
        cmp     eax, Limit
        jnbe    end_while_limit      ; use jnbe since numbers are unsigned

        mov     ecx, 3               ; ecx is factor = 3;
while_factor:
        mov     eax, ecx
        mul     eax                  ; edx:eax = eax*eax
        jo      end_while_factor     ; if answer won't fit in eax alone
        cmp     eax, Guess
        jnb     end_while_factor     ; if !(factor*factor < guess)
        mov     eax, Guess
        mov     edx, 0
        div     ecx                  ; edx = edx:eax % ecx
        cmp     edx, 0
        je      end_while_factor     ; if !(guess % factor != 0)

        add     ecx, 2               ; factor += 2;
        jmp     while_factor
end_while_factor:
        je      end_if               ; if !(guess % factor != 0)
        mov     eax, Guess           ; printf("%u\n")

        pushad                ' save registers
        print eax & chr(9)    ' do some basic
        popad                 ' restore registers

end_if:
        mov     eax, Guess
        add     eax, 2
        mov     Guess, eax           ; guess += 2
        jmp     while_limit
       
end_while_limit:


printl "Enter ..." : waitkey

Again I could not use EBX but I could use ECX, ESI or EDI register instead. I suppose as a rule of thumb one should be very cautious with the use of the EBX register in Oxygen, in particular if global variables are used.

Roland


.

Charles Pegge

  • Guest
Re: Experimenting with O2 assembler
« Reply #20 on: March 05, 2016, 05:05:31 AM »
Hi Roland,

If you put the assembly code inside a function, you will be able to use local variables which are referenced by the EBP register instead of EBX.

Incidently, Oxygen's original value for the EBX register can be recovered at any time by:

call _mem

this is internally invoked in all exported functions.

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #21 on: March 06, 2016, 02:43:54 AM »
Hi Charles,

yes, using assembly inside a function will be my preferred way. These will be small snippets anyway if I use assembly at all, Oxygenbasic is fast enough for any purpose.

call _mem seems to be very interesting. Can this procedure be used somehow with regtrace.o2bas in examples\diagnostics? I tried to use this as an include file with the following example:

Code: OxygenBasic
  1. #include "regtrace.inc"
  2. #include "$/inc/console.inc"
  3.  
  4. tab=chr(9)
  5.  
  6. function FindMin(int a, b) as int
  7.  
  8.    push ebx         ; ebx will be used
  9.  
  10.    mov eax, a  x     ; eax = a
  11.    mov ebx, b       ; ebx = b
  12.  
  13.    cmp eax, ebx  
  14.    jle less         ; if eax < b
  15.    mov eax, b       ; else eax = b
  16.    
  17. less:      
  18.    pop ebx          ; restore ebx
  19.  
  20.    return eax
  21. end function
  22.  
  23. dim int a,b
  24.  
  25. printl "Enter first number: " tab : a=val(input)
  26. printl "Enter second number:" tab : b=val(input)
  27.  
  28. printl "First number =   " tab a  & cr "Second number = " tab b & cr
  29. printl "Minimum = " FindMin(a, b) & cr
  30.  
  31. printl "Enter ... " : waitkey
  32.  

Regtrace.inc (the same code as in examples\diagnostics):
Code: [Select]
'
'-----------------------------------
'TRACING EXECUTION OF ASSEMBLY CODE
'INSPECT REGISTERS AFTER INSTRUCTION
'===================================
 
  include once "$\inc\console.inc"
   
  '----------------
  'DIAGNOSTIC MACROS
  '================
   
  def SHOW
    pushad : mov a,%1 : printl "%1: " hex a : popad
  end def
  '
  def MSG
    pushad : printl %1 : popad
  end def
  '
  def FLAGS
    pushad : pushf : pushf : pop eax : mov a,eax : printl "Flag Register: " hex a : popf : popad
  end def
  '
  def x
    : pushad : pushf : call showregs : popf : popad :
  end def


  dim a
 
  sub showregs()
    dim as long v(9) at [ebp+8]
    dim as string tab=chr 9
    printl "
    Registers:
   
    EAX:   " tab hex (v(9),8) "
    ECX:   " tab hex (v(8),8) "
    EDX:   " tab hex (v(7),8) "
    EBX:   " tab hex (v(6),8) "
    ESP:   " tab hex (v(5),8) "
    EBP:   " tab hex (v(4),8) "
    ESI:   " tab hex (v(3),8) "
    EDI:   " tab hex (v(2),8) "
    EFLAGS:" tab hex (v(1),4) "
    "
  end sub
 
'waitkey

Unfortunately regtrace fails, if I try in function FindMin e.g. mov ebx, b  x
I tried several ways to use call _mem, but it seems I am not clever enough.

Roland


.

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #22 on: March 08, 2016, 02:13:25 AM »
Hi Charles,

does the following approach make sense? I tried to apply your infos and what I found in examples\diagnostics\RegTrace.o2bas and RegSnapShot.o2bas. The goal is to follow the value of EBX and it seems to work, but I am not quite sure if there is a better solution?

Roland

Code: OxygenBasic
  1. #include "$/inc/console.inc"
  2.  
  3. tab=chr(9)
  4.  
  5.  
  6. function FindMin(int a, b) as int
  7. 'values stored in ebp
  8. dim v(2) at [ebp+8]
  9. printl "a = "v(1) ", b = " v(2) & cr & cr
  10.  
  11.    push ebx         ; ebx will be used
  12.  
  13.    mov eax, a       ; eax = a  
  14.    mov ebx, b       ; ebx = b
  15.  
  16. pushad
  17. call _mem           ;restore the vital ebx register for all system calls
  18. print "EBX: "
  19. mov eax, [esp+16]
  20. print eax & cr
  21. popad
  22.  
  23.    cmp eax, ebx  
  24.  
  25.    jle less         ; if eax < b
  26.    mov eax, ebx     ; else eax = b
  27.    
  28. pushad
  29. call _mem           ;restore the vital ebx register for all system calls
  30. print "EBX: "
  31. mov eax, [esp+16]
  32. print eax & cr
  33. popad
  34.    
  35. less:      
  36.    pop ebx          ; restore ebx
  37.  
  38.    return eax
  39. End function
  40.  
  41. dim int num1,num2
  42.  
  43. printl "Enter first number: " tab : num1=val(input)
  44. printl "Enter second number:" tab : num2=val(input)
  45.  
  46. printl "First number =   " tab num1  & cr "Second number = " tab num2 & cr
  47. printl "Minimum = " FindMin(num1, num2) & cr
  48.  
  49. printl "Enter ... " : waitkey
  50.  


.
« Last Edit: March 08, 2016, 02:25:17 AM by Arnold »

Charles Pegge

  • Guest
Re: Experimenting with O2 assembler
« Reply #23 on: March 09, 2016, 10:44:33 AM »
Hi Roland,

A slightly more generic way of displaying registers:

Code: OxygenBasic
  1. function showint(...) ' ... automatically uses cdecl
  2. indexbase 1
  3. sys i
  4. for i=1 to 8
  5.   printl i "  " param(i)
  6. next
  7. printl ""
  8. end function
  9.  
  10. showint edi,esi,ebp,esp,ebx,edx,ecx,eax 'stacked in reverse order
  11.  
  12.  

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #24 on: March 10, 2016, 01:18:19 AM »
Hi Charles,

thank you for the code of the showint function. After some tests I think showint combined with call _mem could be quite helpful to inspect the registers at selected spots inside assembly.
Attached is the modified program of findmin.o2bas to show the tracing I will use for further exploration of the o2 assembly language.

Roland

Code: OxygenBasic
  1. #include "$/inc/console.inc"
  2.  
  3. tab=chr(9)
  4.  
  5. function bin(sys ii, optional nd) as string
  6. =================================================
  7. if ii=0 and nd=0 then return "0"
  8. sys b,j,p
  9. string bi
  10. b=1
  11. n=nd
  12. if n=0 then n=8*sizeof sys
  13. p=n
  14. bi=space n
  15. for j=n to 1 step -1
  16.   if ii and b
  17.     mid bi,j,"1"
  18.     p=j
  19.   else
  20.     mid bi,j,"0"
  21.   end if
  22.   shl b,1
  23. next
  24. if nd=0
  25.   return mid bi,p 'trim leading zeros
  26. else
  27.   return bi
  28. end if
  29. end function
  30.  
  31. def flags
  32.  sys reg
  33.  pushad : pushf : pushf : call _mem
  34.  pop eax : mov reg,eax
  35.  printl "EFLAGS:             ODI SZ A P C"
  36.  printl "          " hex (reg,4) tab bin(reg,16) : printl ""
  37.  popf : popad
  38. end def
  39.  
  40. function showRegs(...) ' ... automatically uses cdecl
  41. indexbase 1
  42. sys i
  43. string reg[8] <= {"EDI","ESI","EBP","ESP","EBX","EDX","ECX","EAX"}
  44. for i=1 to 8
  45.   printl reg[i] " = " hex (param(i),8) tab param(i)
  46. next
  47.   flags
  48.   printl ""
  49. end function
  50.  
  51. def x : pushad : call _mem : showRegs : popad :
  52.  
  53. '----------------------------------------------
  54.  
  55.  
  56. function FindMin(int a, b) as int
  57.  
  58.    push ebx        ; ebx will be useld
  59.  
  60.    mov eax, a      ; eax = a      
  61.    mov ebx, b      ; ebx = b
  62. x
  63.  
  64.    cmp eax, ebx    
  65. flags
  66.    jle less         ; if eax < b
  67.    mov eax, ebx     ; else eax = b
  68. x
  69. less:    
  70. x:   pop ebx          ; restore ebx
  71.  
  72.    return eax
  73. End function
  74.  
  75. dim int num1,num2
  76.  
  77. printl "Enter first number: " tab : num1=val(input)
  78. print "Enter second number:" tab : num2=val(input)
  79.  
  80. printl "Minimum = " FindMin(num1, num2) & cr
  81.  
  82. printl "Enter ... " : waitkey
  83.  



.

Arnold

  • Guest
Re: Experimenting with O2 assembler
« Reply #25 on: March 10, 2016, 01:33:08 AM »
This is an example for the "greatest common divisor" function ported from C to OxygenBasic and two functions in assembly code. I found the original code here:


http://www.azillionmonkeys.com/qed/asmexample.html


I think that it should not be too difficult to apply already existing assembly code in Oxygenbasic functions. The only major difference is that the ebx register must be saved and restored if it is used in a function.

But now it is time to go further into the theoretical part of "PC Assemly Language".

Roland

Code: [Select]
#include "$/inc/console.inc"

int num1=1008
int num2=128

function gcd (int a, b) as int

    if a=0 and b=0 then
      b=1
    elseif b=0 then
      b=a
    elseif a != 0  then
        while a != b
           if a<b then
             b-=a
           else
             a-=b
           end if
        wend
    end if
   
    return b
end function

printl "GCD of " num1 ", " num2 " = " gcd(num1,num2) & cr

; WATCOM C/C++ v10.0a output
function gcd1(int a, b) as int
        push ebx            ; o2 will need this
                   
        mov     eax, a      ; eax = a
        mov     edx, b      ; edx = b

 gcd:   
        mov     ebx,eax
        mov     eax,edx
        test    ebx,ebx
        jne     L1
        test    edx,edx
        jne     L1
        mov     eax,1
        ret
 L1:
        test    eax,eax
        jne     L2
        mov     eax,ebx
        ret
 L2:
        test    ebx,ebx
        je      L5
 L3:
        cmp     ebx,eax
        je      L5
        jae     L4
        sub     eax,ebx
        jmp     L3
 L4:
        sub     ebx,eax
        jmp     L3
 L5:
        pop ebx             ; o2 will need this
       
     return eax
end function

printl "GCD of " num1 ", " num2 " = " gcd1(num1,num2) & cr

function gcd2(int a, b) as int
        mov     eax, a      ; eax = a
        mov     edx, b      ; edx = b
       
gcd:
        neg     eax
        je      L3
L1:
        neg     eax
        xchg    eax,edx
L2:
        sub     eax,edx
        jg      L2
        jne     L1
L3:
        add     eax,edx
        jne     L4
        inc     eax

L4:
    return eax
end function

printl "GCD of " num1 ", " num2 " = " gcd2(num1,num2) & cr

printl "Enter ... " : waitkey


.
« Last Edit: March 10, 2016, 02:02:22 AM by Arnold »

edcronos

  • Guest
Re: Experimenting with O2 assembler
« Reply #26 on: December 21, 2017, 12:21:42 AM »
Thanks for the examples, I was lost on how to start.

edcronos

  • Guest
Re: Experimenting with O2 assembler
« Reply #27 on: December 22, 2017, 06:30:51 AM »
Hello
I was doing some tests here to see what it is possible to do
I'm starting now so I do not know what works or not
in case the popcnt statement does not exist in Oxygen asm?
  of sse4.2

Charles Pegge

  • Guest
Re: Experimenting with O2 assembler
« Reply #28 on: December 22, 2017, 09:18:43 AM »
I think popcnt only becomes standard for all Pentiums in 2011, but I will include it together with lzcnt (leading zero count) in my imminent release.

SSE4.2 includes quite a few new SIMD instructions, unfortunately invalidating older processors.

edcronos

  • Guest
Re: Experimenting with O2 assembler
« Reply #29 on: December 22, 2017, 09:56:15 AM »
thank you

I think it's best to stay current to take advantage of new technologies than getting stuck in the past,
  and fortunately with the popularization and competition between the companies if there is a greater access to these technologies
"less here in Brazil we have corrupt politicians and people accommodated, we are not worse because God blessed and in most of Brazil does not even have to plant, nature itself presents us with its miracles"

I honestly think it's odd that we're stuck with old processes because of compatibility issues instead of having a product created from scratch to take advantage of all the accumulated knowledge
with this it could have instructions cleaner and easier to work, therefore software faster and better, without having to load archaic codes so that it can run without problems in the most varied machines
although a new project is not meant to be a better project