| # |
| # linux_logo in i386 assembly language |
| # based on the code from ll_asm-0.36 |
| # |
| # By Vince Weaver <vince _at_ deater.net> |
| # |
| # Modified to remove non-deterministic system calls |
| # And to avoid reading from /proc |
| # |
| |
| .include "../logo.include" |
| |
| # offsets into the results returned by the uname syscall |
| .equ U_SYSNAME,0 |
| .equ U_NODENAME,65 |
| .equ U_RELEASE,65*2 |
| .equ U_VERSION,(65*3) |
| .equ U_MACHINE,(65*4) |
| .equ U_DOMAINNAME,65*5 |
| |
| # offset into the results returned by the sysinfo syscall |
| .equ S_TOTALRAM,16 |
| |
| # Sycscalls |
| .equ SYSCALL_EXIT, 1 |
| .equ SYSCALL_WRITE, 4 |
| |
| # |
| .equ STDIN,0 |
| .equ STDOUT,1 |
| .equ STDERR,2 |
| |
| .globl _start |
| _start: |
| #========================= |
| # PRINT LOGO |
| #========================= |
| |
| # LZSS decompression algorithm implementation |
| # by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989 |
| # optimized some more by Vince Weaver |
| |
| # we used to fill the buffer with FREQUENT_CHAR |
| # but, that only gains us one byte of space in the lzss image. |
| # the lzss algorithm does automatic RLE... pretty clever |
| # so we compress with NUL as FREQUENT_CHAR and it is pre-done for us |
| |
| mov $(N-F), %bp # R |
| |
| mov $logo, %esi # %esi points to logo (for lodsb) |
| |
| mov $out_buffer, %edi # point to out_buffer |
| push %edi # save this value for later |
| |
| decompression_loop: |
| lodsb # load in a byte |
| |
| mov $0xff, %bh # re-load top as a hackish 8-bit counter |
| mov %al, %bl # move in the flags |
| |
| test_flags: |
| cmp $logo_end, %esi # have we reached the end? |
| je done_logo # if so, exit |
| |
| shr $1, %ebx # shift bottom bit into carry flag |
| jc discrete_char # if set, we jump to discrete char |
| |
| offset_length: |
| lodsw # get match_length and match_position |
| mov %eax,%edx # copy to edx |
| # no need to mask dx, as we do it |
| # by default in output_loop |
| |
| shr $(P_BITS),%eax |
| add $(THRESHOLD+1),%al |
| mov %al,%cl # cl = (ax >> P_BITS) + THRESHOLD + 1 |
| # (=match_length) |
| |
| output_loop: |
| and $POSITION_MASK,%dh # mask it |
| mov text_buf(%edx), %al # load byte from text_buf[] |
| inc %edx # advance pointer in text_buf |
| store_byte: |
| stosb # store it |
| |
| mov %al, text_buf(%ebp) # store also to text_buf[r] |
| inc %ebp # r++ |
| and $(N-1), %bp # mask r |
| |
| loop output_loop # repeat until k>j |
| |
| or %bh,%bh # if 0 we shifted through 8 and must |
| jnz test_flags # re-load flags |
| |
| jmp decompression_loop |
| |
| discrete_char: |
| lodsb # load a byte |
| inc %ecx # we set ecx to one so byte |
| # will be output once |
| # (how do we know ecx is zero?) |
| |
| jmp store_byte # and cleverly store it |
| |
| |
| # end of LZSS code |
| |
| done_logo: |
| |
| pop %ebp # get out_buffer and keep in bp |
| mov %ebp,%ecx # move out_buffer to ecx |
| |
| call write_stdout # print the logo |
| |
| # |
| # Setup |
| # |
| setup: |
| mov $strcat,%edx # use edx as call pointer |
| |
| |
| #========================== |
| # PRINT VERSION |
| #========================== |
| |
| # push $SYSCALL_UNAME # uname syscall |
| # pop %eax # in 3 bytes |
| # mov $uname_info,%ebx # uname struct |
| # int $0x80 # do syscall |
| |
| mov %ebp,%edi # point %edi to out_buffer |
| |
| mov $(uname_info+U_SYSNAME),%esi # os-name from uname "Linux" |
| call *%edx # call strcat |
| |
| mov $ver_string,%esi # source is " Version " |
| call *%edx # call strcat |
| push %esi # save our .txt pointer |
| |
| mov $(uname_info+U_RELEASE),%esi # version from uname "2.4.1" |
| call *%edx # call strcat |
| |
| pop %esi # restore .txt pointer |
| # source is ", Compiled " |
| call *%edx # call strcat |
| push %esi # store for later |
| |
| mov $(uname_info+U_VERSION),%esi # compiled date |
| call *%edx # call strcat |
| |
| mov %ebp,%ecx # move out_buffer to ecx |
| |
| mov $0xa,%ax # store linefeed on end |
| stosw # and zero |
| |
| call *%edx # call strcat |
| |
| call center_and_print # center and print |
| |
| #=============================== |
| # Middle-Line |
| #=============================== |
| |
| #========= |
| # Load /proc/cpuinfo into buffer |
| #========= |
| |
| push %edx # save call pointer |
| |
| # push $SYSCALL_OPEN # load 5 [ open() ] |
| # pop %eax # in 3 bytes |
| |
| # mov $cpuinfo,%ebx # '/proc/cpuinfo' |
| # xor %ecx,%ecx # 0 = O_RDONLY <bits/fcntl.h> |
| # cdq # clear edx in clever way |
| # int $0x80 # syscall. fd in eax. |
| # we should check that eax>=0 |
| |
| # mov %eax,%ebx # save our fd |
| |
| # push $SYSCALL_READ # load 3 = read() |
| # pop %eax # in 3 bytes |
| |
| mov $disk_buffer,%ecx |
| |
| # mov $16,%dh # 4096 is maximum size of proc file #) |
| # we load sneakily by knowing |
| # 16<<8 = 4096. be sure edx clear |
| |
| |
| # int $0x80 |
| |
| # push $SYSCALL_CLOSE # close (to be correct) |
| # pop %eax |
| # int $0x80 |
| |
| #============= |
| # Number of CPUs |
| #============= |
| number_of_cpus: |
| |
| xor %ebx,%ebx # chip count |
| |
| # $disk_buffer still in ecx |
| bogo_loop: |
| mov (%ecx), %eax # load 4 bytes into eax |
| inc %ecx # increment pointer |
| |
| cmp $0,%al # check for end of file |
| je done_bogo |
| |
| # Grrr, due to a bug in binutils 2.18.50.0.9 |
| # (which unfortunately shipped with Fedora 10) |
| # http://sourceware.org/bugzilla/show_bug.cgi?id=6878 |
| # We can't use the apostrophe character |
| |
| # cmp $('o'<<24+'g'<<16+'o'<<8+'b'),%eax |
| cmp $(0x6f<<24+0x67<<16+0x6f<<8+0x62),%eax |
| # "bogo" in little-endian |
| |
| jne bogo_loop # if not equal, keep going |
| |
| inc %ebx # otherwise, we have a bogo |
| inc %ebx # times two for future magic |
| jmp bogo_loop |
| |
| done_bogo: |
| lea one-6(%ebx,%ebx,2), %esi |
| # Load into esi |
| # [one]+(num_cpus*6) |
| # |
| # the above multiplies by three |
| # esi = (ebx+(ebx*2)) |
| # and we double-incremented ebx |
| # earlier |
| |
| mov %ebp,%edi # move output buffer to edi |
| |
| pop %edx # restore call pointer |
| call *%edx # copy it (call strcat) |
| |
| # mov $' ',%al # print a space |
| mov $0x20,%al # print a space |
| stosb |
| |
| push %ebx # store cpu count |
| push %edx # store strcat pointer |
| |
| #========= |
| # MHz |
| #========= |
| print_mhz: |
| # mov $('z'<<24+'H'<<16+'M'<<8+' '),%ebx |
| mov $(0x7a<<24+0x48<<16+0x4d<<8+0x20),%ebx |
| # find ' MHz' and grab up to . |
| # we are little endian |
| # mov $'.',%ah |
| mov $0x2e,%ah |
| |
| # below is same as "sub $(strcat-find_string),%edx |
| # gas won't let us force the one-byte constant |
| .byte 0x83,0xEA,strcat-find_string |
| |
| call *%edx # call find string |
| |
| mov %ebx,%eax # clever way to get MHz in, sadly |
| ror $8,%eax # not any smaller than a mov |
| stosl |
| |
| #========= |
| # Chip Name |
| #========= |
| chip_name: |
| |
| # because of ugly newer cpuinfos from intel I had to hack this |
| # now we grab the first two words in the name field and use that |
| # it works on all recent Intel and AMD chips. Older things |
| # might choke |
| |
| # mov $('e'<<24+'m'<<16+'a'<<8+'n'),%ebx |
| mov $(0x65<<24+0x6d<<16+0x61<<8+0x6e),%ebx |
| # find 'name\t: ' and grab up to \n |
| # we are little endian |
| # mov $' ',%ah |
| mov $0x20,%ah |
| call *%edx # print first word |
| stosb # store a space |
| call skip_spaces # print next word |
| |
| pop %edx |
| pop %ebx # restore chip count |
| pop %esi |
| |
| call *%edx # ' Processor' |
| cmpb $2,%bl |
| jne print_s |
| inc %esi # if singular, skip the s |
| print_s: |
| call *%edx # 's, ' |
| |
| push %esi # restore the values |
| push %edx |
| |
| #======== |
| # RAM |
| #======== |
| |
| # push $SYSCALL_SYSINFO # sysinfo() syscall |
| # pop %eax |
| # mov $sysinfo_buff,%ebx |
| # int $0x80 |
| |
| mov (sysinfo_buff+S_TOTALRAM),%eax # size in bytes of RAM |
| shr $20,%eax # divide by 1024*1024 to get M |
| adc $0, %eax # round |
| |
| |
| call num_to_ascii |
| |
| pop %edx # restore strcat pointer |
| |
| pop %esi # print 'M RAM, ' |
| call *%edx # call strcat |
| |
| push %esi |
| |
| |
| #======== |
| # Bogomips |
| #======== |
| |
| # mov $('s'<<24+'p'<<16+'i'<<8+'m'),%ebx |
| mov $(0x73<<24+0x70<<16+0x69<<8+0x6d),%ebx |
| # find 'mips\t: ' and grab up to \n |
| mov $0xa,%ah |
| call find_string |
| |
| pop %esi # bogo total follows RAM |
| |
| call *%edx # call strcat |
| |
| push %esi |
| |
| mov %ebp,%ecx # point ecx to out_buffer |
| |
| |
| call center_and_print # center and print |
| |
| #================================= |
| # Print Host Name |
| #================================= |
| |
| mov %ebp,%edi # point to output_buffer |
| |
| mov $(uname_info+U_NODENAME),%esi # host name from uname() |
| call *%edx # call strcat |
| |
| # ecx is unchanged |
| call center_and_print # center and print |
| |
| pop %ecx # (.txt) pointer to default_colors |
| |
| call write_stdout |
| |
| |
| #================================ |
| # Exit |
| #================================ |
| exit: |
| xor %ebx,%ebx |
| xor %eax,%eax |
| inc %eax # put exit syscall number (1) in eax |
| int $0x80 # and exit |
| |
| |
| #================================= |
| # FIND_STRING |
| #================================= |
| # ah is char to end at |
| # ebx is 4-char ascii string to look for |
| # edi points at output buffer |
| |
| find_string: |
| |
| mov $disk_buffer-1,%esi # look in cpuinfo buffer |
| find_loop: |
| inc %esi |
| cmpb $0, (%esi) # are we at EOF? |
| je done # if so, done |
| |
| cmp (%esi), %ebx # do the strings match? |
| jne find_loop # if not, loop |
| |
| # ! if we get this far, we matched |
| |
| find_colon: |
| lodsb # repeat till we find colon |
| cmp $0,%al # this is actually smaller code |
| je done # than an or ecx/repnz scasb |
| |
| # cmp $':',%al |
| cmp $0x3a,%al |
| jne find_colon |
| |
| |
| skip_spaces: |
| lodsb # skip spaces |
| cmp $0x20,%al # Loser new intel chips have lots?? |
| je skip_spaces |
| |
| store_loop: |
| cmp $0,%al |
| je done |
| cmp %ah,%al # is it end string? |
| je almost_done # if so, finish |
| # cmp $'\n',%al # also end if linefeed |
| cmp $0xa,%al # also end if linefeed |
| je almost_done |
| stosb # if not store and continue |
| lodsb # load value |
| jmp store_loop |
| |
| almost_done: |
| |
| movb $0, (%edi) # replace last value with NUL |
| done: |
| ret |
| |
| |
| #================================ |
| # strcat |
| #================================ |
| |
| strcat: |
| lodsb # load a byte from [ds:esi] |
| stosb # store a byte to [es:edi] |
| cmp $0,%al # is it zero? |
| jne strcat # if not loop |
| dec %edi # point to one less than null |
| ret # return |
| |
| #============================== |
| # center_and_print |
| #============================== |
| # string to center in ecx |
| |
| center_and_print: |
| push %edx |
| push %ecx # save the string pointer |
| inc %edi # move to a clear buffer |
| push %edi # save for later |
| |
| # mov $('['<<8+27),%ax # we want to output ^[[ |
| mov $(0x5b<<8+27),%ax # we want to output ^[[ |
| stosw |
| |
| cdq # clear dx |
| |
| str_loop2: # find end of string |
| inc %edx |
| cmpb $0,(%ecx,%edx) # repeat till we find zero |
| jne str_loop2 |
| |
| push $81 # one added to cheat, we don't |
| # count the trailing '\n' |
| pop %eax |
| |
| cmp %eax,%edx # see if we are >=80 |
| jl not_too_big # if so, don't center |
| push $80 |
| pop %edx |
| |
| not_too_big: |
| sub %edx,%eax # subtract size from 80 |
| |
| shr %eax # then divide by 2 |
| |
| call num_to_ascii # print number of spaces |
| # mov $'C',%al # tack a 'C' on the end |
| mov $0x43,%al # tack a 'C' on the end |
| # ah is zero from num_to_ascii |
| stosw # store C and a NULL |
| pop %ecx # pop the pointer to ^[[xC |
| |
| call write_stdout # write to the screen |
| |
| done_center: |
| pop %ecx # restore string pointer |
| # and trickily print the real string |
| |
| pop %edx |
| |
| #================================ |
| # WRITE_STDOUT |
| #================================ |
| # ecx has string |
| # eax,ebx,ecx,edx trashed |
| write_stdout: |
| push %edx |
| push $SYSCALL_WRITE # put 4 in eax (write syscall) |
| pop %eax # in 3 bytes of code |
| |
| cdq # clear edx |
| |
| xor %ebx,%ebx # put 1 in ebx (stdout) |
| inc %ebx # in 3 bytes of code |
| |
| # another way of doing this: lea 1(%edx), %ebx |
| |
| str_loop1: |
| inc %edx |
| cmpb $0,(%ecx,%edx) # repeat till zero |
| jne str_loop1 |
| |
| int $0x80 # run the syscall |
| pop %edx |
| ret |
| |
| ############################## |
| # num_to_ascii |
| ############################## |
| # ax = value to print |
| # edi points to where we want it |
| |
| num_to_ascii: |
| push $10 |
| pop %ebx |
| xor %ecx,%ecx # clear ecx |
| div_by_10: |
| cdq # clear edx |
| div %ebx # divide |
| push %edx # save for later |
| inc %ecx # add to length counter |
| or %eax,%eax # was Q zero? |
| jnz div_by_10 # if not divide again |
| |
| write_out: |
| pop %eax # restore in reverse order |
| add $0x30, %al # convert to ASCII |
| stosb # save digit |
| loop write_out # loop till done |
| ret |
| |
| #=========================================================================== |
| # section .data |
| #=========================================================================== |
| .data |
| |
| ver_string: .ascii " Version \0" |
| compiled_string: .ascii ", Compiled \0" |
| processor: .ascii " Processor\0" |
| s_comma: .ascii "s, \0" |
| ram_comma: .ascii "M RAM, \0" |
| bogo_total: .ascii " Bogomips Total\n\0" |
| |
| default_colors: .ascii "\033[0m\n\n\0" |
| |
| cpuinfo: .ascii "/proc/cpuinfo\0" |
| |
| |
| one: .ascii "One\0\0\0" |
| two: .ascii "Two\0\0\0" |
| three: .ascii "Three\0" |
| four: .ascii "Four\0" |
| |
| .include "../logo.lzss_new" |
| |
| disk_buffer: |
| .ascii "processor : 0\n" |
| .ascii "vendor_id : AuthenticAMD\n" |
| .ascii "cpu family : 6\n" |
| .ascii "model : 6\n" |
| .ascii "model name : AMD Athlon(tm) XP 2000+\n" |
| .ascii "stepping : 2\n" |
| .ascii "cpu MHz : 1665.267\n" |
| .ascii "cache size : 256 KB\n" |
| .ascii "fdiv_bug : no\n" |
| .ascii "hlt_bug : no\n" |
| .ascii "f00f_bug : no\n" |
| .ascii "coma_bug : no\n" |
| .ascii "fpu : yes\n" |
| .ascii "fpu_exception : yes\n" |
| .ascii "cpuid level : 1\n" |
| .ascii "wp : yes\n" |
| .ascii "flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse syscall mmxext 3dnowext 3dnow up\n" |
| .ascii "bogomips : 3330.53\n" |
| .ascii "clflush size : 32\n" |
| .ascii "power management: ts\n\0" |
| |
| uname_info: |
| .ascii "Linux\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| |
| .ascii "tobler\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| |
| .ascii "2.6.29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| |
| .ascii "#1 SMP Mon May 4 09:51:54 EDT 2009\0\0" |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| |
| |
| sysinfo_buff: |
| .long 0,0,0,0,512*1024*1024,0,0,0,0 |
| .long 0,0,0,0,0,0,0,0,0 |
| |
| #============================================================================ |
| # section .bss |
| #============================================================================ |
| .bss |
| |
| .lcomm text_buf, (N+F-1) |
| .lcomm out_buffer,16384 |
| |
| |
| |
| |
| |