.text .global start start: ! We have been loaded into low RAM, at an address chosen by ! OBP, offset by booter. Figure out our actual location. 1: call 2f nop 2: ! The instruction we think of as being at 1 is really at %o7. ! Compute the actual address of database and save it in %g1. set 1b - database, %o5 sub %o7, %o5, %g1 ! Set up a preliminary %sp stack set (etext + 65536 + 8) - database, %o5 add %g1, %o5, %o5 andn %o5, 7, %sp ! Create a %sp stack frame so we can call the PROM save %sp, -96, %sp ! PROM vector was passed in %o0, now %i0. Save it in %g2. mov %i0, %g2 ! Check PROM interface version ! XXX do we want to check magic number? ld [%g2+4], %l0 ! pv_romvec_vers tst %l0 beq rom_v0 nop subcc %l0,2, %g0 beq rom_v2 nop subcc %l0,3, %g0 beq rom_v3 nop halt: ld [%g2+0x74], %l0 call %l0 nop b halt nop rom_v0: ld [%g2+0x54], %l0 call %l0 add %g0, 0x56, %o0 call %l0 add %g0, 0x30, %o0 call %l0 add %g0, 0x0d, %o0 call %l0 add %g0, 0x0a, %o0 b halt nop print_string: ! String in %o0, no other args; returns length in %o0 ! Depends on %g2. save %sp, -96, %sp mov %g0, %o2 ! inlined strlen 1: ldub [%i0+%o2], %o3 tst %o3 bne 1b inc %o2 dec %o2 mov %i0, %o1 ld [%g2+0x94], %o0 ! v2_fd1 ld [%g2+0xb8], %o4 ! v2_write mov %o2, %i0 call %o4 ld [%o0], %o0 ! *v2_fd1 ret restore print_onechar: ! Char in %o0, no other args ! Depends on %g1 and %g2. add %g1, numbuf-database, %o1 stb %o0, [%o1] stb %g0, [%o1+1] b print_string ! tail call mov %o1, %o0 print_hex8: ! Number in %o0, no other args ! Depends on %g1 and %g2. add %g1, numbuf-database, %o1 add %g1, digits-database, %o2 set 7, %o5 1: and %o0, 15, %o3 ldub [%o2+%o3], %o4 stb %o4, [%o1+%o5] deccc %o5 bge 1b srl %o0, 4, %o0 stb %g0, [%o1+8] b print_string ! tail call mov %o1, %o0 print_hex2: ! Number in %o0, no other args ! Depends on %g1 and %g2. Uses %o1, %o2, %o3, %o4. add %g1, numbuf-database, %o1 add %g1, digits-database, %o2 and %o0, 15, %o3 ldub [%o2+%o3], %o4 stb %o4, [%o1+1] srl %o0, 4, %o3 and %o3, 15, %o3 ldub [%o2+%o3], %o4 stb %o4, [%o1] stb %g0, [%o1+2] b print_string ! tail call mov %o1, %o0 print_dec: ! Number in %o0, no other args ! Depends on %g1 and %g2. save %sp, -96, %sp add %g1, numbuf-database, %i1 add %g1, digits-database, %i2 tst %i0 bge 1f mov %i1, %i4 set 0x2d, %i3 stb %i3, [%i1] inc %i1 sub %g0, %i0, %i0 1: mov %i1, %l2 mov %i0, %o0 1: call udiv add %g0, 10, %o1 ldub [%i2+%o1], %l1 stb %l1, [%i1] tst %o0 bne 1b inc %i1 stb %g0, [%i1] 1: dec %i1 cmp %i1,%l2 ble 1f nop ldub [%i1], %l3 ldub [%l2], %l4 stb %l3, [%l2] stb %l4, [%i1] b 1b inc %l2 1: mov %i4, %i0 b print_string ! tail call restore udiv: ! Unsigned divide %o0 by %o1, quotient in %o0, remainder in %o1 ! Uses %o2, %o3, %o4. ! If %o1 is zero, division by zero. tst %o1 bne 1f nop call print_string add %g1, divby0-database, %o0 b halt nop 1: ! If %o1 > %o0, easy. (This includes %o0 being zero.) cmp %o1, %o0 bleu 1f nop mov %o0, %o1 retl mov %g0, %o0 1: ! Since %o1 <= %o0, %o1's high bit must be no higher than ! %o0's. Copy %o1 to %o2, then shift %o2 left until its high ! bit matches %o0's; then we can start the divide loop. But ! this means figuring out where %o0's high bit is. ! Annoyingly, there is no srlcc instruction; we could save a ! register (and two instructions per iteration) if there were. set 16, %o4 mov %o4, %o3 2: srl %o0, %o3, %o2 tst %o2 be,a 1f sub %o3, %o4, %o3 1: srl %o4, 1, %o4 tst %o4 bne,a 2b add %o3, %o4, %o3 ! %o3 is now the largest amount %o0 can be shifted right by ! without shifting the high bit away. add %g0, 1, %o2 sll %o2, %o3, %o3 ! %o3 is now %o0's high bit. Copy %o1 to %o2 and shift it left ! until its high bit hits %o3. mov %o1, %o2 1: andcc %o2, %o3, %g0 be,a 1b sll %o2, 1, %o2 ! We can now do the divide loop. We develop the quotient in ! %o3 and the remainder in %o0. mov %g0, %o3 2: cmp %o2, %o0 bgu 1f sll %o3, 1, %o3 sub %o0, %o2, %o0 inc %o3 1: srl %o2, 1, %o2 cmp %o2, %o1 bge 2b nop mov %o0, %o1 retl mov %o3, %o0 umul: ! Unsigned multiply %o0 by %o1. Product in %o0 (low) and %o1 (high). ! Uses %o2, %o3, %o4, %o5. ! Could use mulscc instead. Likely faster, especially for large ! numbers, but definitely more code space needed. mov %g0, %o2 mov %g0, %o3 mov %g0, %o4 2: set 1, %o5 andcc %o1, %o5, %g0 be 1f srl %o1, 1, %o1 addcc %o2, %o0, %o2 addx %o3, %o4, %o3 1: sll %o4, 1, %o4 srl %o0, 31, %o5 or %o4, %o5, %o4 tst %o1 bne 2b sll %o0, 1, %o0 mov %o2, %o0 retl mov %o3, %o1 rom_v2: rom_v3: call print_string add %g1, starting-database, %o0 ! Walk the children of the root node, looking for one with a ! `name' property with value "memory". ! First, get the root with no_nextnode applied to 0. ld [%g2+0x1c], %l1 ! pv_nodeops ld [%l1], %l0 ! no_nextnode call %l0 mov %g0, %o0 ! Get the root's first child. ld [%l1+0x04], %l0 ! no_child call %l0 nop ! arg already in %o0 ! Loop, checking `name' properties. mov %o0, %l7 4: ld [%l1+0x08], %l0 ! no_proplen add %g1, name_str-database, %o1 call %l0 mov %l7, %o0 ! If length isn't <6 or >7, isn't "memory". ("not found" comes ! back to us as a length of -1.) The reason we allow both 6 ! and 7 is that at least some ROMs return "memory\0". sub %o0, 6, %o1 cmp %o1, 1 bgu 1f mov %o0, %l6 ! Fetch the value ld [%l1+0x0c], %l0 ! no_getprop add %g1, propbuf-database, %o2 add %g1, name_str-database, %o1 call %l0 mov %l7, %o0 ! Is it "memory" or "memory\0"? add %g1, propbuf-database, %o0 add %g1, memory_str-database, %o1 mov %g0, %o2 3: ldub [%o0+%o2], %o3 ldub [%o1+%o2], %o4 cmp %o3, %o4 bne 2f inc %o2 cmp %o2, %l6 bl 3b nop ! Found it! ! Fetch the `available' prop. ld [%l1+0x08], %l0 ! no_proplen add %g1, available_str-database, %o1 call %l0 mov %l7, %o0 ! Is it even there? tst %o0 bgu,a 3f ! If so, skip over loop bottom cmp %o0, epropbuf-propbuf call print_string add %g1, missing_available-database, %o0 b halt nop 2: ! Value isn't "memory". 1: ! Length isn't 7. ! Fetch the next child and, if we haven't run out, try it. ld [%l1], %l0 ! no_nextnode call %l0 mov %l7, %o0 orcc %o0, %g0, %l7 bne 4b nop ! Ran out of nodes. call print_string add %g1, no_mem_node-database, %o0 b halt nop 3: ! Come here if the `available' property exists. ! Is it too long? ble 1f mov %o0, %l2 call print_string add %g1, too_fragmented-database, %o0 call print_dec mov %l2, %o0 call print_string add %g1, crlf-database, %o0 b halt nop 1: ! It fits! Fetch the value. ld [%l1+0x0c], %l0 ! no_getprop add %g1, propbuf-database, %o2 add %g1, available_str-database, %o1 call %l0 mov %l7, %o0 ! See how many memory segments we have. mov %l2, %o0 call udiv add %g0, 12, %o1 ! If there's a remainder, something's weird. tst %o1 be,a 1f mov %o0, %l2 call print_string add %g1, bad_length-database, %o0 call print_dec mov %l2, %o0 call print_string add %g1, crlf-database, %o0 b halt nop 1: ! Report the number of memory segments. call print_string add %g1, memseg_count-database, %o0 call print_dec mov %l2, %o0 call print_string add %g1, crlf-database, %o0 ! Print the memory segments themselves. mov %g0, %l3 1: set 12, %o0 call umul mov %l3, %o1 add %g1, propbuf-database, %l4 add %l4, %o0, %l4 ldub [%l4+4], %l5 ldub [%l4+5], %l6 sll %l5, 8, %l5 or %l5, %l6, %l5 ldub [%l4+6], %l6 sll %l5, 8, %l5 or %l5, %l6, %l5 ldub [%l4+7], %l6 sll %l5, 8, %l5 call print_hex8 or %l5, %l6, %o0 call print_string add %g1, spc2-database, %o0 ldub [%l4+8], %l5 ldub [%l4+9], %l6 sll %l5, 8, %l5 or %l5, %l6, %l5 ldub [%l4+10], %l6 sll %l5, 8, %l5 or %l5, %l6, %l5 ldub [%l4+11], %l6 sll %l5, 8, %l5 call print_hex8 or %l5, %l6, %o0 call print_string add %g1, crlf-database, %o0 inc %l3 cmp %l3, %l2 bl 1b nop b halt nop database: toolong: .ascii "\0" starting: .ascii "\r\nMEMTEST-SPARC Started\r\n\0" digits: .ascii "0123456789abcdef" numbuf: .ascii "................\0" crlf: .ascii "\r\n\0" zstr: .ascii "\0" spc: .ascii " \0" spc2: .ascii " \0" colon: .ascii ":\0" name_str: .ascii "name\0" memory_str: .ascii "memory\0" available_str: .ascii "available\0" no_mem_node: .ascii "No `memory' node found!\r\n\0" divby0: .ascii "Division by zero!\r\n\0" missing_available: .ascii "`memory' node has no `available' property\r\n\0" too_fragmented: .ascii "Too many memory segments - property length \0" bad_length: .ascii "memory:available length isn't a multiple of 12: \0" memseg_count: .ascii "memory segment count: \0" propbuf: .skip 256 epropbuf: .align 4 etext: