Jan's Linux & Assembler (x86) HomePage (http://www.linuxfan.com/~asm_eng
)
GNU/Linux is a wonderfull OS, but when I started to use Linux it was difficult to find information about programming in the assembly language with Linux. So, that is why I have started this Linux & Assembly webpage.
But things has changed now :-) Finding information about Linux & Assembly is not that difficult anymore. For this reason I consider my webpage obsolete, especialy because I am not an expert at Linux and/or Assembly.
If you are looking for good information about Linux & Assembly, you can take a look at the following webpages :
Below you can find my experiments about Linux & Assembly. You will also find some example-programs that I have received from other nice Linux-users.The disadvantage of GAS is that gas makes use of the AT&T-syntax, who is very different of the Intel-syntax, that is used by almost every DOS-user.
More information about the AT&T syntax can be found in
'info'. When you prefer a more Intel-compatible syntax, you can
write your program with Nasm.
= Deze bewaar ik : Asm =================================================KeepIt= From : James Vahn 17-Apr-97 15:20:22 1:346/15.1 To : Jan Wagemakers 2:292/8133.23 Subj : NASM & Linux =======================================================================REM_ASM= * Copied from: 80XXX JW> There are demos included, but so far I can see they are JW> written to be called from a C-program. I was wondering how I JW> can write a 100% pure assembly program with NASM under JW> Linux. Use bug-fixed nasm-0.94, and assemble the source below like: nasm -f elf hello.asm gcc hello.o -o hello strip hello There is much to learn. :-) ;-------------NASM's standalone Hello-World.asm for Linux -------- section .text extern puts global main main: push dword msg ;stash the location of msg on the stack. call puts ;call the 'puts' routine (libc?) add esp, byte 4 ;clean the stack? ret ;exit. msg: db "Hello World!",0 --- timEd 1.01 * Origin: James Vahn (jvahn@short.circuit.com) (1:346/15.1) ===============================================================================In this example call puts is used to print a text on the screen. Puts comes from 'C', and is standard present in the libraries that are loaded by Linux. But there is a second way to do certain things by assembly, it is INTerrupt 0x80.
You will often hear that it is not recommended to make use of Int 0x80, but making use of Int 0x80 has certain avantages. You can find more info about Int 0x80 on http://www.linuxassembly.org/
Example 1. By using call puts.
.text message: .ascii "Hello world!\0" .align 4 .globl main main: pushl %ebp movl %esp,%ebp #call ___main pushl $message call puts addl $4,%esp xorl %eax,%eax movl %ebp,%esp popl %ebp ret as hello.s -o hello.o gcc hello.o -o helloExample 2. By using INT 80h.
.text
message:
.ascii "Hello, World!\12\0"
.align 4
.globl _hw
_hw:
movl $4, %eax
movl $1, %ebx
movl $message, %ecx
movl $15, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
as hello.s -o hello.o
ld hello.o -e _hw -o hello
(_hw = entry-point)
But... after done some experiments and reading the info-files of GNU-C I discovered that it is possible to add macro's in a assembly program, just by making use of Gas (as)
Below, I show you an experiment of me, that demonstrate the possibilities of Gas & Macro's :
___ test_fopen.s __________________________________________________________
.include "include.asm"
.globl main
main:
_print hallo # Print title
_open bestand mode # Open the file 'dummy.dummy'
cmp $0,%eax # Success?
je file_error # No, Print error message
_close %eax # Yes, Close file
_print bestaat # Print text 'exist'
jmp einde # End of prg.
file_error:
_print bestaat_niet # Print text 'doesn't exist'
einde:
ret # The End ;-)
hallo:
.string "Test Linux Program ;-) \n"
bestaat:
.string "The file dummy.dummy exists..."
bestaat_niet:
.string "The file dummy.dummy doesn't exist..."
bestand:
.string "dummy.dummy"
mode:
.string "r"
.END
___________________________________________________________________________
___ include.asm ___________________________________________________________
.MACRO _print message
# start _print message
pushl $\message
call puts
addl $4,%esp
# end _print message
.ENDM
.MACRO _open file mode
# start _open file mode
pushl %ebp
movl %esp,%ebp
pushl $\mode
pushl $\file
call fopen
addl $8,%esp
movl %eax,-4(%ebp)
# %eax = file-handle
# - if %eax = 0 then the file doesn't exist
# - if %eax >< 0 %eax is the file-handle
popl %ebp
# end _open file mode
.ENDM
.MACRO _close filehandle
# start _close filehandle
pushl \filehandle
call fclose
addl $4,%esp
# end _close filehandle
.ENDM
___________________________________________________________________________
as test_fopen.s -o test_fopen.o
gcc test_fopen.o -o test_fopen
Below I show you an example-program where you can see how you can call ncurses from a pure assembly-program. It is not very spectacular, but nice for watching to :-)
___ sat_color.s ___________________________________________________________
.include "/home/jan/assembler/include/ncurses.asm"
.globl main
main:
_initscr
_start_color
_init_pair $1,$2,$4
_init_pair $2,$0,$6
_init_pair $3,$3,$4
_init_pair $4,$4,$4 # Hide the flashing cursor
call cls # clear screen/init colors.
_use_pair $0x00000200 # 00 00(NORMAL) 02(PAIR) 00
_locate $1,$0
_printw $titel
_locate $46,$0
_printw $pd
_use_pair $0x00200100 # 00 20(BOLD) 01(PAIR) 00
_locate $32,$12
_printw $world
_use_pair $0x00200300 # 00 20(BOLD) 03(PAIR) 00
movl $0,%esi
lus:
movb tabel(%esi),%dl # %dl = X(%esi)
incl %esi
movb tabel(%esi),%cl # %cl = Y(%esi)
incl %esi
cmpb $242,%cl
jne n242_1
movl $0,%esi
jmp lus
n242_1:
movl %esi,%edi
redo:
movb tabel(%edi),%dh # %dh = X(%esi + 1)
incl %edi
movb tabel(%edi),%ch # %ch = Y(%esi + 1)
cmpb $242,%ch
jne n242_2
movl $0,%edi
jmp redo
n242_2:
movl $leeg,%ebp
call print_item
movl $linux,%ebp
movb %ch,%cl
movb %dh,%dl
call print_item
pushl $160000 # C : usleep(....);
call usleep # Wacht-lus
addl $4,%esp
_refresh
jmp lus
_endwin
ret
print_item:
pushal
movzbl %cl,%eax
movzbl %dl,%ebx
_locate %ebx,%eax
_printw %ebp
popal
ret
cls:
_use_pair $0x00000200 # 00 00(NORMAL) 02(PAIR) 00
# Color of the first line
movb $0,%cl
movb $0,%dl
cls_lus:
movl $chr32,%ebp
call print_item
incb %dl
cmpb $79,%dl
jna cls_lus
pushal
_use_pair $0x00000400 # 00 00(NORMAL) 04(PAIR) 00
# Color of the rest of the screen
popal
xorb %dl,%dl
incb %cl
cmpb $25,%cl
jna cls_lus
ret
linux:
.string "Linux"
leeg:
.string " "
chr32:
.string " "
world:
.string "World Domination"
titel:
.string "sat_color.s - 1997 Jan Wagemakers -"
pd:
.string "Donated to the Public Domain :-)"
tabel:
.include "cirkel.dat"
.byte 242,242
.END
___________________________________________________________________________
___ cirkel.dat ____________________________________________________________
.byte 72 , 12
.byte 71 , 13
.byte 69 , 15
.byte 66 , 17
.byte 62 , 18
.byte 56 , 20
.byte 51 , 21
.byte 44 , 21
.byte 38 , 21
.byte 31 , 21
.byte 24 , 21
.byte 18 , 20
.byte 13 , 19
.byte 8 , 17
.byte 5 , 16
.byte 3 , 14
.byte 2 , 12
.byte 2 , 10
.byte 3 , 8
.byte 6 , 7
.byte 10 , 5
.byte 15 , 4
.byte 20 , 3
.byte 27 , 2
.byte 33 , 2
.byte 40 , 2
.byte 47 , 2
.byte 53 , 3
.byte 59 , 4
.byte 63 , 5
.byte 67 , 7
.byte 70 , 8
.byte 71 , 10
___________________________________________________________________________
___ /home/jan/assembler/include/ncurses.asm _______________________________
# ncurses.asm - donated to the public domain by Jan Wagemakers -
# afgeleid van onderstaand C-programma
# #include <curses.h>
#
# int main(void)
# {
# initscr(); /* Init the curses libraries */
# move(10, 2); /* Place the cursor at X: 2, Y: 10 */
# printw("Hello, World !"); /* Print anything */
# refresh(); /* This places the "Hello, World !" on the physical screen */
# getch(); /* Wait for keypress */
# endwin(); /* deinit the curses libraries. */
# return 0;
# }
#
# Because I know NOT very much of C The following can be incorrect.
# Please, do not hesitate to make corrections :-)
# So, when you want to put something on the screen with ncurses, you call
# the following macro's :
# 1. _initscr
# 2. _locate x y (x,y = screen coordinates)
# 3. _printw message
# 4. _refresh
# 5. _endwin (end win.... sounds nice, isn't it ;-)
.MACRO _initscr
# start _initscr
call initscr
# end _initscr
.ENDM
.MACRO _locate x y
# start _locate x y
pushl \x
pushl \y
movl stdscr,%eax
pushl %eax
call wmove
addl $12,%esp
# end _locate x y
.ENDM
.MACRO _printw message
# start _print message
pushl \message
call printw
addl $4,%esp
# end _printw message
.ENDM
.MACRO _refresh
# start _refresh
movl stdscr,%eax
pushl %eax
call wrefresh
addl $4,%esp
# end _refresh
.ENDM
.MACRO _endwin
# start _endwin
call endwin
# end _endwin
.ENDM
# Colors and ncurses. (15/07/97)
# - After _initscr , call _start_color
# - Init with _init_pair a color-pair.
# - With _use_pair select a color-pair.
.MACRO _start_color
# start _start_color
call start_color
# end _start_color
.ENDM
.MACRO _init_pair pair foreground background
# start _init_pair
pushl \background
pushl \foreground
pushl \pair
call init_pair
addl $12,%esp
# end _init_pair
.ENDM
.MACRO _use_pair pair
# start _use_pair
movl \pair,%eax
# | | %ah | %al |
# %eax = xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
# | |
# | |
# | +----> Number of color-pair
# +-------------> 00 = Normaal , 20 = BOLD
pushl %eax
movl stdscr,%eax
pushl %eax
call wattr_on
addl $8,%esp
#end _use_pair
.ENDM
___________________________________________________________________________
as sat_color.s -o sat_color.o
gcc sat_color.o -o sat_color -lncurses
sat_color
==============================================================================
Area: 80XXX
From: James Vahn To: Fernando Ariel Gont
Subject: Re: Serial port: BIOS function
==============================================================================
> Yes, I was going to program the 8250 at low level, but I simply
> wanted to test the port, so that I used the int 14h functions...
> Well, really, I tried, but didn't success! :)
Here's a little Linux program that I use to reset the modem LED.
Sometimes killing a program will leave the LED glowing, and this
will generally put it out.
It should be plain enough, but for the call to ioperm. In Linux you
have to ask for permission as 'root' to talk to ports, and that's
a given under DOS where you can simply skip this call.
;---wink the DTR light on the modem. Run suid root.
global main
extern ioperm
extern sleep
Port equ 0x2F8 + 4 ; /dev/ttyS1 control port.
section .text
main:
mov eax, Port
push eax
push dword 1
push eax
call ioperm ; Get I/O permissions (port,1,port)
add esp,12
mov dx, Port
in al,dx
or al,00000001b ; Raise DTR.
out dx,al
push dword 1
call sleep ; sleep 1 second.
add esp,4
mov dx, Port
in al,dx
and al,00000000b ; Drop DTR & RTS.
or al,00001100b
out dx,al
ret
--- Linux Inside
* Origin: 300 miles East of Seattle, WA (1:346/3@fidonet)
==============================================================================
==============================================================================
Area: 80XXX
From: James Vahn To: Jan Wagemakers
Subject: Re: Linux asm programming
==============================================================================
From: James Vahn <jvahn@short.circuit.com>
Linux assembly is fine with me! ;-)
These seem to toggle between character sets.
echo -e "\033(B"
echo -e "\033(K"
Running one or the other produces different characters, not sure of the
reason unless it's for non-english purposes. Wish I understood more
about this. There seems to be three pre-defined character sets in the
kernel source. This all strikes me as odd because it would seem to
be a job for an external driver, like setfont.
This snippet produces an ASCII chart so you can see what I mean.
; nasm aascii.asm -o aascii.o
; gcc aascii.o -o aascii
global main
extern printf
extern putchar
section .text
main:
push ebp ;Save these.
mov ebp,esp ;
mov ebx,33 ;Start with character 33d, "!"
l1:
push ebx
push ebx
push dword msg
call printf ;Print the code and character
add esp,8
inc bl
cmp bl,0 ;Characters 33 to 255,
jnz l1 ; if BL rolls to 0 we are done.
mov eax,10
push eax ;Print a linefeed.
call putchar
mov esp,ebp ;Clean up and
pop ebp ; exit.
ret
;section .data
msg db 9," %d=%c ",0
---
* Origin: 300 miles East of Seattle, WA (1:346/3@fidonet)
==============================================================================
==============================================================================
Area: 80XXX
From: James Vahn To: Matias Rizzone
Subject: Re: scrlock
==============================================================================
> i need a code for unlock the scroll lock key...
try toggling bit 4 of 40:17 and calling 16/1 afterwords.
Here's another method. Might need a little work in the buffer delay on
faster machines; it runs pretty quick on my 386DX/40.
;------------------------------------------------
; by MARC KOOY (port to Linux/Nasm by James Vahn)
;
;"LedsGo -- A program to cycle LED's of keyboard"
;
; nasm ledsgo.asm -o ledsgo.o
; gcc ledsgo.o -o ledsgo
; chown root ledsgo
; chmod 4750 ledsgo
global main
extern ioperm
;extern sleep
R EQU 00000001b
L EQU 00000010b
M EQU 00000100b
Port EQU 0x60
section .text
main:
Mov eax, Port
Push eax
Push dword 5
Push eax
Call ioperm
Add esp,12
Mov cx,0Ah
lgm1: Mov [LedsOn], byte L
Call UpdateLeds
Mov [LedsOn], byte M
Call UpdateLeds
Mov [LedsOn], byte R
Call UpdateLeds
Loop lgm1
Ret
UpdateLeds: ; Use this Proc to update the LED's when
PushA ; LedsOn is filled with a new value
Mov AL, 0EDh
Out Port, AL ; Give "Change LED status" command
BufferDelay1:
In AL, Port + 4 ; Read Status register
Test AL, 02h
JNZ BufferDelay1 ; Wait till the Command register is empty
Mov AL, [LedsOn]
Out Port, AL ; Give data for LED's that must go on
BufferDelay2:
In AL, Port + 4 ; Read Status register
Test AL, 02h
JNZ BufferDelay2 ; Wait til the Data register is empty
Mov BX,02h
ul1: Mov CX,0FFFFh
Loop $
Dec BX
Jne ul1
PopA
Ret ; End of UpdateLeds
section .data
LedsOn db 1
---
* Origin: 300 miles East of Seattle, WA (1:346/3.1@fidonet)
==============================================================================
Together with the information that I have received by E-mail from 'paranoya' (Thanks!) I have tried to port this program to Linux assembly.
Below you will find the result. Note that my main goal was to show that it can be done. I have not tried to optimize the program, so, If you like you can optimize the program as an exercise ;-)
# fire.s : fire.asm of apj 4 ported to Linux/SVGAlib ==========================
# gcc -o fire fire.s -lvga
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
call vga_init # Init vga
pushl $5
call vga_setmode # set mode to 5 = 320x200x256
addl $4,%esp
pushl $0
call vga_setpage # Point to page 0 (There is only 1 page)
addl $4,%esp
inb $0x60,%al # Read current value of keyboard
movb %al,key
movw $0x3c8,%dx
movw $0,%ax
outb %al,%dx
incw %dx
lus:
outb %al,%dx
outb %al,%dx
outb %al,%dx
incw %ax
jnz lus
movl graph_mem,%ebx
Mainloop:
movl $1280,%esi # mov si,1280 ;
movl $0x5d00,%ecx # mov ch,5dh ; y-pos, the less the faster demo
pushl %esi # push si
pushl %ecx # push cx
Sloop:
movb (%ebx,%esi),%al # lodsb
incl %esi #
addb (%ebx,%esi),%al # al,[si] ; pick color and
addb 320(%ebx,%esi),%al # add al,[si+320] ; pick one more and
shrb $2,%al # shr al,2
movb %al,-960(%ebx,%esi) # mov [si-960],al ; put color
loop Sloop
popl %edi # pop di
popl %ecx # pop cx
Randoml:
mulw 1(%ebx,%edi) # mul word ptr [di+1] ; 'random' routine.
incw %ax
movw %ax,(%ebx,%edi) #stosw
incl %edi
incl %edi
loop Randoml
inb $0x60,%al
cmpb key,%al
jz Mainloop
pushl $0
call exit
addl $4,%esp
movl %ebp,%esp
popl %ebp
ret
.data
key:
.byte 0
# =============================================================================
the file eq2.c
*********************************************************
extern Calcul_Delta();
extern Calcul_X1_X2();
#include <stdio.h>
/* in global, these var are ok for the asm programs */
float a,b,c,Delta,X1,X2;
float Quatre=4.0, Deux=2.0;/*you can't in immediat !!!*/
main()
{
printf("Value of a :");
scanf("%f",&a);
printf("Value of b :");
scanf("%f",&b);
printf("Value of c:");
scanf("%f",&c);
Calcul_Delta();
printf("Value of Delta is : %f \n",Delta);
Calcul_X1_X2();
printf("Value of X1 is : %f \n",X1);
printf("Value of X2 is : %f \n",X2);
}
*********************************************************
the file Delta.s
*********************************************************
.text
.global Calcul_Delta
Calcul_Delta:
flds b #stack b
fmuls b #stack b*b
flds Quatre #stack 4 b*b
fmuls a #stack 4*a b*b
fmuls c #stack 4*a*c b*b
fsubrp #stack b*b-4*a*c
fstps Delta #stack empty
ret
*********************************************************
the file X1X2.s
*********************************************************
.text
.global Calcul_X1_X2
Calcul_X1_X2:
flds Deux #stack 2.0
fmuls a #stack 2*a
flds Delta #stack Delta 2*a
fsqrt #stack sqr(Delta) 2*a
fst %st(2) #stack sqr(Delta) 2*a sqr(Delta)
#Calcul X1=(-b+sqr(Delta))/2*a
fsubs b #stack -b+sqr(Delta) 2*a sqr(Delta)
fdiv %st(1),%st #stack (-b+sqr(Delta))/2*a 2*a sqr(Delta)
fstps X1 #stack 2*a sqr(Delta)
#Calcul X2=(-b-sqr(Delta))/2*a
fxch #stack sqr(Delta) 2*a
fchs #stack -sqr(Delta) 2*a
fsubs b #stack -b-sqr(Delta) 2*a
fdivp #stack (-b-sqr(Delta))/2*a
fstps X2 #stack empty
ret
*********************************************************
YOU must make :
as X1X2.s -o X1X2.o
as Delta.s -o Delta.o
gcc Delta.o X1X2.o eq2.c -o eq2
; --------------------------------
; D.R tuesday, february 22th 2000
; **Rencontres du troisieme type**
; ufo_3.asm "for Linux"
; --------------------------------
; nasm -f elf ufo_3.asm -o ufo_3.o
; ld -s ufo_3.o -o ufo_3
; su "YOUR ROOT PASSWORD"
; chown root ufo_3
; chmod 4750 ufo_3
; exit
; ./ufo_3
; ================================
struc timespec ;-Model for "nanosleep" structure .
second: resd 1
nanosec: resd 1
endstruc
Octcmd EQU 0B6h ;-Command word for "8254 timer" command
; register.
Fclkl EQU 34DCh ;-Clock frequency least significant byte.
Fclkh EQU 0012h ;-Clock frequency most significant byte.
Port1 EQU 61h ;-Gate adress port for (unabled / disabled)
; "8254 timer" timer(2).
Port2 EQU 40h ;-Basis "8254 timer" adress.
section .data
;===============
temp1 istruc timespec ;-Tuning temporisation (basis 1/8 s).
at second, dd 0 ;-"Fundamental" structure for "nanosleep".
at nanosec, dd 125000000
iend
temp2 istruc timespec ;-"Saving" structure (case of interrupt).
at second, dd 0
at nanosec, dd 1
iend
A3 dw 880 ;-The note A(3).
dd 1 ;-Relative duration / basis.
B3 dw 988 ;-And so on...
dd 1
H3 dw 784
dd 1
H2 dw 392
dd 1
E2 dw 587
dd 2
Basis dd 125000000 ;-Basis temporisation = 1/8 s.
Duration dd 1 ;-Temporisation = Basis x Duration
section .text
; =============
; The "main" procedure
; ++++++++++++++++++++
global _start
_start:
; Validation of "8254 timer" ports and command port of timer(2) GATE
; ------------------------------------------------------------------
mov eax,101 ;-"ioperm" (system call = 101).
mov ebx,Port1 ;-Command port timer(2) adress.
mov ecx,1 ;-One adress to valid.
mov edx,Port1
int 80h
mov eax,101 ;-"ioperm" (system call = 101).
mov ebx,Port2 ;-Basis adress of "8254 timer".
mov ecx,4 ;-Four successives adresses to valid.
mov edx,Port2
int 80h
; And now play the music...
; ------------------------
mov bx,[A3] ;-Tuning timer(2) frequency of "8254 timer".
mov eax,[A3 + 2] ;-Note duration (for temporisation).
mov [Duration],eax
call sound
mov bx,[B3] ;-And so on...
mov eax,[B3 + 2]
mov [Duration],eax
call sound
mov bx,[H3]
mov eax,[H3 + 2]
mov [Duration],eax
call sound
mov bx,[H2]
mov eax,[H2 + 2]
mov [Duration],eax
call sound
mov bx,[E2]
mov eax,[E2 + 2]
mov [Duration],eax
call sound
; The end...
; ----------
mov eax,1 ;-"exit" (system call = 1).
xor ebx,ebx ;-Return code = 0.
int 80h
; Tuning frequency procedure
; ++++++++++++++++++++++++++
frqsound: push eax ;-Save the context.
push edx
mov al,Octcmd ;-"8254 timer" timer(2) configuration.
out 43h,al
mov ax,Fclkl ;-Frequency divisor calculation.
mov dx,Fclkh
div bx
out 42h,al ;-Frequency divisor writting in "8254 timer"
mov al,ah ; timer(2) latches.
out 42h,al
pop edx ;-Reload the context.
pop eax
ret
; "8254 timer" timer(2) unabled/disabled procedure
; ++++++++++++++++++++++++++++++++++++++++++++++++
undisabl: push eax
in al,61h ;-Bits D1-D0 of "port 61h" are unmasked
xor al,03h ; or masked !
out 61h,al
pop eax
ret
; Temporisation procedure
; +++++++++++++++++++++++
tempor: push eax ;-Save the context.
push ebx
push ecx
push edx
mov eax,[Basis]
mul dword[Duration]
mov [temp1 + nanosec],eax
mov eax,162 ;-"nanosleep" (system call = 162).
mov ebx,temp1 ;-Load "fundamental" strucure.
mov ecx,temp2 ;-Load "saving" structure.
int 80h
pop edx ;-Reload the context.
pop ecx
pop ebx
pop eax
ret
; Send out note procedure
; +++++++++++++++++++++++
sound: call frqsound ;-Tuning frequency.
call undisabl ;-Loudspeaker "unabled".
call tempor ;-Hold the note.
call undisabl ;-Loudspeaker "disabled".
ret
; ===============================
; Dominique RICHIER
; 18, rue du General de Castelnau
; 54600 VILLERS-les-NANCY
; FRANCE
; d.richier@caramail.com
; ===============================
You can dowload the source here : pic-asm.tar.gz
Jan Wagemakers
Internet :
janw@digibel.org
Fidonet : 2:292/854.19