Oxygen Basic

Programming => Example Code => General => Topic started by: Arnold on February 26, 2016, 12:23:06 AM

Title: Experimenting with O2 assembler
Post by: Arnold on February 26, 2016, 12:23:06 AM
Hi Charles, Peter,

can you help out a little bit?

I am interested to use some assembly code with OxygenBasic and I have started to study the book "PC Assembly Language" of Paul A. Carter which I found at:
http://www.drpaulcarter.com/pcasm/  (http://www.drpaulcarter.com/pcasm/)

The first program first.asm looks rather complicated although only two statements are of interest. I tried to do it in Oxygen:

Code: OxygenBasic
  1. ; file: first.o2bas
  2. ; First assembly program. This program asks for two integers as
  3. ; input and prints out their sum
  4.  
  5. #include "$/inc/console.inc"
  6.  
  7. 'Basic
  8.  
  9. int input1, input2
  10.  
  11. zstring prompt1 = "Enter a number: "
  12. zstring prompt2 = "Enter another number: "  
  13. zstring outmsg1 = "You entered : "
  14. zstring outmsg2 = " and "
  15. zstring outmsg3 = " The sum of these is "
  16.  
  17. print prompt1 : num = rtrim ltrim input() : input1 = val(num)
  18. print prompt2 : num = rtrim ltrim input() : input2 = val(num)
  19.  
  20. print outmsg1 input1 outmsg2 input2 outmsg3 input1+input2 & cr
  21.  
  22. ; Assembly
  23.  
  24. function add_two(int a, b) as int
  25.    mov eax,  a    ; eax =  a  same as mov eax, [a]?
  26.    add eax, [b]   ; eax += b
  27.  
  28.    'function = eax
  29.   return eax
  30. end function
  31.  
  32. printl "Using Assembly: " & cr
  33.  
  34. ; push / pop  for save / restore registers not necessary?
  35. print outmsg3 add_two(input1,input2) & cr
  36.  
  37. print "Enter ... ": waitkey
  38.  

   It seems that with OxygenBasic it is not necessary to push / pop the registers when calling a function with assembly code?
   Is there no difference between mov eax,a and move eax,[a]?
   The return value is stored in eax? 

Maybe my questions seem to be trivial but I am not sure if my assumptions are correct.

Roland     



.
Title: Re: Experimenting with O2 assembler
Post by: Peter on February 26, 2016, 07:20:57 AM
Hi Arnold,
Code: [Select]
include "asm.inc"
window 640,480,1


Function add_two(sys a, b) as sys
  mov eax, a      ' eax =  a  same as mov eax, [a]?  NO!
  add eax, b      ' eax += b  NO!
  return eax
End Function

sub xAdd( byref a as sys, byref b as sys )
    addr esi,a
    addr edi,b
    mov  eax,[esi]
    add  eax,[edi]
end sub
 
sub AddMul( sys *a, b, c)
    addr esi,a
    mov  eax,[esi]
    add  eax, b
    imul eax, c
end sub

Text 10,10,24,add_two(100,200)
Text 10,26,24,xAdd(200,300)
Text 10,52,24,AddMul(200,300,2)

WaitKey
WinEnd
Title: Re: Experimenting with O2 assembler
Post by: Charles Pegge on February 26, 2016, 07:26:50 AM
Hi Roland,



Oxygen will accept both operand forms as equivalent:
mov eax,a
mov eax,[a]

Integer values and pointers are normally returned in eax

Oxygen functions expect the ebx register to hold the base address for all static variables, and core functions. So it must be preserved by pushing and popping.

Similarly, the ebp register is used to reference all local variables, and the prior stack pointer esp.

Oxygen's exported functions obey the stdcall calling convention, by default. But it is totally different in 64 bit mode.

Title: Re: Experimenting with O2 assembler
Post by: Arnold on February 27, 2016, 12:54:13 AM
Hi Charles,

thank you for the info. Can you help me with this problem too:
How would the above example look without using a function?

I learned that there can be:

a .data section with initialized data
a .bss section with unitialized data
a .code section

but I must admit that it is not obvious to me how I can apply this in Oxygen.

I would like to use the input function to get input1 and input2, then

create variable a and b, store input1 and input2 to a and b
create variable outmsg3, store text to it (or create a constant with text)
do the calculation

use the print function to print outmsg3 and the result.

It would be very helpful to see how the low level steps are done with Oxygen.

Roland
Title: Re: Experimenting with O2 assembler
Post by: Arnold on February 27, 2016, 03:09:14 AM
Hi Peter,

thanks for the examples. Using your latest asm.inc / asm.dll I tested them with Windows Vista and Win7.

In the first example I can indeed use a,b or [a]  and get the same result.
The same is valid for the second example. But there would be a difference if I used esi, edi instead of [esi], [edi].
The third example is interesting. I tested different combinations:

AddMul( sys a, b, c) -> ok
AddMul( sys *a, b, c) -> ok
AddMul( sys a, *b, c) -> ok
AddMul( sys a, b, *c) -> ok
AddMul( sys *a, *b, c) -> different
AddMul( sys *a, b, *c) -> different
AddMul( sys a, *b, *c) -> different
AddMul( sys *a, *b, *c) -> different

I will need some time to reflect about the logic and what happens here. But the examples are very useful to test these situations. Maybe this is only my subjective opinion but I think it is much easier to do these tests with Oxygen than using an independant assembler.

Roland
Title: Re: Experimenting with O2 assembler
Post by: Peter on February 27, 2016, 05:46:09 AM
Quote
AddMul( sys *a, *b, *c) -> different

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

sub AddMul( sys *a, *b, c )
    addr  esi,a
    addr  edi,b
    mov   eax,[esi]
    mov   ecx,[edi]
    add   eax, ecx
    imul  eax, c
end sub

sub AddDiv( sys *a, *b, *c )
    xor   edx,edx
    addr  esi,a
    addr  edi,b
    mov   eax,[esi]
    mov   ecx,[edi]
    add   eax, ecx
    addr  ecx,c
    mov   ecx,[ecx]
    cmp   ecx,0
    jz    gOut
    idiv  ecx
    .gOut
end sub
 
Text 10,10,20, AddDiv(200,300,3)

WaitKey
WinEnd

.
Title: Re: Experimenting with O2 assembler
Post by: Arnold on February 28, 2016, 02:52:34 AM
Hi Peter,

I assume the key is "addr". If I add in the first example: addr c, I can create the function with or without using the '*' pointer. This is the code for only using the console window (I do not know if I did it correctly):

Code: [Select]
$ filename "addmuldiv.exe"

'#include "$/inc/RTL32.inc"
#include "$/inc/console.inc"

function AddMul( int a, b, c )
    addr  esi,a           ; esi=addr a
    addr  edi,b           ; edi=addr b
    mov   eax,[esi]       ; eax=[esi] =[a]
    mov   ecx,[edi]       ; ecx=[edi] =[b]
    add   eax, ecx        ; eax+=ecx
    addr  ecx,c           ; ecx=addr c
    imul  eax,[ecx]       ; eax i*= [ecx] ; =[c]
    return eax
end function

function AddDiv( int a, b, c )
    xor   edx,edx         ; edx=0 , division rest?
    addr  esi,a           ; esi=addr a
    addr  edi,b           ; edi=addr b
    mov   eax,[esi]       ; eax=[esi] =[a]
    mov   ecx,[edi]       ; ecx=[edi] =[b]
    add   eax, ecx        ; eax+=ecx
    addr  ecx,c           ; ecx=addr c
    mov   ecx,[ecx]       ; ecx=[ecx] =[c]
    cmp   ecx,0           ; ecx ? 0
    jz    gOut            ; if 0 .gOut
    idiv  ecx             ; eax i\= ecx ; =[c]
    return eax
    .gOut
    return 1/0  ; should be fixed
end function

print AddMul(200,300,3) : print cr
print AddDiv(200,300,3) : print cr

printl "Enter ..." : WaitKey

I cannot use:
print AddMul(200,300,3) & cr  (crashes)
Maybe something is missing in the functions?

Roland
Title: Re: Experimenting with O2 assembler
Post by: Peter on February 28, 2016, 05:40:32 AM
Quote
I can create the function with or without using the '*' pointer.


Hi Arnold,

sure, you have not any pointers in your source code.
but is not necessary to write it with indirect addressing.
Code: [Select]
include "asm.inc"
window 320,240,1

function AddMul( int a, b, c ) as int
    mov  eax,a           
    add  eax,b           
    imul eax,c
    return eax   
end function

function AddDiv( int a, b, c ) as int
    mov  ecx,c
    cmp  ecx,0
    jz   gOut
    xor  edx,edx         
    mov  eax,a                         
    add  eax,b           
    idiv ecx             
    return eax
    .gOut
end function

text 10,10,12,AddMul(10,10,10)
text 10,22,12,AddDiv(10,10, 4)

waitkey
winEnd


.
Title: Re: Experimenting with O2 assembler
Post by: Peter on February 28, 2016, 06:52:49 AM
Better is we use a SUB, we need then no RETURN.
Code: [Select]
sub AddMul( int a, b, c )
    mov  eax,a           
    add  eax,b           
    imul eax,c
end sub

sub AddDiv( int a, b, c )
    mov  ecx,c
    cmp  ecx,0
    jz   gOut
    xor  edx,edx         
    mov  eax,a                         
    add  eax,b           
    idiv ecx             
    .gOut
end sub
Title: Re: Experimenting with O2 assembler
Post by: Arnold on February 29, 2016, 01:47:30 AM
Hi Peter,

I think your latest code works better and is easier to underständ. Also print (result) & cr works. I could also combine the console window with asm.inc which might be useful sometimes with debugging.

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

window 320,240,1

function AddMul( int a, b, c ) as int
    mov  eax,a           
    add  eax,b           
    imul eax,c
    return eax   
end function

function AddDiv( int a, b, c ) as int
    mov  ecx,c
    cmp  ecx,0
    jz   gOut
    xor  edx,edx         
    mov  eax,a                         
    add  eax,b           
    idiv ecx             
    return eax
    .gOut
end function

text 10,10,12,AddMul(10,10,10)
text 10,22,12,AddDiv(10,10, 4)
text 10,40,12, "Enter ..."

print AddMul(10,10,10) & cr
print AddDiv(10,10, 4) & cr

waitkey
winEnd

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

sub AddMul( int a, b, c )
    mov  eax,a           
    add  eax,b           
    imul eax,c
end sub

sub AddDiv( int a, b, c )
    mov  ecx,c
    cmp  ecx,0
    jz   gOut
    xor  edx,edx         
    mov  eax,a                         
    add  eax,b           
    idiv ecx             
    .gOut
end sub

AddMul(10,10,10)
print eax & cr

AddDiv(10,10,4)
print eax & cr

printl "Enter ..." : waitkey

Of course I underständ these examples are not fully assembly language but they hopefully will help me some day to combine OxygenBasic with some assembly snippets.

Roland


.
Title: Re: Experimenting with O2 assembler
Post by: Peter on February 29, 2016, 04:52:01 AM
Hi Roland,

I am glad it works.
Title: Re: Experimenting with O2 assembler
Post by: Arnold on March 02, 2016, 08:29:17 AM
Hello,

this is the second example of the book "PC Assembly Language" by Paul A. Carter. It takes some time to study the theory (and will take some more time to understand it).
For using the code in Oxygen I tried to create a function and a sub. Using the function works quite nice.

Code: [Select]
;
; file: math1.o2bas
; This program demonstrates how the integer multiplication and division
; instructions work.
;

#include "$/inc/console.inc"

;segment .data
;
; Output strings
;
zstring
prompt      = "Enter a number: ",
square_msg  = "Square of input is ",
cube_msg    = "Cube of input is ",
cube25_msg  = "Cube of input times 25 is ",
quot_msg    = "Quotient of cube/100 is ",
rem_msg     = "Remainder of cube/100 is ",
neg_msg     = "The negation of the remainder is "


;segment .bss
;entry   resd 1
int entry

print prompt
entry = val (rtrim ltrim input())


function do_math(int num) as string
    int square, cube, cube25, quot, rem_, neg_

    pushad                    ' save registers

    mov     eax, num
       
    imul    eax               ; edx:eax = eax * eax       
    mov     square, eax

    mov     ebx, eax          ; save answer in ebx
    imul    ebx, [num]        ; ebx *= [num]
    mov     eax, ebx
    mov     cube, eax

    imul    ecx, ebx, 25      ; ecx = ebx*25
    mov     eax, ecx
    mov     cube25, eax

    mov     eax, ebx
    cdq                       ; initialize edx by sign extension
    mov     ecx, 100          ; can't divide by immediate value
    idiv    ecx               ; edx:eax / ecx
    mov     ecx, eax          ; save quotient into ecx
    mov     eax, ecx
    mov     quot, eax

    mov     eax, edx
    mov     rem_, eax

    neg     edx               ; negate the remainder
    mov     eax, edx
    mov     neg_, eax

    popad                     ' restore registers

' Output
    string s = square_msg + square + cr +
               cube_msg + cube + cr +
               cube25_msg + cube25 + cr +
               quot_msg + quot + cr +
               rem_msg + rem_ + cr +
               neg_msg + neg_ + cr
    return s
end function

print do_math(entry)

printl "Enter ..." : waitkey


For using the code in a sub I had to replace "ebx" with "esi". I assume the reason is because I used variables which are created outside the sub? I thought the use of pushad / popad would be sufficient but it seems I missed something else.

I wonder if there is a way to use "ebx" in the example which uses sub?

Roland

Code: [Select]
;
; file: math2.o2bas
; This program demonstrates how the integer multiplication and division
; instructions work.
;

#include "$/inc/console.inc"

;segment .data
;
; Output strings
;
zstring
prompt      = "Enter a number: ",
square_msg  = "Square of input is ",
cube_msg    = "Cube of input is ",
cube25_msg  = "Cube of input times 25 is ",
quot_msg    = "Quotient of cube/100 is ",
rem_msg     = "Remainder of cube/100 is ",
neg_msg     = "The negation of the remainder is "


;segment .bss
int entry
int square, int cube, cube25, quot, rem_, neg_

print prompt
entry = val (rtrim ltrim input())


sub do_math(int num)               

    pushad                    ' save registers

    mov     eax, num
       
    imul    eax               ; edx:eax = eax * eax           
    mov     square, eax

    mov     esi, eax          ; save answer in ebx
    imul    esi, [num]        ; ebx *= [num]
    mov     eax, esi
    mov     cube, eax

    imul    ecx, esi, 25      ; ecx = ebx*25
    mov     eax, ecx
    mov     cube25, eax

    mov     eax, esi
    cdq                       ; initialize edx by sign extension
    mov     ecx, 100          ; can't divide by immediate value
    idiv    ecx               ; edx:eax / ecx
    mov     ecx, eax          ; save quotient into ecx
    mov     eax, ecx
    mov     quot, eax

    mov     eax, edx
    mov     rem_, eax

    neg     edx               ; negate the remainder
    mov     eax, edx
    mov     neg_, eax

    popad                     ' restore registers

end sub

do_math(entry)

' Output
string s = square_msg & square & cr &
           cube_msg & cube & cr &
           cube25_msg & cube25 & cr &
           quot_msg & quot & cr &
           rem_msg & rem_ & cr &
           neg_msg & neg_ & cr
print s

printl "Enter ..." : waitkey



.
Title: Re: Experimenting with O2 assembler
Post by: Peter on March 02, 2016, 11:33:45 AM
Hi Arnold,

same here!

Here is a shorter version of your source code.
Code: [Select]
include "asm.inc"
window 320,240,1

int data[6]

sub do_math(int num)
    pushad                    ' save registers
    mov     eax, num
    xor     edx, edx
       
    imul    eax               ; edx:eax = eax * eax       
    mov     data[0], eax
    mov     edi, eax          ; save answer inedi
    imul    edi, num          ; edi *= [num]
    mov     eax,edi
    mov     data[1], eax

    imul    ecx,edi, 25      ; ecx =edi*25
    mov     data[2], ecx

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

    mov     eax, edx
    mov     data[4], eax

    neg     edx               ; negate the remainder
    mov     data[5], edx
    popad
end sub


do_math(15)

text 10, 20, 20, data[0]
text 10, 40, 20, data[1]
text 10, 60, 20, data[2]
text 10, 80, 20, data[3]
text 10,100, 20, data[4]
text 10,120, 20, data[5]

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

waitkey
winEnd

.
Title: Re: Experimenting with O2 assembler
Post by: Arnold on March 03, 2016, 02:05:16 AM
Hi Peter,

thank you for the alternative code. It will work in a console window too.
I am not quite sure why using ebx does work in the function but fails with the sub. I assume the reason is because global variables are declared and used in the sub. In examples\Asm32\Opengl2.o2bas and WinApi1.o2bas Charles used a way for saving and restoring procs and globals table ptr. But at the moment I do not understand this approach and am not able to apply it. I understand that EBX is used by Oxygen itself internally for several tasks.

I found this in a previous message:
Quote
You have defined variables in global/static memory space, which are always referenced by the ebx register

The cure is to put this into a procedure and use local variables, which are referenced by ebp.

How could this approach be applied with the example above?

Roland
Title: Re: Experimenting with O2 assembler
Post by: Peter on March 03, 2016, 05:44:50 AM
Hi Roland,

This may be interesting: 
Without STR() I got a nervous crash.
Code: [Select]
include "asm.inc"
window 640,480,1

sub test1
    push ebx
    mov  ebx,200
    mov  eax,ebx
    pop  ebx
    return eax
end sub

sub test2(sys a, b)
    push ebx
    mov  ebx,a
    add  ebx,b
    mov  eax,ebx
    pop  ebx
    return eax
end sub

text 20,10,12,str(test1) + " bananas"
waitkey

text 20,26,12, str(test2(50,50)) + " apples"
waitkey
winEnd
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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

.
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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
Title: Re: Experimenting with O2 assembler
Post by: Peter on March 04, 2016, 04:13:01 AM
Hi Roland,

Is a bit strange, the functions end with (end sub) !
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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


.
Title: Re: Experimenting with O2 assembler
Post by: Charles Pegge 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.
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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


.
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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.  


.
Title: Re: Experimenting with O2 assembler
Post by: Charles Pegge 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.  
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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.  



.
Title: Re: Experimenting with O2 assembler
Post by: Arnold 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
 (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


.
Title: Re: Experimenting with O2 assembler
Post by: edcronos on December 21, 2017, 12:21:42 AM
Thanks for the examples, I was lost on how to start.
Title: Re: Experimenting with O2 assembler
Post by: edcronos 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
Title: Re: Experimenting with O2 assembler
Post by: Charles Pegge 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.
Title: Re: Experimenting with O2 assembler
Post by: edcronos 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
Title: Re: Experimenting with O2 assembler
Post by: edcronos on December 22, 2017, 03:14:15 PM
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.

Hello
would have a forecast for the new version?
Title: Re: Experimenting with O2 assembler
Post by: Charles Pegge on December 23, 2017, 02:08:31 AM

A few more days for test and debug, I hope.