Материал взят со страницы профессора университета Сан-Франциско Allan B. Cruse
Рассмотрим первый пример - demo2
Он складывает два числа
Для сборки этого примера нужно откомпилировать 2 файла .
В первом файле - sprintf.c - находится оригинальная версия
библиотечной функции printf .
Для компиляции нужно выполнить 3 команды :
as sprintf.s -o sprintf.o
as demo2.s -o demo2.o
ld demo2.o sprintf.o -o demo2
//----------------------------------------------------------------
// demo2.s
// programmer: ALLAN CRUSE
// written on: 2005
//----------------------------------------------------------------
.equ sys_exit, 1
.equ sys_write, 4
.equ device_id, 1
.section .data
x: .int 4
y: .int 5
z: .int 0
fmt: .asciz "%d + %d = %d \n"
buf: .space 80
len: .int 0
.section .text
_start: # perform the assignment: z = x + y;
movl x, %eax
addl y, %eax
movl %eax, z
# call library-function: len = sprintf( buf, fmt, x, y, z );
pushl z
pushl y
pushl x
pushl $fmt
pushl $buf
call sprintf
addl $20, %esp
orl %eax, %eax
js fini
movl %eax, len
# make Linux system-call: write( device_id, &buf, len );
movl $sys_write, %eax
movl $device_id, %ebx
movl $buf, %ecx
movl len, %edx
int $0x80
fini: # make Linux system-call: exit( 0 );
movl $sys_exit, %eax
movl $0, %ebx
int $0x80
.global _start
.end
//----------------------------------------------------------------
// sprintf.s
//
// int sprintf( char *dst, char *fmt, ... );
//----------------------------------------------------------------
.section .text
numeral: .ascii "0123456789ABCDEF" # our translation-table
sprintf:
pushl %ebp # preserve frame-pointer
movl %esp, %ebp # setup local stack-frame
subl $4, %esp # create space for radix
pushal # preserve cpu registers
movl 8(%ebp), %edi # dst parameter into EDI
movl 12(%ebp), %esi # fmt parameter into ESI
movl $0, %ecx # initial argument-index
cld # use forward processing
again:
cmpb $0, (%esi) # test: null-terminator?
je finish # yes, we are finished
cmpb $'%', (%esi) # test: format-escape?
je escape # yes, insert numerals
movsb # else copy the character
jmp again # and go back for another
finish: subl 8(%ebp), %edi # compute output length
movl %edi, 8(%ebp) # and save it on stack
jmp return # then exit this function
escape: incl %esi # skip past escape-code
lodsb # and fetch escape-type
cmpb $'d', %al # wanted decimal-format?
movl $10, -4(%ebp) # yes, use 10 as the radix
je do_tx # convert number to string
cmpb $'X', %al # wanted hexadecimal-format?
movl $16, -4(%ebp) # yes, use 16 as the radix
je do_tx # convert number to string
jmp errorx # otherwise return error
do_tx:
movl $numeral, %ebx # point EBX to numeral-list
movl 16(%ebp,%ecx,4), %eax # get next argument in EAX
incl %ecx # and advance argument-index
pushl %ecx # preserve argument-index
xorl %ecx, %ecx # initialize digit-counter
nxdiv:
xorl %edx, %edx # extend dividend to quadword
divl -4(%ebp) # divide by selected radix
push %edx # push remainder onto stack
incl %ecx # and increment digit-count
orl %eax, %eax # test: quotient was zero?
jnz nxdiv # no, another digit needed
nxdgt:
popl %eax # saved remainder into EAX
xlat %cs:(%ebx) # convert number to numeral
stosb # store numeral in buffer
loop nxdgt # go get the next remainder
popl %ecx # recover the argument-index
jmp again # and resume copying format
errorx: movl $-1, 8(%ebp) # store error-indicator
return: popal # restore saved registers
movl 8(%ebp), %eax # copy return-value to EAX
movl %ebp, %esp # discard temporary storage
popl %ebp # restore saved frame-pointer
ret # return control to caller
.global sprintf # make entry-point public
.end # ignore everything beyond
Следующий пример - squares.s
Распечатываются квадраты первых 20 натуральных чисел .
Прошу обратить внимание на то , что при вычислении квадратов
используются только 2 инструкции - add и inc :
//----------------------------------------------------------------
// squares.s
// $ gcc squares.s -o squares
//----------------------------------------------------------------
.equ MAX, 20 # total number of lines
.section .data
arg: .int 1 # stores current number
val: .int 1 # stores current square
head: .string "\n Table of Squares\n\n"
body: .string " %2d %3d \n"
foot: .string "\n" # newline format-string
.section .text
main: # print the table's title
pushl $head # address of format-string
call printf # call the runtime library
addl $4, %esp # discard the one argument
# program loop, to print table's body
again:
# print next line of the table
pushl val # numerical argument #2
pushl arg # numerical argument #1
pushl $body # format-string argument
call printf # call the runtime library
addl $12, %esp # discard the 3 arguments
# compute the next square
movl arg, %eax # fetch the current number
addl %eax, %eax # compute twice its value
incl %eax # add one to that result
addl %eax, val # add this to prior square
# and compute next number
incl arg # increment current number
# check loop exit-condition
cmpl $MAX, arg # does arg exceed maximum?
jle again # no, display another line
# print a blank bottom line
pushl $foot # format-string argument
call printf # call the runtime library
addl $4, %esp # now discard the argument
ret # return control to caller
.global main # makes entry-point public
Следующий пример обрабатывает ввод с клавиатуры числа ,
переводит набранный символ в число и организует по нему
цикл распечатки
//----------------------------------------------------------------
// showdots.s
// assemble-and-link using: gcc showdots.s -o showdots
//----------------------------------------------------------------
# our symbolic constants
.equ sys_write, 4 # service ID-number
.equ sys_read, 3 # service ID-number
.equ stdout_ID, 1 # device ID-number
.equ stdin_ID, 0 # device ID-number
.section .data
msg: .ascii "How many dots do you want to see? "
len: .int . - msg # number of msg characters
inbuf: .space 8 # storage for user's input
maxin: .int . - inbuf # amount of storage space
dot: .byte '.' # ascii-code for a period
count: .int 0 # dot repetitions counter
newln: .byte '\n' # ascii-code for linefeed
.section .text
main: call obtain_input # our 'Input' subroutine
call process_data # our 'Process' subroutine
call print_output # our 'Output' subroutine
ret # return to command shell
.globl main # make entry-point visible
obtain_input:
# print message asking for user's input
movl $sys_write, %eax # specify system service
movl $stdout_ID, %ebx # specify desired device
movl $msg, %ecx # output-string's address
movl len, %edx # length of output-string
int $0x80 # enter the Linux kernel
# input the user's response
movl $sys_read, %eax # specify system service
movl $stdin_ID, %ebx # specify desired device
movl $inbuf, %ecx # address of input buffer
movl maxin, %edx # maximum bytes to accept
int $0x80 # enter the Linux kernel
movl %eax, maxin # actual length of input
# TODO: we ought to terminate with an error-message if our
# user types in more characters than can fit in our buffer
ret
process_data:
# We will loop though the characters in 'inbuf' until we
# encounter an ascii-code that is NOT a decimal numeral,
# or until we come to the last byte in our input-buffer.
# We shall begin with a 'count' value equal to zero, but
# each time we encounter another valid numeral (i.e., an
# ascii-code from '0' through '9') we will convert it to
# its corresponding integer value, and that integer will
# be added to ten-times-count. (We have not yet studied
# the Pentium's multiplication instruction, so right now
# we can get the effect of multiplying by ten by using a
# series of add-instructions, since 10*x = 2*(x + 4*x).
movl $0, count # initial count is zero
cmpl $0, maxin # test: error in input?
jle fini # yes, no further work
xorl %esi, %esi # initial index is zero
again: cmpl %esi, maxin # input exhausted yet?
jle fini # yes, we are finished
movb inbuf(%esi), %al # fetch next character
incl %esi # advance source index
cmpb $'0', %al # char preceeds '0'?
jb fini # yes, invalid numeral
cmpb $'9', %al # char follows '9'?
ja fini # yes, invalid numeral
subb $'0', %al # convert to a number
movzx %al, %eax # extended to 32-bits
movl count, %edx # prior 'count' value
addl count, %edx # is doubled in EDX
addl %edx, %edx # and doubled again
addl count, %edx # now five times count
addl %edx, %edx # is added to itself
addl %eax, %edx # add the new number
movl %edx, count # store this new total
jmp again # go back for another
fini: ret # return to 'main'
print_output:
# check: are any more dots to be printed?
cmp $0, count # is 'count' positive?
jle done # no, printing is done
# print one dot character
movl $sys_write, %eax # specify system service
movl $stdout_ID, %ebx # specify desired device
movl $dot, %ecx # address of output buffer
movl $1, %edx # number of buffer bytes
int $0x80 # enter the Linux kernel
# adjust the loop counter
decl count # decrement our counter
jmp print_output # then go back for more
done:
# output the ascii 'newline' code
movl $sys_write, %eax # specify system service
movl $stdout_ID, %ebx # specify desired device
movl $newln, %ecx # address of output buffer
movl $1, %edx # number of buffer bytes
int $0x80 # enter the Linux kernel
ret # return to 'main' function
.end # assembler can stop here
Следующая программа показывает использование стека
Она предлагает вам набрать строку и потом распечатывает ее
в обратном порядке
//----------------------------------------------------------------
// reverse.s
// assemble using: $ as reverse.s -o reverse.o
// and link using: $ ld reverse.o -o reverse
//
// programmer: ALLAN CRUSE
// written on: 03 FEB 2005
//----------------------------------------------------------------
# manifest constants
.equ stdin_ID, 0 # input-device (keyboard)
.equ stdout_ID, 1 # output-device (screen)
.equ sys_exit, 1 # ID-number for 'exit'
.equ sys_read, 3 # ID-number for 'read'
.equ sys_write, 4 # ID-number for 'write'
.section .data
msg1: .ascii "\nPlease type a sentence on the line below: \n"
len1: .int . - msg1
msg2: .ascii "\nHere is what you typed in backward order: \n"
len2: .int . - msg2
msg3: .ascii "\n"
len3: .int . - msg3
inbuf: .space 80
maxin: .int . - inbuf
.section .text
_start:
# request user input
movl $sys_write, %eax # service ID-number
movl $stdout_ID, %ebx # device ID-number
leal msg1, %ecx # message address
movl len1, %edx # message length
int $0x80 # enter the kernel
# receive user input
movl $sys_read, %eax # service ID-number
movl $stdin_ID, %ebx # device ID-number
leal inbuf, %ecx # buffer address
movl maxin, %edx # buffer length
int $0x80 # enter the kernel
movl %eax, maxin # save input count
# push characters onto stack
leal inbuf, %esi # point ESI to buffer
movl maxin, %ecx # setup ECX for count
decl %ecx # but omit final byte
nxch1: movb (%esi), %al # load next character
pushl %eax # and push onto stack
incl %esi # advance array-pointer
loop nxch1 # then back for another
# pop characters from stack
leal inbuf, %edi # point EDI to buffer
movl maxin, %ecx # setup ECX for count
decl %ecx # but onlt final byte
nxch2: popl %eax # pop next character
movb %al, (%edi) # and store in buffer
incl %edi # advance array-pointer
loop nxch2 # then back for another
# announce program output
movl $sys_write, %eax # service ID-number
movl $stdout_ID, %ebx # device ID-number
leal msg2, %ecx # message address
movl len2, %edx # message length
int $0x80 # enter the kernel
# display program output
movl $sys_write, %eax # service ID-number
movl $stdout_ID, %ebx # device ID-number
leal inbuf, %ecx # message address
movl maxin, %edx # message length
int $0x80 # enter the kernel
# output one blank line
movl $sys_write, %eax # service ID-number
movl $stdout_ID, %ebx # device ID-number
leal msg3, %ecx # message address
movl len3, %edx # message length
int $0x80 # enter the kernel
# terminate this program
movl $1, %eax # service ID-number
movl $0, %ebx # setup exit-code
int $0x80 # enter the kernel
.globl _start # visible entry-point
.end # no more to assemble
Следующая программа в качестве параметра берет аргумент
командной строки , и если это число , выводит все его делители
//----------------------------------------------------------------
// factors.s
// gcc factors.s -o factors
//----------------------------------------------------------------
.section .data
n: .int 0
i: .int 0
ten: .int 10
errmsg: .string "A command-line argument is required"
report: .string "\nThe factors of %d are shown below: \n"
outnum: .string " %d "
nxline: .string "\n"
.section .text
main:
# check for a command-line argument
cmpl $1, 4(%esp) # is argc > 1 ?
jg argok # yes, continue
# if none, print an error-message and exit
pushl $errmsg
call printf
addl $4, %esp
jmp finis
# convert argument from a digit-string to a number
argok:
movl 8(%esp), %ebx # get argv
movl 4(%ebx), %esi # get argv[1]
movl $0, n # initial n
# conversion: add next digit-value to ten times n
nxdgt:
movb (%esi), %al # get next digit
incl %esi # advance pointer
cmpb $'0', %al # below '0'?
jb inval # yes, not a digit
cmpb $'9', %al # above '9'?
ja inval # yes, not a digit
andl $0xF, %eax # convert ascii to number
xchgl %eax, n # xchange with prior n
mull ten # multiply n by ten
addl %eax, n # add to digit-value
jmp nxdgt # check for another digit
inval:
# print explanatory message, showing user's value for n
pushl n
pushl $report
call printf
addl $8, %esp
# while-loop: to check for all possible factors of n
movl $1, i # initialize i
while:
movl i, %eax # get current i
cmpl %eax, n # check: i <= n ?
jl finis # no, then exit
# see if integer i divides n without any remainder
movl n, %eax
xorl %edx, %edx
divl i
orl %edx, %edx
jnz notf
# show this factor of n
pushl i
pushl $outnum
call printf
addl $8, %esp
notf:
# increment the "trial" factor
incl i
jmp while
finis: # print a newline and exit
pushl $nxline
call printf
addl $4, %esp
ret
.globl main
.end
Для компиляции следующего примера нужны 2 исходника
as softmulb.s -o softmulb.o
as testmulb.s -o testmulb.o
gcc testmulb.o softmulb.o -o testmulb
Пользователь набирает 2 числа и получает их произведение
//----------------------------------------------------------------
// testmulb.s
//
// This interactive program tests our 'softmulb.s' function.
//
//----------------------------------------------------------------
.section .data
legend: .asciz "\nThis program computes x times y \n\n"
format: .asciz "%d"
report: .asciz "OK, %d times %d equals %d \n\n"
ask_x: .asciz "Please type your x value: "
ask_y: .asciz "Please type your y value: "
x: .int 0
y: .int 0
z: .int 0
.section .text
main: # show the program's legend
pushl $legend # push function argument
call printf # call library function
addl $4, %esp # discard the argument
# show prompt #1 and get reply
pushl $ask_x # push function argument
call printf # call library function
addl $4, %esp # discard the argument
pushl $x # push argument #2
pushl $format # push argument #1
call scanf # call library function
addl $8, %esp # discard two arguments
# show prompt #2 and get reply
pushl $ask_y # push function argument
call printf # call library function
addl $4, %esp # discard the argument
pushl $y # push argument #2
pushl $format # push argument #1
call scanf # call library function
addl $8, %esp # discard two arguments
# now compute the product
movb x, %al # copy multiplicand to AL
movb y, %bl # copy multiplier to BL
call softmulb # software multiplication
movw %ax, z # copy product to storage
# report the result, then exit
pushl z # push argument #4
pushl y # push argument #3
pushl x # push argument #2
pushl $report # push argument #1
call printf # call library function
addl $16, %esp # discard all arguments
ret # return to the caller
.global main # entry-point is visible
.end # no further statements
//----------------------------------------------------------------
// softmulb.s
//
// This file uses shift/rotate instructions to emulate with
// software the Pentium's hardware multiplication operation
// for the case when the multiplier is held in register BL.
//----------------------------------------------------------------
.section .text
softmulb:
#
# EXPECTS: AL = multiplicand
# BL = multiplier
# RETURNS: AX = product
#
pushl %ecx # preserve register's value
movl $9, %ecx # setup for 8-bit multiplier
subb %ah, %ah # clear high result register
nxbit8: rcrw $1, %ax # next multiplier bit to CF
jnc noadd8 # skip addition if bit == 0
addb %bl, %ah # else add the multiplicand
noadd8: loop nxbit8 # and shift the result
subb %ah, %cl # set CF if 8-bits exceeded
popl %ecx # restore register's value
ret
.globl softmulb
Следующий пример - си-inline-версия предыдущего
Для его компиляции наберите команду :
g++ multest.cpp softmulb.o -o multest
Она распечатывает произведения всех возможных вариантов чисел ,
каждое из которых состоит не более чем из 2-х байтов .
//----------------------------------------------------------------
// multest.cpp
// compile using: $ g++ multest.cpp softmulb.o -o multest
//----------------------------------------------------------------
#include // needed for printf()
extern void softmulb( void ); // function symbol
unsigned int a, b, c, d; // global variables
int main( void )
{
printf( "\n--a-- --b-- hardware software \n" );
// iterate over all possible pairs of 8-bit values
int errors = 0;
for (a = 0; a < 256; a++)
{
for (b = 0; b < 256; b++)
{
printf( " a=%02X b=%02X ", a, b );
asm(" movb a, %al ");
asm(" movb b, %bl ");
asm(" mulb %bl ");
asm(" movw %ax, d ");
asm(" movb a, %al ");
asm(" movb b, %bl ");
asm(" call softmulb ");
asm(" movw %ax, c ");
printf( " c=%04X d=%04X ", c, d );
if ( c != d ) ++errors;
if ( c == d ) printf( " ok \n" );
else printf( "<--- error #%d \n", errors );
}
}
printf( "\n\tNumber of errors = %d \n\n", errors );
}
Следующая программа распечатывает степени числа 2 :
//----------------------------------------------------------------
// powers.s
// $ g++ powers.s -o powers
//----------------------------------------------------------------
.equ MAX, 15 # total number of lines
.section .data
expon: .int 0 # stores current exponent
power: .int 1 # stores current power
head: .string "\n Nonnegative Powers of Two \n\n"
body: .string " %2d %6d \n"
foot: .string "\n" # newline format-string
.section .text
main: # print the table's title
pushl $head # address of format-string
call printf # call the runtime library
addl $4, %esp # discard the one argument
# program loop, to print table's body
again:
# print next line of the table
pushl power # numerical argument #2
pushl expon # numerical argument #1
pushl $body # format-string argument
call printf # call the runtime library
addl $12, %esp # discard the 3 arguments
# prepare the next exponent and power
movl power, %eax # get the previous power
addl %eax, %eax # now double its value
movl %eax, power # save this as new power
incl expon # add one to the exponent
# but check loop-exit condition
cmpl $MAX, expon # exponent exceeds maximum?
jnle finis # yes, no more lines to do
jmp again # else display another line
finis:
# print a blank bottom line
pushl $foot # format-string argument
call printf # call the runtime library
addl $4, %esp # now discard the argument
ret # return control to caller
.global main # makes entry-point public
В следующей программе пользователь набирает строку ,
и программа сравнивает ее с шаблоном .
//----------------------------------------------------------------
// password.s
// gcc password.s -o password
//----------------------------------------------------------------
# manifest constants
.equ MAXIN, 20
.equ FALSE, 0
.equ TRUE, ~0
.section .data
target: .ascii "123123\n"
pwsize: .int . - target
prompt: .asciz "\nPlease type in the secret password: "
success:.string "Correct password -- access granted.\n"
failure:.string "Invalid password -- access refused.\n"
buffer: .space MAXIN
valid: .byte FALSE
.section .text
#-----------------------------------------------------------------
main: call obtain_password
call verify_validity
call notify_the_user
call grant_or_refuse
ret
#-----------------------------------------------------------------
obtain_password:
#
# This function requests the user to type the secret password,
# then stores the user's response in a character-array buffer.
#
call request_input
call receive_reply
ret
#-----------------------------------------------------------------
verify_validity:
#
# This procedure compares the array of input-characters typed
# by the user with the 'target' array which holds the correct
# password. The 'valid' flag does not remain TRUE unless all
# of their ascii codes agree.
#
xorl %esi, %esi # initial array index
movl $TRUE, valid # starting assumption
next: movb buffer(%esi), %al # get next input-char
cmpb target(%esi), %al # it matches target?
je incr # yes, advance index
movl $FALSE, valid # else flag adjusted
incr: incl %esi # advance array index
cmpl %esi, pwsize # end-of-array yet?
jg next # <-- bug fixed # no, check another
ret
#-----------------------------------------------------------------
#-----------------------------------------------------------------
request_input:
#
# This function prints an initial message which asks the user
# to type in the secret password.
#
push $prompt # argument to function
call printf # call runtime library
addl $4, %esp # discard the argument
ret
#-----------------------------------------------------------------
receive_reply:
#
# This function reads in the characters that are typed by the
# user and returns (when the user hits the key) with
# the ascii character-codes stored in an array named 'buffer'.
#
push stdin # argument #3
push $MAXIN # argument #2
push $buffer # argument #1
call fgets # call runtime library
addl $12, %esp # discard arguments
ret
#-----------------------------------------------------------------
notify_the_user:
#
# This procedure prints a diagonistic message, based on the
# value (TRUE or FALSE) it finds stored in the 'valid' flag.
#
movl $success, %eax # setup message-address
cmpl $TRUE, valid # is it the proper one?
je msgok # yes, proceed to print
movl $failure, %eax # no, use other message
msgok: push %eax # function's argument
call puts # call runtime library
addl $4, %esp # discard the argument
ret
#-----------------------------------------------------------------
grant_or_refuse:
#
# This procedure checks the value of the 'valid' flag and
# leaves this function in case 'valid' is TRUE; otherwise
# it continues executing here in an infinite loop so that
# the user is thus prevented from making further progress.
#
cmpl $TRUE, valid # was password valid?
je return # yes, then ok to exit
freeze: jmp freeze # else continue here
return: ret
#-----------------------------------------------------------------
.globl main
.end
Следующий пример распечатывает имя текущего пользователя
//----------------------------------------------------------------
// mywhoami.s
//
// gcc mywhoami.s -o mywhoami
//
// This program illustrates the use of string-manipulation
// instructions: (1) to search for an environment variable
// and (2) to copy its value into a local buffer area from
// which it can then be displayed. The algorithm which is
// used here relies on the fact that Linux systems provide
// for three arguments to the function 'main()':
//
// int main( int argc, char *argv[], char *envp[] );
//
// Here the third argument 'envp' is a pointer to an array
// of character pointers (known as the 'environment list')
// in which each of the strings has the form 'name=value'.
// Among these we search here for the one in which 'USER='
// appears: its value gives the current effective user id.
//----------------------------------------------------------------
.section .data
target: .ascii "USER=" # variable to search for
tgtlen: .int . - target # length for target name
user: .zero 80 # for storing a username
size: .int 0 # for length of username
fmt: .asciz "%s\n" # for 'printf()' format
.section .text
main:
# setup local frame-pointer (to access 'envp' argument)
pushl %ebp # preserve caller's frame
movl %esp, %ebp # setup local stack frame
# get 'envp' and search the list for the 'USER=' string
movl 16(%ebp), %ebx # EBX points to the array
xorl %edx, %edx # use EDX for array-index
cld # do 'forward' processing
nxvar:
# check for end-of-array (i.e., a null string)
movl (%ebx, %edx, 4), %edi # point EDI to next string
cmpb $0, (%edi) # check: is a NULL string?
je finis # yes, search unsuccessful
# compare the string's variable-name to our target name
movl $target, %esi # point ESI to our target
movl tgtlen, %ecx # setup length for target
rep cmpsb # check: do strings agree?
je found # yes, search succeessful!
# if they don't match, increment array-index and try again
inc %edx # else advance array-index
jmp nxvar # and examine new variable
found: # determine the length of the environment variable's value
movl %edi, %esi # save start-address in ESI
xorb %al, %al # setup EAX with null value
movl $0xC0000000, %ecx # address for top of memory
subl %edi, %ecx # less address to scan from
repne scasb # scan string for null byte
movl %edi, size # ok, store the end address
subl %esi, size # minus the begin address
# now copy the variable's value to our local buffer
movl $user, %edi # point EDI to local buffer
movl size, %ecx # number of bytes to copy
rep movsb # perform the string-copy
# print the current effective user id
pushl $user # printf argument #2
pushl $fmt # printf argument #1
call printf # call to runtime library
addl $8, %esp # discard the arguments
# restore caller stack-frame and exit
finis:
mov %ebp, %esp # insure stack is balanced
popl %ebp # restore caller's frame
ret # and return from 'main()'
.globl main # make entry-point public
Следующая программа распечатывает свой собственный стек
//-------------------------------------------------------------------
// seestack.cpp
//
// g++ seestack.cpp -o seestack
//
//-------------------------------------------------------------------
#include // for printf()
#define KERNEL_BASE_ADDRESS 0xC0000000
unsigned long tos, ebp, len;
unsigned char *where = (unsigned char *)KERNEL_BASE_ADDRESS;
unsigned char ch;
int i, j;
int main( int argc, char **argv, char **envp )
{
// insert statement that copies %esp-value to a variable
asm(" movl %esp, tos ");
asm(" movl %ebp, ebp ");
// calculate the size of the active stack area (in bytes)
len = KERNEL_BASE_ADDRESS - tos;
// loop to display the contents of the active stack area
for (i = 0; i < len; i += 16)
{
where -= 16;
printf( "\n%08X: ", where );
for (j = 0; j < 16; j++) printf( "%02X ", where[j] );
for (j = 0; j < 16; j++)
{
ch = where[ j ];
if (( ch < 0x20 )||( ch > 0x7E )) ch = '.';
printf( "%c", ch );
}
}
printf( "\n" );
printf( "\n ebp=%08X ", ebp );
printf( "\n esp=%08X ", tos );
printf( "\n&argc=%08X argc=%08X ", &argc, argc );
printf( "\n&argv=%08X argv=%08X ", &argv, argv );
printf( "\n&envp=%08X envp=%08X ", &envp, envp );
printf( "\n\n" );
}
Следующая программа по набранной дате определяет ее
день недели
//----------------------------------------------------------------
// xmas.s
//
// gcc xmas.s -o xmas
//
//----------------------------------------------------------------
.section .data
param: .int 2005 # command-line parameter
radix: .int 10 # base of decimal system
seven: .int 7 # number of days-per-week
four: .int 4 # frequency of leap-years
years: .int 0 # counts years since 1900
leaps: .int 0 # count of the leap-years
days: .int 0 # elapsed days since 1900
theday: .int 0 # weekday number (0=Tues)
daylst: .ascii "TueWedThuFriSatSunMon" # names of days in a week
report: .asciz "xxx, Dec 25 \n" # message text to display
errmsg: .asciz "Year cannot be before 1900\n" # error-message
.section .text
main: call obtain_input # get the designated year
call process_data # calculate Christmas day
call print_output # display results to user
ret # return control to Linux
print_output:
# copy day's name into report-string
xorl %edi, %edi # array-index for dst
imul $3, theday, %esi # array-index for src
movl $3, %ecx # chars to be copied
nxmv: movb daylst(%esi), %al # fetch source char
movb %al, report(%edi) # store dest'n char
incl %esi # advance src index
incl %edi # advance dst index
loop nxmv # copy rest of chars
# print report on day when Christmas occurs
pushl $report # push message address
call printf # call runtime library
addl $4, %esp # discard the argument
ret
process_data:
# compute the number of years since 1900
movl param, %eax # get the parameter value
subl $1900, %eax # subtract the value 1900
movl %eax, years # store as count of years
# compute the number of leap-years since 1900
movl years, %eax # get the number of years
xorl %edx, %edx # extend dividend to long
divl four # perform unsigned divide
movl %eax, leaps # quotient counts leapyrs
# compute the total number of days since 1900
imul $365, years, %eax # years * days-per-year
addl leaps, %eax # plus the "extra" days
movl %eax, days # gives days since 1900
# divide number of days by seven
movl days, %eax # get the number of days
xorl %edx, %edx # extended as a dividend
divl seven # divide by days-in-week
movl %edx, theday # remainder is day-of-week
ret # return to caller
obtain_input:
movl 12(%ebp), %eax # get number of arguments
cmpl $1, %eax # check: does argc == 1?
je argx # yes, use a default year
movl 16(%ebp), %ebx # get argv[] base-address
movl $1, %edx # setup 1 as array-index
movl (%ebx,%edx,4), %esi # get pointer to argument
call ascii_to_int # convert ascii to number
movl %eax, param # save the argument-value
cmpl $1900, param # check: 1900 > param ?
jge argx # no, parameter accepted
pushl $errmsg # error-message address
call printf # call runtime library
addl $4, %esp # discard the argument
pushl $1 # exit-function argument
call exit # call runtime library
argx: ret # return to the caller
ascii_to_int:
#
# This procedure converts a string of decimal digits, located at
# the memory-address found in register %esi, into its numerical
# value, and returns that value in register %eax. Upon return,
# register %esi points to the first non-digit that occurs in the
# string. The contents of other general registers is unchanged.
#
pushl %ecx # preserve work registers
pushl %edx
xorl %eax, %eax # initialize accumulator
nxdgt:
movb (%esi), %cl # fetch next ascii-code
cmpb $'0', %cl # is it below '0'?
jb cvtxx # yes, not a digit
cmpb $'9', %cl # is it above '9'?
ja cvtxx # yes, not a digit
andl $0xF, %ecx # convert digit to integer
mull radix # ten times previous total
addl %ecx, %eax # plus the newest integer
incl %esi # advance source pointer
jmp nxdgt # check for more digits
cvtxx:
popl %edx # restore saved registers
popl %ecx
ret
.globl main # make entry-point public
.end
Следующая программа генерит новый текстовой файл
//----------------------------------------------------------------
// out2file.s
//
// assemble with: $ as out2file.s -o out2file.o
// and link with: $ ld out2file.o -o out2file
//
//----------------------------------------------------------------
# manifest constants
.equ STDERR, 2
.equ sys_EXIT, 1
.equ sys_WRITE, 4
.equ sys_OPEN, 5
.equ sys_CLOSE, 6
.equ O_CREAT, 0100
.equ O_TRUNC, 01000
.equ O_WRONLY, 01
.equ S_ACCESS, 00600
.section .text
#-----------------------------------------------------------------
_start: call create_file
call output_data
call commit_file
call notify_user
call termination
#-----------------------------------------------------------------
.section .data
#-----------------------------------------------------------------
flags: .int O_CREAT | O_WRONLY | O_TRUNC
access: .int S_ACCESS # define file-permissions
handle: .int -1 # used as file identifier
filename: .asciz "mycookie.dat" # name for file to create
#-----------------------------------------------------------------
fileinfo: .ascii "Computer Science 210\n"
.ascii "Spring 2005\n"
.ascii "University of San Francisco\n"
.equ infosize, . - fileinfo
#-----------------------------------------------------------------
.section .text
#-----------------------------------------------------------------
create_file: movl $sys_OPEN, %eax # service-number
movl $filename, %ebx # address of name
movl flags, %ecx # mode-settings
movl access, %edx # file-permissions
int $0x80 # enter the kernel
movl %eax, handle # save return-code
or %eax, %eax # check: success?
jns creatx # yes, finished
pushl $size05 # message-length
pushl $fail05 # message-address
pushl $exitmsg # function-address
creatx: ret # transfer control
#-----------------------------------------------------------------
fail05: .ascii "Could not create file.\n\n"
.equ size05, . - fail05
#-----------------------------------------------------------------
output_data: movl $sys_WRITE, %eax # service-number
movl handle, %ebx # specify the file
movl $fileinfo, %ecx # buffer's address
movl $infosize, %edx # buffer's length
int $0x80 # enter the kernel
or %eax, %eax # check: success?
jns writex # yes, finished
pushl $size04 # message-length
pushl $fail04 # message-address
pushl $exitmsg # function-address
writex: ret # transfer control
#-----------------------------------------------------------------
fail04: .ascii "Could not write to file.\n\n"
.equ size04, . - fail04
#-----------------------------------------------------------------
commit_file: movl $sys_CLOSE, %eax # service-number
movl handle, %ebx # specify the file
int $0x80 # enter the kernel
or %eax, %eax # check: success?
jns closex # yes, finished
pushl $size06 # message-length
pushl $fail06 # message-address
pushl $exitmsg # function-address
closex: ret # transfer control
#-----------------------------------------------------------------
fail06: .ascii "Could not close file.\n\n"
.equ size06, . - fail06
#-----------------------------------------------------------------
#-----------------------------------------------------------------
notify_user: movl $sys_WRITE, %eax # service-number
movl $STDERR, %ebx # device-handle
movl $message, %ecx # buffer's address
movl $msgsize, %edx # buffer's length
int $0x80 # enter the kernel
ret # transfer control
#-----------------------------------------------------------------
message: .ascii "\nNew file successfully created\n\n"
.equ msgsize, . - message
#-----------------------------------------------------------------
#
# This is our common error-handling routine, which only executes
# in case one of our kernel system-calls returns a failure-code.
# Upon entry, this routine assumes that the address of a message
# (and the length of that message) will be found atop the stack.
#
#-----------------------------------------------------------------
exitmsg: # first show the name of this program (so a user
# will know the source of the message to follow)
movl $sys_WRITE, %eax # service-number
movl $STDERR, %ebx # device-handle
movl $exitfmt, %ecx # string's address
movl $fmtsize, %edx # string's length
int $0x80 # enter the kernel
# then show the diagnostic error-message (whose
# address and length are popped from the stack)
movl $sys_WRITE, %eax # service-number
movl $STDERR, %ebx # device-handle
popl %ecx # string's address
popl %edx # string's length
int $0x80 # enter the kernel
# finally, fall through to the program-exit code
termination:
movl $sys_EXIT, %eax # service-number
xorl %ebx, %ebx # return-code
int $0x80 # enter the kernel
#-----------------------------------------------------------------
exitfmt: .ascii "\nout2file: "
.equ fmtsize, . - exitfmt
#-----------------------------------------------------------------
.globl _start
.end
Следующая программа читает и выводит на экран текстовой файл
//----------------------------------------------------------------
// readdemo.s
//
// assemble using: $ as readdemo.s -o readdemo.o
// and link using: $ ld readdemo.o -o readdemo
//----------------------------------------------------------------
# manifest constants
.equ STDOUT, 1
.equ SEEK_SET, 0
.equ SEEK_END, 2
.equ O_RDONLY, 0
.equ sys_EXIT, 1
.equ sys_READ, 3
.equ sys_WRITE, 4
.equ sys_OPEN, 5
.equ sys_LSEEK, 19
.equ sys_BRK, 45
.data
filnam: .asciz "mycookie.dat"
handle: .int -1
filesz: .int -1
bufadr: .int -1
.text
#-----------------------------------------------------------------
_start: call open_file_ro
call get_filesize
call allocate_ram
call read_in_file
call display_info
call exit_to_bash
#-----------------------------------------------------------------
open_file_ro:
movl $sys_OPEN, %eax
movl $filnam, %ebx
movl $O_RDONLY, %ecx
movl $0, %edx
int $0x80
movl %eax, handle
orl %eax, %eax
jns openx
pushl $mlen5
pushl $fail5
pushl $exitmsg
openx: ret
#-----------------------------------------------------------------
fail5: .ascii "Could not open the file \n"
.equ mlen5, . - fail5
#-----------------------------------------------------------------
get_filesize:
movl $sys_LSEEK, %eax
movl handle, %ebx
xorl %ecx, %ecx
movl $SEEK_END, %edx
int $0x80
movl %eax, filesz
orl %eax, %eax
js skerr
movl $sys_LSEEK, %eax
movl handle, %ebx
xorl %ecx, %ecx
movl $SEEK_SET, %edx
int $0x80
orl %eax, %eax
jns seekx
skerr: pushl $mlen19
pushl $fail19
pushl $exitmsg
seekx: ret
#-----------------------------------------------------------------
fail19: .ascii "Could not do lseek. \n"
.equ mlen19, . - fail19
#-----------------------------------------------------------------
allocate_ram:
movl $sys_BRK, %eax
xorl %ebx, %ebx # we want current 'brk'
int $0x80
movl %eax, bufadr
movl $sys_BRK, %eax
movl bufadr, %ebx
addl filesz, %ebx # here we set new 'brk'
int $0x80
cmp $-1, %eax
jne alocx
pushl $mlen45
pushl $fail45
pushl $exitmsg
alocx: ret
#-----------------------------------------------------------------
fail45: .ascii "Could not allocate memory. \n"
.equ mlen45, . - fail45
#-----------------------------------------------------------------
read_in_file:
movl $sys_READ, %eax
movl handle, %ebx
movl bufadr, %ecx
movl filesz, %edx
int $0x80
orl %eax, %eax
jns readx
pushl $mlen3
pushl $fail3
pushl $exitmsg
readx: ret
#-----------------------------------------------------------------
fail3: .ascii "Could not read from file. \n"
.equ mlen3, . - fail3
#-----------------------------------------------------------------
display_info:
movl $sys_WRITE, %eax
movl $STDOUT, %ebx
movl bufadr, %ecx
movl filesz, %edx
int $0x80
ret
#-----------------------------------------------------------------
exitmsg:
movl $sys_WRITE, %eax
movl $STDOUT, %ebx
movl $idmsg, %ecx
movl $idlen, %edx
int $0x80
movl $sys_WRITE, %eax
movl $STDOUT, %ebx
popl %ecx
popl %edx
int $0x80
exit_to_bash:
movl $sys_EXIT, %eax
movl $0, %ebx
int $0x80
#-----------------------------------------------------------------
idmsg: .ascii "readdemo: "
.equ idlen, . - idmsg
#-----------------------------------------------------------------
.globl _start
.end
Следующий пример показывает использование 'fork' system-call
//----------------------------------------------------------------
// tryfork.s
//
// This program directly invokes the 'fork' system-call in
// order to create a 'child' process; the 'parent' process
// then issues the 'waitpid' system-call, causing it to be
// 'blocked' until the child-process has terminated. Both
// processes display a small number of their topmost stack
// elements, for comparison purposes.
//
// assemble using: $ as tryfork.s -o tryfork.o
// and link using: $ ld tryfork.o -o tryfork
//----------------------------------------------------------------
# manifest constants
.equ sys_EXIT, 1
.equ sys_FORK, 2
.equ sys_WRITE, 4
.equ sys_WAITPID, 7
.equ STDOUT, 2
.section .data
pid: .int -1 # holds the process-ID
hexlst: .ascii "0123456789ABCDEF" # list of hex numerals
status: .int 0 # status for 'waitpid'
pfini: .ascii "\nparent is finished.\n\n"
.equ pfsz, . - pfini
cfini: .ascii "\nchild is finished.\n\n"
.equ cfsz, . - pfini
.section .text
_start:
# issue the 'fork' system-call
movl $sys_FORK, %eax # service ID-number
int $0x80 # enter the kernel
movl %eax, pid # save return-value
# branch to different threads
orl %eax, %eax # was zero returned?
jnz parent # no, it's the parent
jmp child # else it's the child
terminate:
# issue the 'exit' system-call
movl $sys_EXIT, %eax # service ID-number
movl $0, %ebx # process exit-code
int $0x80 # enter the kernel
#-----------------------------------------------------------------
parent:
# go to sleep until the child-process has terminated
movl $sys_WAITPID, %eax # service ID-number
movl pid, %ebx # process to wait for
movl $status, %ecx # address for status
movl $0, %edx # service options
int $0x80 # enter the kernel
# display the topmost stack-elements
call show_stack # display stack items
# print a termination notification
movl $sys_WRITE, %eax # service ID-number
movl $STDOUT, %ebx # device-handle
movl $pfini, %ecx # message address
movl $pfsz, %edx # message length
int $0x80 # enter the kernel
jmp terminate # goto exit-routine
#-----------------------------------------------------------------
child:
# display the topmost stack-elements
call show_stack # display stack items
# print a termination notification
movl $sys_WRITE, %eax # service ID-number
movl $STDOUT, %ebx # device-handle
movl $cfini, %ecx # message address
movl $cfsz, %edx # message length
int $0x80 # enter the kernel
jmp terminate # goto exit-routine
#-----------------------------------------------------------------
show_stack:
#
# This procedure is called by both of the processes, to display
# a few of the topmost elements on their stacks. (It allocates
# some storage-space (128 bytes) on their respective stacks for
# its temporary use as a buffer-area, but releases that storage
# before exiting back to the calling procedure.)
#
push %ebp # save frame-pointer
movl %esp, %ebp # setup our own frame
subl $128, %esp # allocate buffer area
pushal # preserve registers
# prepare registers for string-processing
leal -128(%ebp), %edi # point EDI to buffer-area
movl %ebp, %esi # point ESI to stack-frame
cld # use forward processing
# write stack-frame's address to buffer-area
movl %esi, %eax # copy address into EAX
call eaxout # write EAX as hex-value
movb $':', %al # then append a colon
stosb # following the address
# write topmost stack elements to buffer-area
movl $7, %ecx # number of stack-items
nxdwd:
movb $' ', %al # prepend a blank space
stosb # before next hex-value
lodsl # fetch next stack-item
call eaxout # write item as hex-value
loop nxdwd # process remaining items
# display the sequence of stack elements
movl $sys_WRITE, %eax # service ID-number
movl $STDOUT, %ebx # device-handle
leal -128(%ebp), %ecx # message address
movl %edi, %edx # end-of-message
subl %ecx, %edx # minus its start
int $0x80 # enter the kernel
popal # restore registers
movl %ebp, %esp # discard buffer area
pop %ebp # recover frame-pointer
ret # return to caller
#-----------------------------------------------------------------
eaxout:
#
# This helper-function converts the value found in register EAX
# to its representation as a hexadecimal digit-string at (EDI),
# and advances the address in EDI to the next buffer position.
#
pushl %eax # preserve work registers
pushl %ebx
pushl %ecx
pushl %edx
movl %eax, %edx # copy EAX value to EDX
movl $hexlst, %ebx # point EBX to digit-list
movl $8, %ecx # the number of nybbles
nxnyb: roll $4, %edx # next nybble into DL
movb %dl, %al # copy nybble into AL
andb $0xF, %al # isolate the nybble
xlat # convert nybble to hex
stosb # store digit to buffer
loop nxnyb # process rest of nybbles
popl %edx # restore saved registers
popl %ecx
popl %ebx
popl %eax
ret # return to caller
#-----------------------------------------------------------------
.globl _start # make symbol visible
.end
В следующем примере показана работа с сигналами ,
где реализован вызов sys_sigaction
//----------------------------------------------------------------
// sigdemo1.s
//
// assemble using: $ as sigdemo1.s -o sigdemo1.o
// then link with: $ ld sigdemo1.o -o sigdemo1
//
//----------------------------------------------------------------
.equ SIGSEGV, 11 # ID-number of the signal
.equ sys_exit, 1 # system-call's ID-number
.equ sys_sigaction, 67 # system-call's ID-number
.equ SA_SIGINFO, 0x00000004 # bitmask for flag value
.section .data
sa: # this labels the beginning of our 'struct sigaction' object
sa_handler: .long action # __sighandler_t sa_handler
sa_mask: .long 0 # sigset_t sa_mask
sa_flags: .long SA_SIGINFO # unsigned long sa_flags
sa_restorer: .long 0 # __sig_restore_t sa_restorer
.section .text
action: # this is the entry-point to our signal-handling function
# exit( 1 )
movl $sys_exit, %eax # system-call ID-number
movl $1, %ebx # value for exit-status
int $0x80 # enter the Linux kernel
subrtn: # try writing 0 to the supplied memory-address
movl 4(%esp), %edi # get function-argument
movb $0, (%edi) # store 0 into address
ret # return to the caller
_start: # sigaction( SIGSEGV, &mysa, NULL );
movl $sys_sigaction, %eax # system-call ID-number
movl $SIGSEGV, %ebx # ID-number for signal
movl $sa, %ecx # address of sa struct
movl $0, %edx # null-pointer
int $0x80 # enter the Linux kernel
# subrtn( NULL );
pushl $0 # push function argument
call subrtn # call the function
addl $4, %esp # discard the argument
# exit( 0 );
movl $sys_exit, %eax # system-call ID-number
movl $0, %ebx # value for exit-status
int $0x80 # enter the Linux kernel
.global _start # program-entry is public
.end # no instructions follow
Следующий пример распечатывает факториал первых 12 целых чисел
с использованием рекурсивной функции на ассемблере
//-------------------------------------------------------------------
// fact.cpp
//
// compile using: $ g++ fact.cpp fact.s -o fact
//-------------------------------------------------------------------
#include // for printf()
#define NMAX 12 // maximum function-argument
extern "C" unsigned int fact( unsigned int n );
int main( int argc, char **argv )
{
printf( "\n\t\t N N! " );
printf( "\n\t\t------------------------------" );
for (int n = 0; n <= NMAX; n++)
printf( "\n\t\t%10u %10u ", n, fact(n) );
printf( "\n\t\t------------------------------" );
printf( "\n\n" );
}
//----------------------------------------------------------------
// fact.s
//
// Here is an implementation in assembly language for the
// famous factorial function, defined recursively for any
// nonnegative integer n by this two-part rule:
//
// unsigned int fact( unsigned int n )
// {
// if ( n == 0 ) return 1;
// else return n*fact( n-1 );
// }
//
//----------------------------------------------------------------
.text
fact: # setup access to the stack-frame
pushl %ebp # preserve former frame-pointer
movl %esp, %ebp # establish access to new frame
# test for branching to the two-part rule
cmpl $0, 8(%ebp) # function argument equals zero?
jne recur # no, then do the recursive case
# here we handle the base case
movl $1, %eax # else setup one as return-value
jmp factx # and exit to the calling routine
recur: # here we handle the recursion case
push %edx # preserve caller's value in EDX
# prepare function-argument for recursive call
movl 8(%ebp), %eax # copy current function-argument
decl %eax # decrement argument for call
# this is the recursive call to obtain: fact( n-1 )
pushl %eax # push the new argument n-1
call fact # call the factorial function
addl $4, %esp # discard argument from stack
# now the function-value in EAX gets multiplied by n
mull 8(%ebp) # multiplies 'fact( n-1 )' by n
popl %edx # restore caller's value to EDX
factx:
popl %ebp # restore former frame-pointer
ret # and return conrol to caller
.global fact # entry-point is a public symbol
.end # no further statements follow
Следующая утилита наполняет экран символами , давая возможность
набирать свои . Выход - Ctrl-C :
// assemble using: $ as alphabet.s -o alphabet.o
// and link using: $ ld alphabet.o -o alphabet
.equ sys_write, 4 # service ID-number
.equ dev_stdout, 1 # device ID-number
.data
outln: .space 2000 # no. of screen cells
total: .long 26 # no. of alphabet letters
count: .long 0 # current loop iteration
.text
_start:
call do_fill # fill the output buffer
call do_draw # write buffer to screen
call do_wait # perform a brief delay
call do_incr # increment cycle count
jmp _start # repeat loop forever
do_fill:
# fill buffer with copies of ascii character-code
pushal
movl count, %eax # get iteration counter
addl $'A', %eax # add ascii-code for 'A'
lea outln, %edi # point ES:EDI to buffer
cld # do forward processing
movl $2000, %ecx # setup character count
rep stosb # fill the entire buffer
popal
ret
do_draw:
# write contents of buffer to standard output device
pushal
movl $sys_write, %eax # service ID-number
movl $dev_stdout, %ebx # device ID-number
lea outln, %ecx # buffer offset
movl $2000, %edx # buffer length
int $0x80 # enter the kernel
popal
ret
do_wait:
# do a timed delay of approximately 500-million cpu-cycles
pushal
rdtsc # read timestamp counter
addl $500000000, %eax # increment the quadword
adcl $0x0000000, %edx # counter by 500-million
movl %eax, %ebx # copy bits 31..0 to EBX
movl %edx, %ecx # and bits 63..32 to ECX
.L2: rdtsc # read timestamp again
subl %ebx, %eax # subtract saved quadword
sbbl %ecx, %edx # from latest timestamp
js .L2 # negative? read it again
popal
ret
do_incr:
# advance the cycle-count by 1 (with wrapping at 26)
pushal
incl count # add 1 to the count
movl count, %eax # get new count value
xorl %edx, %edx # extend to quadword
divl total # divide by letter-count
movl %edx, count # remainder is new count
popal
ret
.global _start
Следующая программа выводит системную дату :
// assemble using: $ as showdate.s -o showdate.o
// and link using: $ ld showdate.o -o showdate
.equ sys_EXIT, 1 # ID-number for 'exit'
.equ sys_WRITE, 4 # ID-number for 'write'
.equ sys_IOPL, 110 # ID-number for 'iopl'
.equ dev_STDOUT, 1 # ID-number for STDOUT
.section .data
dayname: .ascii " Sun Mon Tue Wed Thu Fri Sat "
monname: .ascii " Jan Feb Mar Apr May Jun Jul "
.ascii "Aug Sep Oct Nov Dec "
message: .ascii " \n\r"
len: .int . - message # count of characters
.section .text
_start: # adjust this task's IO Privilege-Level
movl $sys_IOPL, %eax # system-call ID-number
movl $3, %ebx # new value for IOPL
int $0x80 # enter the kernel
# read the current day-of-the-week
movb $6, %al # select register-number
out %al, $0x70 # to be accessed
in $0x71, %al # read selected register
# use day-number to lookup weekday name
movzx %al, %eax # extend byte to dword
movl dayname(,%eax,4), %edx # lookup weekday's name
mov %edx, message # insert name in message
# read the current day-of-the-month
movb $7, %al # select register-number
out %al, $0x70 # to be accessed
in $0x71, %al # read selected register
# convert BCD value to decimal-string
mov %al, %ah # copy BCD value to AH
rol $4, %al # exchange the nybbles
and $0x0F0F, %ax # mask isolates nybbles
or $0x3030, %ax # convert to numerals
mov %ax, message+4 # insert into message
# read current month-of-the-year
movb $8, %al # select register-number
out %al, $0x70 # to be accessed
in $0x71, %al # read selected register
# use month-number to lookup month's name
movzx %al, %eax # extend byte to dword
movl monname(,%eax,4), %edx # lookup name for month
mov %edx, message+7 # insert name in message
# read the current year
movb $9, %al # select register-number
out %al, $0x70 # to be accessed
in $0x71, %al # read selected register
# convert BCD value to decimal-string
mov %al, %ah # copy BCD value to AH
rol $4, %al # exchange the nybbles
and $0x0F0F, %eax # mask isolates nybbles
rol $16, %eax # swap hiword and loword
or $0x30303032, %eax # convert into numerals
mov %eax, message+11 # insert into message
# display message showing the current date
mov $sys_WRITE, %eax # system-call ID-number
mov $dev_STDOUT, %ebx # ID-number for device
lea message, %ecx # message's offset
mov len, %edx # message's length
int $0x80 # enter the kernel
# terminate this application
mov $sys_EXIT, %eax # system-call ID-number
mov $0, %ebx # value for exit-code
int $0x80 # enter the kernel
.globl _start # entry-point is public
|