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