The Art-Music Forum
July 22, 2018, 06:09:28 am
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Here you may discover hundreds of little-known composers, hear thousands of long-forgotten compositions, contribute your own rare (non-copyright) recordings, and discuss all the Arts in an erudite and decorous atmosphere full of freedom and delight. To participate, simply log in or register.
 
  Home Help Search Gallery Staff List Login Register  

64-bit Assembly Language - a simple example


Pages: [1]   Go Down
  Print  
Author Topic: 64-bit Assembly Language - a simple example  (Read 23014 times)
Sydney Grew
Level 7
*******

Times thanked: 28
Offline Offline

Posts: 1447


View Profile
« on: December 18, 2011, 03:01:38 pm »


; Simple examples of the use of 64-bit assembly language to perform
; a few fundamental functions in a "c" and "windows" environment.

; ====================================================================

extern printf
extern _getch
extern gets
extern _findfirst64
extern _findnext64
extern _findclose
extern fopen
extern fread
extern feof
extern fclose
extern GetCommandLineA

; ====================================================================

buflen  equ   2000000                 ; File input buffer size

; ====================================================================

; Structure definition used by findfirst64 and findnext64:

struc finddata64_t
  attrib:       resq  1
  time_create:  resq  1
  time_access:  resq  1
  time_write:   resq  1
  size:         resq  1
  name:         resb  260
endstruc

; ====================================================================

; This directive is essential; 64-bit code normally addresses things
; in a new mode, relative to the instruction pointer!

default rel

; ====================================================================

section .code

[BITS 64]
global _start

; The conventional prologue
_start: mov   [rsp + 8],rcx
        push  r15
        push  r14
        push  r13
        sub   rsp,128           ; Stack space

; Get the command line (an example of one of the many hundreds of
; Windows APIs which may be called in this way)
        xor   rcx,rcx
        xor   rdx,rdx
        xor   r8,r8
        xor   r9,r9
        call  GetCommandLineA   ; Returns rax pointing to the command line

; Display something using printf
        mov   rcx,mess1
        mov   rdx,rax           ; Pointer to the command line (%s)
        xor   r8,r8
        xor   r9,r9
        call  printf

; Get some text from the user (note - no check for buffer overflow here - to avoid
; that one could use GetStdHandle and fgets, which accepts a maximum count)
        mov   rcx,buffer
        xor   rdx,rdx
        xor   r8,r8
        xor   r9,r9
        call  gets
;
        mov   rcx,youent
        mov   rdx,buffer
        xor   r8,r8
        xor   r9,r9
        call  printf

; List a selection of files in the current directory
        mov   rcx,wci         ; The mask *.txt will select all text files
        mov   rdx,finddata64  ; Address of the result structure
        xor   r8,r8
        xor   r9,r9
        call  _findfirst64
;
        cmp   rax,-1
        je    goout           ; --> No file of that kind found
        mov   [hfile],rax     ; Save the file "handle"

; Display the file's name and size
filope: mov   rcx,fileinfo
        mov   rdx,finddata64+name   ; Address of the file name
        mov   r8,[finddata64+size]  ; File size - note that it is a quadword here (%lld)
        xor   r9,r9
        call  printf

; Open the file for reading in binary mode
        mov   rcx,finddata64+name   ; Address of the file name
        mov   rdx,ipmods      ; Mode string - important - must be zero-terminated!
        xor   r8,r8
        xor   r9,r9
        call  fopen

; It will return either a FILE * or null
        or    rax,rax
        jz    error                 ; --> Open failed
        mov   qword [filpoi],rax    ; Save the FILE pointer

; Test whether end of file has been reached
        mov   rcx,rax         ; The FILE pointer
        xor   rdx,rdx
        xor   r8,r8
        xor   r9,r9
        call  feof
;
        or    rax,rax
        jnz   goteof          ; --> Yes it has

; Read the first twenty bytes of the file
        mov   rcx,buffer      ; Destination
        mov   rdx,1           ; unit or item
        mov   r8,20           ; Read 20 bytes
        mov   r9,qword [filpoi]     ; FILE *
        call  fread
;
        or    rax,rax
        jz    goteof          ; --> No readable bytes

; Display those 20 bytes in hexadecimal. Note that printf will
; preserve registers r13 and r14
        mov   r13,buffer      ; First byte to be displayed
        mov   r14,buffer+20
disbak: mov   rcx,bytesmes
        xor   rdx,rdx
        mov   dl,[r13]
        xor   r8,r8
        xor   r9,r9
        call  printf
        inc   r13
        cmp   r13,r14
        jne   disbak
;
        mov   rcx,bmafter
        xor   rdx,rdx
        xor   r8,r8
        xor   r9,r9
        call  printf

; Close the file
goteof: mov   rcx,[filpoi]
        xor   rdx,rdx
        xor   r8,r8
        xor   r9,r9
        call  fclose

; Wait for a key-press - if the user types "x" we exit right away
        xor   rcx,rcx
        call  _getch
        cmp   al,'x'
        je    goout

; Otherwise we go on to find the next file, if there is one
        mov   rcx,[hfile]     ; The findfirst handle again
        mov   rdx,finddata64
        xor   r8,r8
        xor   r9,r9
        call  _findnext64
;
        or    rax,rax
        jz    filope          ; --> Found one, so go back and open it
;
        jmp   goout           ; --> No more files found

; ---------------------------------------------------

; Error exit
error:  mov   rcx,errmes
        xor   rdx,rdx   
        xor   r8,r8
        xor   r9,r9
        call  printf

; Normal exit
; Call _findclose if necessary to terminate the directory loop
goout:  mov   rcx,[hfile]
        or    rcx,rcx
        jz    noneed 
        xor   rdx,rdx
        xor   r8,r8
        xor   r9,r9
        call  _findclose
noneed:

; Standard epilogue
        xor   rax,rax
;
        add   rsp,128
        pop   r13
        pop   r14
        pop   r15
        ret

; ====================================================================

section .data

wci     db      "*.txt",0 ; Wild-card will find all .txt files 
ipmods  db      "rb",0    ; Modes for file binary read

        align   16
filpoi  dq      0         ; FILE pointer
hfile   dq      0         ; Handle from findfirst

; An instance of the structure finddata64_t. The initializations (the lines
; beginning with "at") are not always necessary.
finddata64:
  istruc finddata64_t
  at attrib,      dq  0
  at time_create, dq  0
  at time_access, dq  0
  at time_write,  dq  0
  at size,        dq  0
  at name,        db  0
iend

errmes   db   "Error",0
mess1    db   'Command line is ->%s<-',0x0a,0x0d, 'Please type your name: ',0
youent   db   'You entered "%s"',0x0a,0x0a,0x0d,0
fileinfo db   'File name "%s," file size %lld',0x0d,0x0a,0
bytesmes db   '%2.2x ',0
bmafter  db   0x0d,0x0a, 0x0d,0x0a, 0x07,0    ; The 7 is a proper beep

; ====================================================================

section .bss

; Input buffer
        alignb  16
buffer: resb  buflen

; End of the programme ===============================================
Report Spam   Logged

Social Buttons

Sydney Grew
Level 7
*******

Times thanked: 28
Offline Offline

Posts: 1447


View Profile
« Reply #1 on: December 18, 2011, 03:07:01 pm »

Here is a straightforward batch file which will assemble and link the programme. Of course the addresses of nasm and MS Visual Studio will have to be changed to suit your own installation; in my case Sample.asm is in a directory on drive e, and the batch file is run from there.

------------------------------------------------------------------------------------
c:\nasm\nasm -f win64 -Ox -Z Sample.err Sample.asm -l Sample.lst
if errorlevel 1 goto nasmfail
k:
cd \"Microsoft Visual Studio 10.0"\VC
call vcvarsall.bat x64
e:
link Sample.obj /subsystem:console /defaultlib:msvcrt.lib /defaultlib:kernel32.lib /entry:_start
if errorlevel 1 goto linkfail
pause
goto end

:nasmfail
:linkfail
@echo There were errors, so examine Sample.err
pause

:end
-----------------------------------------------------------------------------------

And a few notes:

1) GetCommandLineA is an example of how to call a Windows API. Any of the hundreds of Windows APIs may be called in a similar way. If you use arguments on the command line after the programme name, for some reason Windows inserts an extra space, such that there will be TWO spaces dividing the programme name from the first argument. Also, if you invoke the programme in some way other than from a command prompt window, Windows will return the command line in an altered format, inside quotation marks.

2) Register usage: Note that registers r13, r14, and r15 should be preserved by all functions that are called. More information about that, and about the prologue and epilogue, should be available here: http://msdn.microsoft.com/en-US/library/tawsa7cb%28v=VS.80%29.aspx

3) printf: The format specifiers %d %c %s and so on may all be used in exactly the same way as in a "c" programme. But escape sequences such as "\n" will not work. That is why the CR and LF (0x0d and 0x0a) are explicitly supplied where desired. Note too the important zero which must always terminate the strings to be printed.

4) To display quadwords in printf you may use %lld or %llf (where the "ll" is two lower-case "L"s, not the number 11).

5) In one of the printf statements I have inserted a 07 character, to demonstrate a proper beep, not the often inaudible and pathetic sound produced by MS Windows.

6) Of course if you are not invoking the programme from the command line you should call _getch again right at the end so that the window is not closed before the screen can be read!

7) The executable easily fits into one allocation unit of 4096 bytes, which is something like one-tenth of the space required by the equivalent thing written in "c".
Report Spam   Logged
ahinton
Level 6
******

Times thanked: 26
Offline Offline

Posts: 851


View Profile WWW
« Reply #2 on: December 18, 2011, 04:21:16 pm »

written in "c"
Ah, yes - rather than transposing the transposing instruments' parts in the score; I'm afraid that this is what I always do - write all the parts at pitch (except piccolo, contrabassoon and double basses which I transpose at the octave comme d'habitude).

Sorry - as you were...
Report Spam   Logged
Sydney Grew
Level 7
*******

Times thanked: 28
Offline Offline

Posts: 1447


View Profile
« Reply #3 on: December 20, 2011, 12:35:14 pm »

By way of supplement I would like to give a little detail about a very strange phenomenon indeed.

First consider this "c" programme - it is about the simplest "c" programme imaginable; it adds 1 to 2 and obtains the result 3:

-------------------------------------------------------------------------------
/*
    An extremely simple "c" programme
*/
int main( void )
  {
  int numa, numb, numc;
 
  numa = 1;
  numb = 2;
  numc = numa + numb;
 
  printf( "The answer is %lld\n", numc );
  }
-----------------------------------------
k:
cd \"Microsoft Visual Studio 10.0"\VC
call vcvarsall.bat x64
e:
cl Simplec.c
pause
--------------------------------------------------------------------------------

After compilation the resultant executable file simplec.exe occupies 51,712 bytes on my hard drive (which of course will be rounded up to the next highest allocation unit, and becomes 53,248 bytes on my machine).

Secondly, now, consider this assembly language programme, which calls a "c" function. Again the "c" function adds 1 to 2 and obtains the result 3. The only real difference between this function and the main programme above is that this is called from a main programme written in assembly language:

--------------------------------------------------------------------------------

; Example of calling a "c" function from an assembly language main programme.

; ====================================================================

extern printf
extern _getch
extern trythis

; ====================================================================

default rel

; ====================================================================

section .code

[BITS 64]
global _start

; The conventional prologue
_start: mov   [rsp + 8],rcx
        push  r15
        push  r14
        push  r13
        sub   rsp,128           ; Stack space

; Send a C function two numbers to be added
        mov   qword [numa],1
        mov   rcx,[numa]
        mov   rdx,2
        xor   r8,r8
        xor   r9,r9
        call  trythis       ; The return value will come back in rax

; Display the return value
        mov   rcx,retmes
        mov   rdx,rax   
        xor   r8,r8
        xor   r9,r9
        call  printf

; Wait for a key-press
        xor   rcx,rcx
        call  _getch

; Standard epilogue
        xor   rax,rax
;
        add   rsp,128
        pop   r13
        pop   r14
        pop   r15
        ret

; ====================================================================

section .data

numa    dq  0
retmes  db  'We are now back after calling the C function; it has returned %lld',0x0a,0x0d,0

; End of Programme ===================================================

--------------------------------------------------------------------------------
/*
   An extremely simple "c" function
*/

int trythis( int numa, int numb )
  {
  int numc;
 
  numc = numa + numb;
 
  printf( "We are now in the c function and the answer is %lld\n", numc );
  return numc;
  }
--------------------------------------------------------------------------------
c:\nasm\nasm -f win64 -Ox -Z SampleCF.err SampleCF.asm -l SampleCF.lst
if errorlevel 1 goto nasmfail
k:
cd \"Microsoft Visual Studio 10.0"\VC
call vcvarsall.bat x64
e:
cl /c Simplecf.c
if errorlevel 1 goto cfail
link SampleCF.obj Simplecf.obj /subsystem:console /defaultlib:msvcrt.lib /defaultlib:kernel32.lib /entry:_start
if errorlevel 1 goto linkfail
pause
goto end

:nasmfail
:cfail
:linkfail
@echo There were errors!
pause

:end
--------------------------------------------------------------------------------

After assembly and compilation the resultant executable file SampleCF.exe occupies just 4096 bytes on my hard drive. In other words, the version with a single "c" main programme takes 13 times the space of the version with an essentially identical "c" function called from assembly language. Odd is it not? Fishy even perhaps.

And of course the assembly code - if it is time critical - is guaranteed to run about five times faster than the "c" code.

I may add that it is possible to make a "c" function called in this way as complex as one wishes. And I have tried opening files in the assembler section and passing the FILE pointer to a "c" function as argument - no problem at all.
Report Spam   Logged
unRheal
Guest
« Reply #4 on: January 28, 2012, 02:18:35 am »

Thanks for the sample 64-bit code, just what I was looking for - and found thanks to Google.

It does seem a bit of an odd place to post it though, as was presumably being implied by ahinton(?)

Much appreciated. Cheers!
Report Spam   Logged
Sydney Grew
Level 7
*******

Times thanked: 28
Offline Offline

Posts: 1447


View Profile
« Reply #5 on: January 28, 2012, 01:41:13 pm »

Thanks for the kind message - I'm very glad you find it useful.
Report Spam   Logged
jasonmath11
Guest
« Reply #6 on: September 27, 2012, 10:35:43 am »

From starting i have not any interest in assembly language.having good knowledge of JAVA and C. 
Report Spam   Logged

Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by EzPortal
Bookmark this site! | Upgrade This Forum
SMF For Free - Create your own Forum

Buy traffic for your forum/website
traffic-masters
Powered by SMF | SMF © 2016, Simple Machines
Privacy Policy