2 # linux_logo in i386 assembly language
3 # based on the code from ll_asm-0.36
5 # By Vince Weaver <vince _at_ deater.net>
7 # Modified to remove non-deterministic system calls
8 # And to avoid reading from /proc
11 .include "logo.include"
13 # offsets into the results returned by the uname syscall
19 .equ U_DOMAINNAME,65*5
21 # offset into the results returned by the sysinfo syscall
35 #=========================
37 #=========================
39 # LZSS decompression algorithm implementation
40 # by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989
41 # optimized some more by Vince Weaver
43 # we used to fill the buffer with FREQUENT_CHAR
44 # but, that only gains us one byte of space in the lzss image.
45 # the lzss algorithm does automatic RLE... pretty clever
46 # so we compress with NUL as FREQUENT_CHAR and it is pre-done for us
50 mov $logo, %esi # %esi points to logo (for lodsb)
52 mov $out_buffer, %edi # point to out_buffer
53 push %edi # save this value for later
56 lodsb # load in a byte
58 mov $0xff, %bh # re-load top as a hackish 8-bit counter
59 mov %al, %bl # move in the flags
62 cmp $logo_end, %esi # have we reached the end?
63 je done_logo # if so, exit
65 shr $1, %ebx # shift bottom bit into carry flag
66 jc discrete_char # if set, we jump to discrete char
69 lodsw # get match_length and match_position
70 mov %eax,%edx # copy to edx
71 # no need to mask dx, as we do it
72 # by default in output_loop
75 add $(THRESHOLD+1),%al
76 mov %al,%cl # cl = (ax >> P_BITS) + THRESHOLD + 1
80 and $POSITION_MASK,%dh # mask it
81 mov text_buf(%edx), %al # load byte from text_buf[]
82 inc %edx # advance pointer in text_buf
86 mov %al, text_buf(%ebp) # store also to text_buf[r]
88 and $(N-1), %bp # mask r
90 loop output_loop # repeat until k>j
92 or %bh,%bh # if 0 we shifted through 8 and must
93 jnz test_flags # re-load flags
95 jmp decompression_loop
99 inc %ecx # we set ecx to one so byte
100 # will be output once
101 # (how do we know ecx is zero?)
103 jmp store_byte # and cleverly store it
110 pop %ebp # get out_buffer and keep in bp
111 mov %ebp,%ecx # move out_buffer to ecx
113 call write_stdout # print the logo
119 mov $strcat,%edx # use edx as call pointer
122 #==========================
124 #==========================
126 # push $SYSCALL_UNAME # uname syscall
127 # pop %eax # in 3 bytes
128 # mov $uname_info,%ebx # uname struct
129 # int $0x80 # do syscall
131 mov %ebp,%edi # point %edi to out_buffer
133 mov $(uname_info+U_SYSNAME),%esi # os-name from uname "Linux"
134 call *%edx # call strcat
136 mov $ver_string,%esi # source is " Version "
137 call *%edx # call strcat
138 push %esi # save our .txt pointer
140 mov $(uname_info+U_RELEASE),%esi # version from uname "2.4.1"
141 call *%edx # call strcat
143 pop %esi # restore .txt pointer
144 # source is ", Compiled "
145 call *%edx # call strcat
146 push %esi # store for later
148 mov $(uname_info+U_VERSION),%esi # compiled date
149 call *%edx # call strcat
151 mov %ebp,%ecx # move out_buffer to ecx
153 mov $0xa,%ax # store linefeed on end
156 call *%edx # call strcat
158 call center_and_print # center and print
160 #===============================
162 #===============================
165 # Load /proc/cpuinfo into buffer
168 push %edx # save call pointer
170 # push $SYSCALL_OPEN # load 5 [ open() ]
171 # pop %eax # in 3 bytes
173 # mov $cpuinfo,%ebx # '/proc/cpuinfo'
174 # xor %ecx,%ecx # 0 = O_RDONLY <bits/fcntl.h>
175 # cdq # clear edx in clever way
176 # int $0x80 # syscall. fd in eax.
177 # we should check that eax>=0
179 # mov %eax,%ebx # save our fd
181 # push $SYSCALL_READ # load 3 = read()
182 # pop %eax # in 3 bytes
184 mov $disk_buffer,%ecx
186 # mov $16,%dh # 4096 is maximum size of proc file #)
187 # we load sneakily by knowing
188 # 16<<8 = 4096. be sure edx clear
193 # push $SYSCALL_CLOSE # close (to be correct)
202 xor %ebx,%ebx # chip count
204 # $disk_buffer still in ecx
206 mov (%ecx), %eax # load 4 bytes into eax
207 inc %ecx # increment pointer
209 cmp $0,%al # check for end of file
212 # Grrr, due to a bug in binutils 2.18.50.0.9
213 # (which unfortunately shipped with Fedora 10)
214 # http://sourceware.org/bugzilla/show_bug.cgi?id=6878
215 # We can't use the apostrophe character
217 # cmp $('o'<<24+'g'<<16+'o'<<8+'b'),%eax
218 cmp $(0x6f<<24+0x67<<16+0x6f<<8+0x62),%eax
219 # "bogo" in little-endian
221 jne bogo_loop # if not equal, keep going
223 inc %ebx # otherwise, we have a bogo
224 inc %ebx # times two for future magic
228 lea one-6(%ebx,%ebx,2), %esi
232 # the above multiplies by three
233 # esi = (ebx+(ebx*2))
234 # and we double-incremented ebx
237 mov %ebp,%edi # move output buffer to edi
239 pop %edx # restore call pointer
240 call *%edx # copy it (call strcat)
242 # mov $' ',%al # print a space
243 mov $0x20,%al # print a space
246 push %ebx # store cpu count
247 push %edx # store strcat pointer
253 # mov $('z'<<24+'H'<<16+'M'<<8+' '),%ebx
254 mov $(0x7a<<24+0x48<<16+0x4d<<8+0x20),%ebx
255 # find ' MHz' and grab up to .
256 # we are little endian
260 # below is same as "sub $(strcat-find_string),%edx
261 # gas won't let us force the one-byte constant
262 .byte 0x83,0xEA,strcat-find_string
264 call *%edx # call find string
266 mov %ebx,%eax # clever way to get MHz in, sadly
267 ror $8,%eax # not any smaller than a mov
275 # because of ugly newer cpuinfos from intel I had to hack this
276 # now we grab the first two words in the name field and use that
277 # it works on all recent Intel and AMD chips. Older things
280 # mov $('e'<<24+'m'<<16+'a'<<8+'n'),%ebx
281 mov $(0x65<<24+0x6d<<16+0x61<<8+0x6e),%ebx
282 # find 'name\t: ' and grab up to \n
283 # we are little endian
286 call *%edx # print first word
287 stosb # store a space
288 call skip_spaces # print next word
291 pop %ebx # restore chip count
294 call *%edx # ' Processor'
297 inc %esi # if singular, skip the s
301 push %esi # restore the values
308 # push $SYSCALL_SYSINFO # sysinfo() syscall
310 # mov $sysinfo_buff,%ebx
313 mov (sysinfo_buff+S_TOTALRAM),%eax # size in bytes of RAM
314 shr $20,%eax # divide by 1024*1024 to get M
320 pop %edx # restore strcat pointer
322 pop %esi # print 'M RAM, '
323 call *%edx # call strcat
332 # mov $('s'<<24+'p'<<16+'i'<<8+'m'),%ebx
333 mov $(0x73<<24+0x70<<16+0x69<<8+0x6d),%ebx
334 # find 'mips\t: ' and grab up to \n
338 pop %esi # bogo total follows RAM
340 call *%edx # call strcat
344 mov %ebp,%ecx # point ecx to out_buffer
347 call center_and_print # center and print
349 #=================================
351 #=================================
353 mov %ebp,%edi # point to output_buffer
355 mov $(uname_info+U_NODENAME),%esi # host name from uname()
356 call *%edx # call strcat
359 call center_and_print # center and print
361 pop %ecx # (.txt) pointer to default_colors
366 #================================
368 #================================
372 inc %eax # put exit syscall number (1) in eax
376 #=================================
378 #=================================
379 # ah is char to end at
380 # ebx is 4-char ascii string to look for
381 # edi points at output buffer
385 mov $disk_buffer-1,%esi # look in cpuinfo buffer
388 cmpb $0, (%esi) # are we at EOF?
389 je done # if so, done
391 cmp (%esi), %ebx # do the strings match?
392 jne find_loop # if not, loop
394 # ! if we get this far, we matched
397 lodsb # repeat till we find colon
398 cmp $0,%al # this is actually smaller code
399 je done # than an or ecx/repnz scasb
408 cmp $0x20,%al # Loser new intel chips have lots??
414 cmp %ah,%al # is it end string?
415 je almost_done # if so, finish
416 # cmp $'\n',%al # also end if linefeed
417 cmp $0xa,%al # also end if linefeed
419 stosb # if not store and continue
425 movb $0, (%edi) # replace last value with NUL
430 #================================
432 #================================
435 lodsb # load a byte from [ds:esi]
436 stosb # store a byte to [es:edi]
437 cmp $0,%al # is it zero?
438 jne strcat # if not loop
439 dec %edi # point to one less than null
442 #==============================
444 #==============================
445 # string to center in ecx
449 push %ecx # save the string pointer
450 inc %edi # move to a clear buffer
451 push %edi # save for later
453 # mov $('['<<8+27),%ax # we want to output ^[[
454 mov $(0x5b<<8+27),%ax # we want to output ^[[
459 str_loop2: # find end of string
461 cmpb $0,(%ecx,%edx) # repeat till we find zero
464 push $81 # one added to cheat, we don't
465 # count the trailing '\n'
468 cmp %eax,%edx # see if we are >=80
469 jl not_too_big # if so, don't center
474 sub %edx,%eax # subtract size from 80
476 shr %eax # then divide by 2
478 call num_to_ascii # print number of spaces
479 # mov $'C',%al # tack a 'C' on the end
480 mov $0x43,%al # tack a 'C' on the end
481 # ah is zero from num_to_ascii
482 stosw # store C and a NULL
483 pop %ecx # pop the pointer to ^[[xC
485 call write_stdout # write to the screen
488 pop %ecx # restore string pointer
489 # and trickily print the real string
493 #================================
495 #================================
497 # eax,ebx,ecx,edx trashed
500 push $SYSCALL_WRITE # put 4 in eax (write syscall)
501 pop %eax # in 3 bytes of code
505 xor %ebx,%ebx # put 1 in ebx (stdout)
506 inc %ebx # in 3 bytes of code
508 # another way of doing this: lea 1(%edx), %ebx
512 cmpb $0,(%ecx,%edx) # repeat till zero
515 int $0x80 # run the syscall
519 ##############################
521 ##############################
522 # ax = value to print
523 # edi points to where we want it
528 xor %ecx,%ecx # clear ecx
532 push %edx # save for later
533 inc %ecx # add to length counter
534 or %eax,%eax # was Q zero?
535 jnz div_by_10 # if not divide again
538 pop %eax # restore in reverse order
539 add $0x30, %al # convert to ASCII
541 loop write_out # loop till done
544 #===========================================================================
546 #===========================================================================
549 ver_string: .ascii " Version \0"
550 compiled_string: .ascii ", Compiled \0"
551 processor: .ascii " Processor\0"
552 s_comma: .ascii "s, \0"
553 ram_comma: .ascii "M RAM, \0"
554 bogo_total: .ascii " Bogomips Total\n\0"
556 default_colors: .ascii "\033[0m\n\n\0"
558 cpuinfo: .ascii "/proc/cpuinfo\0"
561 one: .ascii "One\0\0\0"
562 two: .ascii "Two\0\0\0"
563 three: .ascii "Three\0"
564 four: .ascii "Four\0"
566 .include "logo.lzss_new"
569 .ascii "processor : 0\n"
570 .ascii "vendor_id : AuthenticAMD\n"
571 .ascii "cpu family : 6\n"
573 .ascii "model name : AMD Athlon(tm) XP 2000+\n"
574 .ascii "stepping : 2\n"
575 .ascii "cpu MHz : 1665.267\n"
576 .ascii "cache size : 256 KB\n"
577 .ascii "fdiv_bug : no\n"
578 .ascii "hlt_bug : no\n"
579 .ascii "f00f_bug : no\n"
580 .ascii "coma_bug : no\n"
582 .ascii "fpu_exception : yes\n"
583 .ascii "cpuid level : 1\n"
585 .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"
586 .ascii "bogomips : 3330.53\n"
587 .ascii "clflush size : 32\n"
588 .ascii "power management: ts\n\0"
591 .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"
592 .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"
594 .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"
595 .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"
597 .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"
598 .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"
600 .ascii "#1 SMP Mon May 4 09:51:54 EDT 2009\0\0"
601 .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"
603 .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"
604 .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"
606 .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"
607 .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"
611 .long 0,0,0,0,512*1024*1024,0,0,0,0
612 .long 0,0,0,0,0,0,0,0,0
614 #============================================================================
616 #============================================================================
619 .lcomm text_buf, (N+F-1)
620 .lcomm out_buffer,16384