| 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.