Menu (English) | Linux & Asm x86-32 | PIC microchip | Debian Packages | Links | Javastation Sun | Weblog | About
Menu (Nederlands) | Linux & Asm x86-32 | PIC microchip | Tux maakt koffie | Links |   | Weblog | Over
Last update : 2011-08-28

i. Preface :

Please forgive my flinstone-english, Yabbe-dabbe-doe ;-)))

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 x86-32. You will also find some example-programs that I have received from other nice Linux-users. 
Information about my experiments with x86-64 assembly is available at my weblog.

1. Assembler & Linux :

When you want to write some Linux assembly programs, there are a lot of possibilities. First, you can choose which assembler you going to use. A logical choice is GAS (as), the assembler that is standard in almost every Linux distribution.

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.


1.1. Let's start with an example for Nasm :

From the 80XXX-FidoNet-Area :
= 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://linuxassembly.org/


1.2. And now, two examples for Gas :

From assembler.028 (fidonet), thanks to Pieter de Jong for this nice examples.

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 hello
Example 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)

1.3. Adding Macro's to GAS :

By making use of macro's you can make the programming less complex. In certain documents I had read that Gas itself can not recognize Macro's and that you must make use of Gasp to implement macro's in your assembly-work.

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

1.4. Using ncurses

Ncurses is a library with functions that can be used to write text-oriented Linux-programs.

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

1.5. Examples from 80XXX fidonet

==============================================================================
   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)
==============================================================================

1.6. SVGAlib & Assembler

In apj 4 of Assembly Programming Journal you can find a very nice and little "fire-demo" that was written in DOS assembly.

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
# =============================================================================

1.7. Example : 387 inline asm

Pascal Fougeray has send me the following program that shows how to use 387 inline assembly with Linux.
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

1.8. Example : Music with the PC-speaker

Dominique Richier has send me the follwoing program that uses that PC-speaker to play music. (nasm 0.98)
; --------------------------------
; 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
; ===============================

1.9. Example : shared libraries

Robin Miyagi has send me the following program that : "I have some sample code for your site. This code gives a somewhat useless example of writing shared libraries."

You can dowload the source here : pic-asm.tar.gz


1.10. Tetris Linux asm

Daniel Burr has send me this source of Tetris written in assembly. More info and the most recent version can be found at http://bordello.2y.net:8000/programs/tetris.
#Tetris in x86 assembly
#Copyright 2000,2001 dburr@ug.cs.usyd.edu.au, under the terms of the GPL
#
#Notes:
#Uses VT100 terminal codes to position the cursor and to draw coloured text.
#I also assume that this requires a > 2.0 Linux kernel which supports
#sys_newselect (also uses sys_write, sys_read, sys_nanosleep, sys_exit,
#sys_times and sys_ioctl).  I have only tested it with 2.0.x, 2.2.x and 2.4.x
#kernels.  I'm pretty sure that this will work with 386+ processors.
#
#Example Makefile:
#--start--
#NAME = tetris
#ENTRY = _tetris
#
#SYMS =  -defsym instructionsX=12
#SYMS += -defsym instructionsY=19
#SYMS += -defsym width=10
#SYMS += -defsym xoffset=3
#SYMS += -defsym height=16
#SYMS += -defsym yoffset=2
#SYMS += -defsym wait=50
#SYMS += -defsym scoredrop=2
#SYMS += -defsym scorelockin=3
#SYMS += -defsym scoreline=100
#SYMS += -defsym scoretetris=1000
#SYMS += -defsym speedup=10
#
#$(NAME): $(NAME).o
#   ld -s -o $@ -m elf_i386 $^ -e $(ENTRY)
#
#$(NAME).o: $(NAME).s
#   as -o $@ $^ $(SYMS)
#
#clean:
#   rm -f $(NAME).o $(NAME)
#--end--
#Explanation of the symbols which can be changed to suit personal taste:
#instructionsX, instructionsY: The offset of the instructions from the
#top, left hand corner of the screen.
#width, height: The dimensions of the playing area
#xoffset, yoffset: The offset of the playing area from the top, left hand
#corner of the screen.
#wait: Controls the speed of the game.  Lower number equals faster play.
#scoredrop: Number of points scored for 'dropping' a piece (with spacebar).
#scorelockin: Number of points scored for 'locking' a piece in.
#scoreline: Number of points scored for eliminating a line (for 1-3 lines)
#scoretetris: Number of points scored for a 'tetris' (ie eliminating 4 lines
#at once).
#speedup: The game will get twice as fast for every n lines.

.macro sys_newselect
    xor %eax, %eax #smaller than writing to %eax directly
    mov $142, %al #new sys_select
    xor %ebx, %ebx
    mov $1, %bl
    mov selectargs+4, %ecx
    xor %edx,%edx
    xor %esi,%esi
    mov $timeout,%edi
    int $0x80
.endm

.macro sys_nanosleep length
    xor %eax, %eax
    mov $162, %al #sys_nanosleep
    movl $0,-8(%esp) #seconds
    movl \length,-4(%esp) #nanoseconds
    lea -8(%esp),%ebx
    xor %ecx, %ecx
    int $0x80
.endm

.macro sys_exit
    xor %eax, %eax
    mov $1, %al #sys_exit
    xor %ebx,%ebx #with 0 status
    int $0x80
.endm

.macro sys_times
    xor %eax, %eax
    mov $43, %al #sys_times
    xor %ebx,%ebx #NULL
    int $0x80
.endm

.macro getterm
    push %ebp
    mov %esp,%ebp
    lea -60(%ebp),%edx
    mov $0x5401, %ecx #TCGETS
    call sys_ioctl 
.endm

.macro setterm
    leal -60(%ebp),%edx
    mov $0x5403, %ecx #TCSETSW
    call sys_ioctl
    pop %ebp
.endm

#Write the chars equivalent to 'source' into vt100_position
.macro twodigits source first second
    mov \source, %ax
    inc %ax
    movb $10, %bl
    divb %bl
    add $0x30,%al
    movb %al, vt100_position+\first
    add $0x30, %ah
    movb %ah, vt100_position+\second
.endm

#Named in honour of the ncurses function
.macro mvaddstr y x string length
    twodigits \y, 2, 3
    twodigits \x, 5, 6
    mov $vt100_position, %ecx
    xor %edx, %edx
    mov $8, %dl
    call sys_write
    mov \string, %ecx
    xor %edx, %edx
    mov \length, %dl
    call sys_write
.endm

#Mask of the bits for the n'th block
.macro bitMask n
    .if 4-\n
        shr $8-2*\n, %dx
    .endif
    and $0x303, %dx #Lower two bits of each
    mov yposition, %ax
    add %dl, %al
.endm

#Put the y location of the n'th block in %ax, x location in %bx
.macro screenoffset n
    bitMask \n
    mov xposition, %bx
    add %dh, %bl
.endm

#Make %bx the offset of the n'th block from the start of the screen array
#where %dx is the piece in question
.macro pieceoffset n
    bitMask \n
    imul $width, %ax
    add xposition, %ax
    shr $8, %dx
    add %dx, %ax
.endm

#Make real use of gas macros
.macro storeLoop from=1, to=4
    .if 4-\from
        movw (%esp), %dx
    .else
        pop %dx
    .endif
    pieceoffset \from
    movb %bl, (%eax,%ecx)
    .if \to-\from
        storeLoop "(\from+1)", \to
    .endif
.endm

.macro collisionLoop from=1, to=4
    .if 1-\from
        movw (%esp), %dx
    .endif
    pieceoffset \from
    movb (%ebx,%eax), %cl
    cmpb $0x30, %cl
    .if 4-\from
        jnz collisionTest_over
    .endif
    .if \to-\from
        collisionLoop "(\from+1)", \to
    .endif
.endm

.macro drawLoop from=1, to=4
    .if 1-\from
        movw (%esp), %cx
    .endif
    .if 4-\from
        mov 2(%esp), %dx
    .else
        pop %cx
        pop %dx
    .endif
    screenoffset \from
    call drawblock
    .if \to-\from
        drawLoop "(\from+1)", \to
    .endif
.endm

.data

quitstring:
    .ascii "'q' to quit, arrow keys to move"

scorestring:
    .ascii "Score: "

linestring:
    .ascii "Lines: "

namestring:
    .ascii "Daniel's Tetris"

blankstring:
    .ascii "  "

exitstring:
    .ascii "User exitted"
    
newline:
    .ascii "\n" #Also used after the previous string

loserstring:
    .ascii "Loser\n"

creditstring:
    .ascii "Tetris in 3k, by dburr@ug.cs.usyd.edu.au\n"

score:
    .hword 0

timeout:
    .long 0
    .long 1 #1 millisecond wait which checking stdin

selectargs:
    .long 1 #Max is 0, +1 = 1
    .long -1 #Overwrite with stack pointer
    .long 0 #No write
    .long 0 #No except
    .long timeout

vt100_position:
    .byte 0x1b
    .ascii "[12;13H"

vt100_colour: #The proper english way of spelling the word!
    .byte 0x1b
    .ascii "[44m"

vt100_clear:
    .byte 0x1b
    .ascii "[2J"

vt100_cursor:
    .byte 0x1b
    .ascii "[?25l"

yposition:
    .hword 0

xposition:
    .hword 2

sleepcount:
    .byte 0

shapeStarts:
    .byte 2, 3, 5, 7, 11, 15, 19

shapeIndex: #This data contains the positions of the blocks in each shape
#Each requires 16 bits: x1<<14|x2<<12|x3<<10|x4<<8|y1<<6|y2<<4|y3<<2|y4
    .hword 0b0000010100010110 #0<<14|0<<12|1<<10|1<<8|0<<6|1<<4|1<<2|2
    .hword 0b0100100100010001 #1<<14|0<<12|2<<10|1<<8|0<<6|1<<4|0<<2|1
    .hword 0b0000010100010100 #0<<14|0<<12|1<<10|1<<8|0<<6|1<<4|1<<2|0
    .hword 0b0000000000011011 #0<<14|0<<12|0<<10|0<<8|0<<6|1<<4|2<<2|3
    .hword 0b0001101100000000 #0<<14|1<<12|2<<10|3<<8|0<<6|0<<4|0<<2|0
    .hword 0b0001011000000101 #0<<14|1<<12|1<<10|2<<8|0<<6|0<<4|1<<2|1
    .hword 0b0100010000010110 #1<<14|0<<12|1<<10|0<<8|0<<6|1<<4|1<<2|2
    .hword 0b0001011001000101 #0<<14|1<<12|1<<10|2<<8|1<<6|0<<4|1<<2|1
    .hword 0b0100010100010110 #1<<14|0<<12|1<<10|1<<8|0<<6|1<<4|1<<2|2
    .hword 0b0001100100000001 #0<<14|1<<12|2<<10|1<<8|0<<6|0<<4|0<<2|1
    .hword 0b0000000100011001 #0<<14|0<<12|0<<10|1<<8|0<<6|1<<4|2<<2|1
    .hword 0b0001101000000001 #0<<14|1<<12|2<<10|2<<8|0<<6|0<<4|0<<2|1
    .hword 0b0001000000000110 #0<<14|1<<12|0<<10|0<<8|0<<6|0<<4|1<<2|2
    .hword 0b0000011000010101 #0<<14|0<<12|1<<10|2<<8|0<<6|1<<4|1<<2|1
    .hword 0b0101010000011010 #1<<14|1<<12|1<<10|0<<8|0<<6|1<<4|2<<2|2
    .hword 0b0001010100000110 #0<<14|1<<12|1<<10|1<<8|0<<6|0<<4|1<<2|2
    .hword 0b0001100000000001 #0<<14|1<<12|2<<10|0<<8|0<<6|0<<4|0<<2|1
    .hword 0b0000000100011010 #0<<14|0<<12|0<<10|1<<8|0<<6|1<<4|2<<2|2
    .hword 0b1000011000010101 #2<<14|0<<12|1<<10|2<<8|0<<6|1<<4|1<<2|1

linesgone:
    .hword 0 #number of lines eliminated so far in the game

currentwait:
    .byte wait #gets smaller as the game gets faster

.bss

buffer:
    .byte 0, 0 #for arrow keys we read two

rotation:
    .byte 0 #overwrite with a random rotation

blockType:
    .byte 0 #overwrite with a random block type

currentcolour:
    .byte 0 #overwrite with random colour

stringbuffer:
    .fill 5

screen:
    .fill width*height

lastrand:
    .long 0

.globl _tetris
.text

#Return a 4-bit number in %al that is no greater than %cl
rand:
    movl lastrand, %eax
    mov %eax, %ebx
    imul $1664525, %eax;
    add $1013904223, %eax
    shr $10, %eax
    xor %ebx, %eax
    movl %eax, lastrand
    andb $0x7, %al
    cmp %al, %cl
    jb rand
    ret

#Requires the string to write in %ecx, length in %edx
sys_write:
    xor %eax, %eax
    mov $4, %al #sys_write
    xor %ebx, %ebx
    mov $1, %bl #stdout
    int $0x80
    ret

#Requires the length to read in %edx
sys_read:
    xor %eax, %eax
    mov $3, %al #sys_read
    xor %ebx, %ebx #fd stdin
    mov $buffer, %ecx #buffer
    int $0x80
    ret

#Requires the number of the call in %ecx
sys_ioctl:
    xor %eax, %eax
    mov $54, %al #sys_ioctl
    xor %ebx, %ebx
    int $0x80
    ret

#Take the current entry from the shapeIndex and push it on the stack
coords:
    xor %edx, %edx
    xor %eax, %eax
    mov blockType, %al
    test %al, %al
    jz coords_noIndex
    mov $shapeStarts, %ebx
    dec %ebx
    mov (%eax, %ebx), %dl
coords_noIndex:
    add rotation, %dl
    shl $1, %dl #because each entry is 2 bytes
    pop %eax
    pushw shapeIndex(%edx)
    jmp *%eax

#There are 4 squares in the current piece.  Test the lines which these
#occupy to see if they are part of a complete line.  If so, remove, redraw
#Also adds to the score and speeds the game up if necessary
elimline:
    mov yposition, %dx
    add $4, %dl
    xor %eax, %eax
    mov yposition, %al #%al contains the ypositions to test
    xor %dh, %dh #number of lines eliminated
    cmpb $height-1, %dl
    jl elimline_skip
    mov $height-1, %dl #%dl contains one more than the last value to test
elimline_skip:
    xor %ebx, %ebx
    mov $width, %bl
    imul %eax, %ebx
    add $screen, %ebx #ebx contains the start of the line
    xor %ecx, %ecx
elimline_test:
    inc %cl #%ecx contains the x position to test
    cmpb $0x30, (%ecx, %ebx) #test this for each position in line
    je elimline_linedone #ie: don't eliminate this line
    cmpb $width-2, %cl
    jne elimline_test
    inc %dh
    add $width, %ebx
elimline_loop:
    dec %ebx
    movb -width(%ebx), %cl
    movb %cl, (%ebx)
    cmp $screen+width, %ebx
    jne elimline_loop
elimline_linedone:
    inc %al
    cmp %al, %dl
    jne elimline_skip
    mov %dx, %cx #for testing linesgone later
    cmpb $4, %dh
    je elimline_tetris
    shr $8, %dx

    imul $scoreline, %dx
    addw %dx, score
    jmp elimline_finished
elimline_tetris:
    addw $scoretetris, score
elimline_finished:
    shr $8, %cx
    movw linesgone, %ax
    mov $speedup, %bl
    div %bl
    mov %al, %dl
    addw %cx, linesgone
    movw linesgone, %ax
    div %bl
    cmp %al, %dl
    je elimline_samespeed
    shrb $1, currentwait
elimline_samespeed:
    call redraw
    ret

#Write the block into the screen array at xposition,yposition
storePiece:
    addw $scorelockin, score
    decw yposition
    mov yposition, %ax
    test %ax, %ax
    jz gameover

    call coords
    xor %eax, %eax
    mov currentcolour, %bl
    mov $screen, %ecx
    storeLoop

    call elimline
    mov currentcolour, %cl
    movw $0, yposition
    movw $2, xposition
    movb $0, sleepcount
    ret

#Draw the current blockType at xposition,yposition (offset from xoffset,
#yoffset).  Will be coloured depending on %cl. Update score
drawShape:
    call coords
    push %cx
    drawLoop
    movb $0x30, vt100_colour+3
    mov $vt100_colour, %ecx
    xor %edx, %edx
    mov $5, %dl
    call sys_write
    mvaddstr $instructionsY+2, $instructionsX, $scorestring, $7
    mov score, %ax
    call numbertostring
    call sys_write
    mvaddstr $instructionsY+3, $instructionsX, $linestring, $7
    mov linesgone, %ax
    call numbertostring
    call sys_write
    ret

#Return the string location in %ecx, length in %edx, requires number in %ax
numbertostring:
    mov $10, %bx
    mov $stringbuffer+5, %ecx
numbertostring_loop:
    dec %ecx
    xor %dx,%dx
    div %bx
    add $0x30, %dx
    movb %dl, (%ecx)
    test %ax,%ax
    jnz numbertostring_loop
    mov %ecx,%ebx
    sub $stringbuffer, %ebx
    xor %edx, %edx
    mov $5, %dl
    sub %ebx, %edx
    ret

#Requires the y coord in %ax, x coord in %bx, val to colour in %cl
drawblock:
    add $xoffset,%bx
    add $yoffset,%ax
    push %ax
    push %bx
    movb %cl, vt100_colour+3
    mov $vt100_colour, %ecx
    xor %edx, %edx
    mov $5, %dl
    call sys_write
    pop %cx
    pop %ax
    shl $1, %cx
    mvaddstr %ax, %cx, $blankstring, $2
    ret

#Redraw the playing area (doesn't update score)
redraw:
    xor %ax, %ax #y
redraw_outer:
        xor %ebx, %ebx #x
redraw_inner:
        push %ebx
        push %ax

        xor %ecx, %ecx
        mov $width, %cl
        imul %eax, %ecx
        mov screen(%ebx, %ecx), %cx

        call drawblock

        pop %ax
        pop %ebx

        inc %bl
        cmpb $width, %bl
        jl redraw_inner
    inc %ax
    cmpb $height, %al
    jl redraw_outer
    ret

gameover:
    mov currentcolour, %cl
    call drawShape
    movw $0x3030, vt100_colour+2
    mov $vt100_colour, %ecx
    xor %edx, %edx
    mov $5, %dl
    call sys_write
    movb $'h',vt100_cursor+5
    mov $vt100_cursor, %ecx
    xor %edx, %edx
    mov $6, %dl
    call sys_write
    cmpb $'q',buffer
    jne gameover_loser
    mvaddstr $instructionsY+4, $0, $exitstring, $13
    jmp gameover_quit
gameover_loser:
    mvaddstr $instructionsY+4, $0, $loserstring, $6
gameover_quit:
    mov $scorestring, %ecx
    xor %edx, %edx
    mov $7, %dl
    call sys_write
    mov score, %ax
    call numbertostring
    call sys_write
    mov $newline, %ecx
    xor %edx, %edx
    mov $1, %dl
    call sys_write
    mov $creditstring, %ecx
    xor %edx, %edx
    mov $41, %dl
    call sys_write
    getterm
    or $10,-48(%ebp) #c_lflag |= (ICANON|ECHO)
    setterm
    sys_exit

#Test the shape for any collision.  If collision, then the zero flag will
#NOT be set
collisionTest:
    call coords
    xor %eax, %eax
    mov $screen, %ebx
    collisionLoop
collisionTest_over:
    pop %dx
    ret

#Writes the number of rotations of blockType into %cl
numberrots:
    xor %ebx, %ebx
    mov blockType, %bl
    test %bl,%bl
    jz numberrots_zeroshape
    add $shapeStarts, %ebx
    mov (%ebx), %cl
    sub -1(%ebx), %cl
    jmp numberrots_done
numberrots_zeroshape:
    mov shapeStarts, %cl
numberrots_done:
    ret

_tetris:
    getterm
    andb $245,-48(%ebp) #c_lflags &= ~(ICANON|ECHO)
    setterm

    sys_times
    mov %eax, lastrand #seed the randomizer

    mov $vt100_clear, %ecx
    xor %edx, %edx
    mov $4, %dl
    call sys_write
    mov $vt100_cursor, %ecx
    xor %edx, %edx
    mov $6, %dl
    call sys_write
    mov $vt100_colour, %ecx
    xor %edx, %edx
    mov $5, %dl
    call sys_write
    mvaddstr $instructionsY, $instructionsX, $namestring, $15
    mvaddstr $instructionsY+1, $instructionsX, $quitstring, $31

    xor %al,%al
    mov $screen,%ebx
tetris_yloop:
    movb $0x31, (%ebx) #red for the playing arena
    movb $0x31, width-1(%ebx)
    xor %ecx, %ecx
    mov $1,%cl
tetris_yloop_inner:
        movb $0x30,(%ebx,%ecx) #init to black
        inc %cl
        cmpb $width-1,%cl
        jl tetris_yloop_inner

    add $width,%ebx
    inc %al
    cmpb $height-1,%al
    jl tetris_yloop

    xor %ebx, %ebx
    mov $width,%bl
    imul $height-1,%ebx
    add $screen,%ebx
    xor %eax,%eax
tetris_xloop:
    movb $0x31,(%eax,%ebx)
    inc %al
    cmpb $width,%al
    jl tetris_xloop

    call redraw

playgame:
    mov $6, %cl #7 shapes
    call rand
    movb %al, blockType
    call numberrots
    dec %cl
    call rand
    movb %al, rotation
    mov $6, %cl
    call rand
    add $0x31, %al
    mov %al, currentcolour
    call collisionTest
    jnz gameover

playgame_keyloop:
    sys_nanosleep $250000
    push %ebp
        xor %eax,%eax
        mov %esp,%ebp
        sub $252,%esp
        bts %eax,-128(%ebp)
        lea -128(%ebp),%eax
    mov %eax,selectargs+4
    sys_newselect
        movl %ebp,%esp
    pop %ebp

    test %eax,%eax
    jnz playgame_checkkey
playgame_keychecked:
    movb currentcolour, %cl
    call drawShape
    incb sleepcount
    movb currentwait, %cl
    cmpb %cl,sleepcount
    jne playgame_keyloop
    movb $0,sleepcount
    mov $0x30, %cl #black to overwrite
    call drawShape
    incw yposition
    call collisionTest
    jz playgame_keychecked
    call storePiece
    jmp playgame
playgame_checkkey:
    mov $0x30, %cl
    call drawShape
    xor %edx, %edx
    mov $1, %dl
    call sys_read
    cmpb $'q',buffer
    je gameover
    cmpb $' ',buffer
    jne playgame_checkarrow
playgame_droploop:
    incw yposition
    call collisionTest
    jz playgame_droploop
    call storePiece
    addw $scoredrop, score
    jmp playgame
playgame_checkarrow:
    cmpb $0x1b,buffer #check for arrow key
    jne playgame_keychecked
    xor %edx, %edx
    mov $2, %dl
    call sys_read
#use a jump table later
    movb buffer+1,%al
    cmpb $'D',%al #Left arrow
    jne playgame_nextkey
    decw xposition
    call collisionTest
    jz playgame_nextkey
    incw xposition
playgame_nextkey:
    cmpb $'C',%al #Right Arrow
    jne playgame_nextkey2
    incw xposition
    call collisionTest
    jz playgame_nextkey2
    decw xposition
playgame_nextkey2:
    cmpb $'B',%al #Down Arrow
    jne playgame_nextkey3
    incw yposition
    call collisionTest
    jz playgame_nextkey3
    decw yposition
playgame_nextkey3:
    cmpb $'A',%al #Up Arrow
    jne playgame_keychecked
    xor %ah, %ah
    mov rotation, %al
    push %ax
    inc %ax
    call numberrots
    divb %cl
    movb %ah, rotation
    call collisionTest
    jz playgame_keychecked
    pop %cx
    mov %cl, rotation
    jmp playgame_keychecked

1.11. Example : replace LF by CR LF

Michael Adam has send me a program that replaces the LF of a file by CR LF to avoid the 'stairways'-effect when printing.

You can download anti_sw.tgz here.



Valid HTML 4.01!
Google