# # linux_logo in ARM assembly language # based on the code from ll_asm-0.41 # # By Vince Weaver # # 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: ldr r11,data_addr ldr r12,bss_addr #========================= # PRINT LOGO #========================= # LZSS decompression algorithm implementation # by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989 # optimized some more by Vince Weaver ldr r1,out_addr @ buffer we are printing to mov r2,#(N-F) @ R add r3,r11,#(logo-data_begin) @ r3 points to logo data ldr r8,logo_end_addr @ r8 points to logo end ldr r9,text_addr @ r9 points to text buf decompression_loop: ldrb r4,[r3],#+1 @ load a byte, increment pointer mov r5,#0xff @ load top as a hackish 8-bit counter orr r5,r4,r5,LSL #8 @ shift 0xff left by 8 and or in the byte we loaded test_flags: cmp r3,r8 @ have we reached the end? bge done_logo @ if so, exit lsrs r5,#1 @ shift bottom bit into carry flag bcs discrete_char @ if set, we jump to discrete char offset_length: ldrb r0,[r3],#+1 @ load a byte, increment pointer ldrb r4,[r3],#+1 @ load a byte, increment pointer @ we can't load halfword as no unaligned loads on arm orr r4,r0,r4,LSL #8 @ merge back into 16 bits @ this has match_length and match_position mov r7,r4 @ copy r4 to r7 @ no need to mask r7, as we do it @ by default in output_loop mov r0,#(THRESHOLD+1) add r6,r0,r4,LSR #(P_BITS) @ r6 = (r4 >> P_BITS) + THRESHOLD + 1 @ (=match_length) output_loop: ldr r0,pos_mask @ urgh, can't handle simple constants and r7,r7,r0 @ mask it ldrb r4,[r9,r7] @ load byte from text_buf[] add r7,r7,#1 @ advance pointer in text_buf store_byte: strb r4,[r1],#+1 @ store a byte, increment pointer strb r4,[r9,r2] @ store a byte to text_buf[r] add r2,r2,#1 @ r++ mov r0,#(N) sub r0,r0,#1 @ grrr no way to get this easier and r2,r2,r0 @ mask r subs r6,r6,#1 @ decement count bne output_loop @ repeat until k>j tst r5,#0xff00 @ are the top bits 0? bne test_flags @ if not, re-load flags b decompression_loop discrete_char: ldrb r4,[r3],#+1 @ load a byte, increment pointer mov r6,#1 @ we set r6 to one so byte @ will be output once b store_byte @ and store it # end of LZSS code done_logo: ldr r1,out_addr @ buffer we are printing to bl write_stdout @ print the logo #========================== # PRINT VERSION #========================== first_line: mov r0,#0 add r1,r11,#(uname_info-data_begin) @ os-name from uname "Linux" ldr r10,out_addr @ point r10 to out_buffer bl strcat @ call strcat add r1,r11,#(ver_string-data_begin) @ source is " Version " bl strcat @ call strcat add r1,r11,#((uname_info-data_begin)+U_RELEASE) @ version from uname, ie "2.6.20" bl strcat @ call strcat add r1,r11,#(compiled_string-data_begin) @ source is ", Compiled " bl strcat @ call strcat add r1,r11,#((uname_info-data_begin)+U_VERSION) @ compiled date bl strcat @ call strcat mov r3,#0xa strb r3,[r10],#+1 @ store a linefeed, increment pointer strb r0,[r10],#+1 @ NUL terminate, increment pointer bl center_and_print @ center and print @=============================== @ Middle-Line @=============================== middle_line: @========= @ Load /proc/cpuinfo into buffer @========= ldr r10,out_addr @ point r10 to out_buffer @============= @ Number of CPUs @============= number_of_cpus: add r1,r11,#(one-data_begin) # cheat. Who has an SMP arm? bl strcat @========= @ MHz @========= print_mhz: @ the arm system I have does not report MHz @========= @ Chip Name @========= chip_name: mov r0,#'s' mov r1,#'o' mov r2,#'r' mov r3,#' ' bl find_string @ find 'sor\t: ' and grab up to ' ' add r1,r11,#(processor-data_begin) @ print " Processor, " bl strcat @======== @ RAM @======== ldr r3,[r11,#((sysinfo_buff-data_begin)+S_TOTALRAM)] @ size in bytes of RAM movs r3,r3,lsr #20 @ divide by 1024*1024 to get M adc r3,r3,#0 @ round mov r0,#1 bl num_to_ascii add r1,r11,#(ram_comma-data_begin) @ print 'M RAM, ' bl strcat @ call strcat @======== @ Bogomips @======== mov r0,#'I' mov r1,#'P' mov r2,#'S' mov r3,#'\n' bl find_string add r1,r11,#(bogo_total-data_begin) bl strcat @ print bogomips total bl center_and_print @ center and print #================================= # Print Host Name #================================= last_line: ldr r10,out_addr @ point r10 to out_buffer add r1,r11,#((uname_info-data_begin)+U_NODENAME) @ host name from uname() bl strcat @ call strcat bl center_and_print @ center and print add r1,r11,#(default_colors-data_begin) @ restore colors, print a few linefeeds bl write_stdout @================================ @ Exit @================================ exit: mov r0,#0 @ result is zero mov r7,#SYSCALL_EXIT swi 0x0 @ and exit @================================= @ FIND_STRING @================================= @ r0,r1,r2 = string to find @ r3 = char to end at @ r5 trashed find_string: ldr r7,disk_addr @ look in cpuinfo buffer find_loop: ldrb r5,[r7],#+1 @ load a byte, increment pointer cmp r5,r0 @ compare against first byte ldrb r5,[r7] @ load next byte cmpeq r5,r1 @ if first byte matched, comp this one ldrb r5,[r7,#+1] @ load next byte cmpeq r5,r2 @ if first two matched, comp this one beq find_colon @ if all 3 matched, we are found cmp r5,#0 @ are we at EOF? beq done @ if so, done b find_loop find_colon: ldrb r5,[r7],#+1 @ load a byte, increment pointer cmp r5,#':' bne find_colon @ repeat till we find colon add r7,r7,#1 @ skip the space store_loop: ldrb r5,[r7],#+1 @ load a byte, increment pointer strb r5,[r10],#+1 @ store a byte, increment pointer cmp r5,r3 bne store_loop almost_done: mov r0,#0 strb r0,[r10],#-1 @ replace last value with NUL done: bx r14 @ return #================================ # strcat #================================ # value to cat in r1 # output buffer in r10 # r3 trashed strcat: ldrb r3,[r1],#+1 @ load a byte, increment pointer strb r3,[r10],#+1 @ store a byte, increment pointer cmp r3,#0 @ is it zero? bne strcat @ if not loop sub r10,r10,#1 @ point to one less than null bx r14 @ return #============================== # center_and_print #============================== # string to center in at output_buffer center_and_print: stmfd SP!,{LR} @ store return address on stack add r1,r11,#(escape-data_begin) @ we want to output ^[[ bl write_stdout str_loop2: ldr r2,out_addr @ point r2 to out_buffer sub r2,r10,r2 @ get length by subtracting rsb r2,r2,#81 @ reverse subtract! r2=81-r2 @ we use 81 to not count ending \n bne done_center @ if result negative, don't center lsrs r3,r2,#1 @ divide by 2 adc r3,r3,#0 @ round? mov r0,#0 @ print to stdout bl num_to_ascii @ print number of spaces add r1,r11,#(C-data_begin) @ we want to output C bl write_stdout done_center: ldr r1,out_addr @ point r1 to out_buffer ldmfd SP!,{LR} @ restore return address from stack #================================ # WRITE_STDOUT #================================ # r1 has string # r0,r2,r3 trashed write_stdout: mov r2,#0 @ clear count str_loop1: add r2,r2,#1 ldrb r3,[r1,r2] cmp r3,#0 bne str_loop1 @ repeat till zero write_stdout_we_know_size: mov r0,#STDOUT @ print to stdout mov r7,#SYSCALL_WRITE swi 0x0 @ run the syscall bx r14 @ return @############################# @ num_to_ascii @############################# @ r3 = value to print @ r0 = 0=stdout, 1=strcat num_to_ascii: stmfd SP!,{r10,LR} @ store return address on stack add r10,r12,#((ascii_buffer-bss_begin)) add r10,r10,#10 @ point to end of our buffer mov r4,#10 @ we'll be dividing by 10 div_by_10: bl divide @ Q=r7,$0, R=r8,$1 add r8,r8,#0x30 @ convert to ascii strb r8,[r10],#-1 @ store a byte, decrement pointer adds r3,r7,#0 @ move Q in for next divide, update flags bne div_by_10 @ if Q not zero, loop write_out: add r1,r10,#1 @ adjust pointer ldmfd SP!,{r10,LR} @ restore return address from stack cmp r0,#0 bne strcat @ if 1, strcat b write_stdout @ else, fallthrough to stdout @=================================================== @ Divide - because ARM has no hardware int divide @ yes this is an awful algorithm, but simple @ and uses few registers @================================================== @ r3=numerator r4=denominator @ r7=quotient r8=remainder @ r5=trashed divide: mov r7,#0 @ zero out quotient divide_loop: mul r5,r7,r4 @ multiply Q by denominator add r7,r7,#1 @ increment quotient cmp r5,r3 @ is it greater than numerator? ble divide_loop @ if not, loop sub r7,r7,#2 @ otherwise went too far, decrement @ and done mul r5,r7,r4 @ calculate remainder sub r8,r3,r5 @ R=N-(Q*D) bx r14 @ return bss_addr: .word bss_begin data_addr: .word data_begin out_addr: .word out_buffer disk_addr: .word disk_buffer logo_end_addr: .word logo_end pos_mask: .word ((POSITION_MASK<<8)+0xff) text_addr: .word text_buf #=========================================================================== # section .data #=========================================================================== .data data_begin: ver_string: .ascii " Version \0" compiled_string: .ascii ", Compiled \0" processor: .ascii " Processor, \0" ram_comma: .ascii "M RAM, \0" bogo_total: .ascii " Bogomips Total\n\0" default_colors: .ascii "\033[0m\n\n\0" escape: .ascii "\033[\0" C: .ascii "C\0" one: .ascii "One \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 "lindt\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 "2.6.32\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 Wed May 13 15:51:54 UTC 2009\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" disk_buffer: .ascii "Processor : Feroceon 88FR131 rev 1 (v5l)\n" .ascii "BogoMIPS : 1192.75\n" .ascii "Features : swp half thumb fastmult edsp \n" .ascii "CPU implementer : 0x56\n" .ascii "CPU architecture: 5TE\n" .ascii "CPU variant : 0x2\n" .ascii "CPU part : 0x131\n" .ascii "CPU revision : 1\n" .ascii "\n" .ascii "Hardware : Marvell SheevaPlug Reference Board\n" .ascii "Revision : 0000\n" .ascii "Serial : 0000000000000000\n\0" sysinfo_buff: .long 0,0,0,0,512*1024*1024,0,0,0 .include "logo.lzss_new" #============================================================================ # section .bss #============================================================================ .bss bss_begin: .lcomm ascii_buffer,10 .lcomm text_buf, (N+F-1) .lcomm out_buffer,16384