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
Laatste aanpassing : 2011-08-28

i. Voorwoord :

Toen ik pas GNU/Linux begon te gebruiken merkte ik al snel dat het vinden van informatie over Assembler programmeren onder Linux niet zo eenvoudig was. Dus kwam ik tot het besluit om een Linux & Assembler webpagina te maken om zo de Linux-asm informatie die ik her en der had verzameld met de Linux-gemeenschap te delen.

Ondertussen is de situatie veranderd :-)  Het vinden van goede informatie over Linux & Assembler is nu veel eenvoudiger dan vroeger. Daarom beschouw ik mijn webpagina ook als zijnde verouderd, zeker omdat ikzelf geen expert ben op het gebied van Linux en/of Assembler.

Als je op zoek bent naar goede en zeer uitgebreidde informatie over Linux & Assembler kan je het beste eens een kijkje nemen op devolgende webpagina's :

Hieronder vind je mijn experimenten met Linux & Assembler x86-32, alsook voorbeeldprogramma's die ik toegestuurd heb gekregen van andere toffe Linux-gebruikers. 
Informatie over mijn experimenten met x86-64 assembler vind je terug op mijn weblog.

1. Assembler & Linux :

Om onder Linux assembler te schrijven zijn er verschillende mogelijkheden. Veel hangt af van welke assembler je gaat gebruiken. De meest voor de hand liggende keuze is GAS (as), de assembler die standaard met elke Linux distributie wordt meegeleverd.

Een nadeel van GAS is dat hij gebruik maakt van de AT&T-sysntax, die sterk verschilt van de Intel-syntax, die de meeste DOS-gebruikers gewoon zijn.

Meer informatie over de AT&T syntax is terug te vinden d.m.v. 'info'. Wanneer je liever een meer Intel-kompatibele syntax gebruikt kan je je programma schrijven d.m.v. Nasm.


1.1. Laat ons beginnen met een voorbeeld voor Nasm :

Afkomstig uit de 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 dit voorbeeld zie je dat om een tekst af te drukken gebruik wordt gemaakt van call puts. Puts is eigenlijk afkomstig uit 'C', en zit eigenlijk standaard aanwezig in de libraries die door linux geladen worden. Er bestaat echter nog een tweede manier om via assembler bepaalde zaken aan te spreken, nl. via INTerrupt 0x80.

Alhoewel er dikwijls beweerd wordt dat Int 0x80 beter niet gebruikt kan worden zijn er zeker voordelen aan verbonden. Meer informatie over het gebruik van Int 0x80 kan je vinden op http://linuxassembly.org


1.2. En dan nu twee voorbeelden voor Gas :

Afkomstig uit assembler.028 (fidonet), dank aan Pieter de Jong voor deze prachtige voorbeelden.

Voorbeeld 1. Door gebruik te maken van 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

Voorbeeld 2. Door gebruik te maken van 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. Macro's toevoegen aan GAS :

Door gebruik te maken van macro's kan het programmeerwerk heel wat vereenvoudigd worden. In bepaalde dokumenten had ik vernomen dat Gas geen mogelijkheden gaf tot het toevoegen van Macro's aan een assembler-programma, en dat hiervoor de toevlucht genomen moest worden tot Gasp.

Maar... na wat eksperimenteren en het lezen van de info-files van GNU-C bleek dat het weldegelijk mogelijk is om aan een assembler-programma macro's toe te voegen, enkel en alleen door gebruik te maken van Gas (as)

Hieronder toon ik een eksperiment van mij met Gas dat de mogelijkheden van het gebruik van Gas & Macro's demonstreerd :

___ test_fopen.s __________________________________________________________

        .include "include.asm"

        .globl main
main:
        _print hallo            # Druk de 'titel' af
        _open bestand mode      # Open het bestand 'dummy.dummy'
        cmp $0,%eax             # Is dit gelukt?
        je file_error           # Nee, druk foutmelding af
        _close %eax             # Ja, sluit eerst het bestand terug
        _print bestaat          # Druk de tekst 'hij bestaat' af
        jmp einde               # Einde van prog.
file_error:
        _print bestaat_niet     # Druk de tekst 'hij bestaat niet' af
einde:
        ret                     # The End ;-)

hallo:
 .string "Test Linux Programma ;-) \n"
bestaat:
 .string "Het bestand dummy.dummy bestaat..."
bestaat_niet:
 .string "Het bestand dummy.dummy bestaat niet..."
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
 #        - als %eax = 0 dan bestaat bestandsnaam niet
 #        - als %eax >< 0 dan kan deze als file-handle gebruikt worden
 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. ncurses gebruiken

Ncurses is een librarie met funkties die gebruikt kunnen worden om tekst-gerichte Linux-programma's te schrijven.

Hieronder volgt een voorbeeldprogramma dat toont hoe ncurses aan te roepen is vanuit pure assembler. Het is geen spektakulair programma, maar toch leuk om te bekijken :-)

___ 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             # Verbergen van de pinkende kursor
                
        call cls                        # scherm wissen/kleuren instellen

        _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
                                        # Kleur 1ste regel
        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 
                                        # Rest van het scherm
        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;
 # }
 #
 # Vermits ikzelf bitter weinig van C ken kan volgende uitleg niet helemaal
 # korrekt zijn. Korrekties zijn dus meer dan welkom :-)
 # Dus, om via ncurses op een welbepaalde plaats op het scherm iets af te 
 # drukken roep je onderstaande macro's aan :
 #      1. _initscr
 #      2. _locate x y  (x,y = scherm-koordinaten)
 #      3. _printw message
 #      4. _refresh
 #      5. _endwin      (end win.... klinkt leuk he ;-)

 .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

# Het gebruiken van kleur via ncurses. (15/07/97)
# - Na _initscr , _start_color aanroepen
# - Met _init_pair een kleurenpaar defini\"eren
# - Met _use_pair het te gebruiken paar selekteren

 .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
 #                    |        |
 #                    |        |
 #                    |        +----> Nummer van te gebruiken kleurpaar
 #                    +-------------> 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. Voorbeeldprogramma's van de 80XXX fidonet area

==============================================================================
   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 van Assembly Programming Journal stond een mooie kleine "fire-demo" geschreven in DOS assembler.

Samen met de informatie die ik via E-mail ontvangen had van 'paranoya' (Thanks!) besloot ik om eens te proberen dit programma om te zetten naar Linux-Assembler.

Hieronder zie je het resultaat. Merk op dat mijn belangrijkste doel was te laten zien dat het mogelijk was. Ik heb mij dus niet beziggehouden met het eventueel optimaliseren van het programma. Dit laat ik als een oefening aan de lezer over ;-)

# 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. Voorbeeld : 387 inline asm

Pascal Fougeray heeft mij onderstaand programma toegestuurd dat toont hoe je in Linux 387 inline assembly kunt gebruiken.
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. Voorbeeld : Muziek door de PC-luidspreker

Dominique Richier heeft mij onderstaand programma toegestuurd dat laat zien hoe je muziek door de luidspreker van de PC kan afspelen. (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. Voorbeeld : shared libraries

Robin Miyagi heeft mij hetvolgende programma toegestuurd dat : "I have some sample code for your site. This code gives a somewhat useless example of writing shared libraries."

Je kan hier pic-asm.tar.gz de bronkode downloaden.


1.10. Tetris Linux asm

Daniel Burr heeft mij de bronkode toegestuurd voor Tetris geschreven in assembler. Meer info en de recentste versie kan je terugvinden op : 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. Voorbeeld : vervang LF door CR LF

Michael Adam heeft mij een programma toegestuurd dat in een bestand de LF vervangt door CR LF om bij het printen het 'stairways'-effect te vermijden.

Je kan hier anti_sw.tgz downloaden.



Valid HTML 4.01!
Google