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