11da177e4SLinus Torvalds| 21da177e4SLinus Torvalds| x_operr.sa 3.5 7/1/91 31da177e4SLinus Torvalds| 41da177e4SLinus Torvalds| fpsp_operr --- FPSP handler for operand error exception 51da177e4SLinus Torvalds| 61da177e4SLinus Torvalds| See 68040 User's Manual pp. 9-44f 71da177e4SLinus Torvalds| 81da177e4SLinus Torvalds| Note 1: For trap disabled 040 does the following: 91da177e4SLinus Torvalds| If the dest is a fp reg, then an extended precision non_signaling 101da177e4SLinus Torvalds| NAN is stored in the dest reg. If the dest format is b, w, or l and 111da177e4SLinus Torvalds| the source op is a NAN, then garbage is stored as the result (actually 121da177e4SLinus Torvalds| the upper 32 bits of the mantissa are sent to the integer unit). If 131da177e4SLinus Torvalds| the dest format is integer (b, w, l) and the operr is caused by 141da177e4SLinus Torvalds| integer overflow, or the source op is inf, then the result stored is 151da177e4SLinus Torvalds| garbage. 161da177e4SLinus Torvalds| There are three cases in which operr is incorrectly signaled on the 171da177e4SLinus Torvalds| 040. This occurs for move_out of format b, w, or l for the largest 181da177e4SLinus Torvalds| negative integer (-2^7 for b, -2^15 for w, -2^31 for l). 191da177e4SLinus Torvalds| 201da177e4SLinus Torvalds| On opclass = 011 fmove.(b,w,l) that causes a conversion 211da177e4SLinus Torvalds| overflow -> OPERR, the exponent in wbte (and fpte) is: 221da177e4SLinus Torvalds| byte 56 - (62 - exp) 231da177e4SLinus Torvalds| word 48 - (62 - exp) 241da177e4SLinus Torvalds| long 32 - (62 - exp) 251da177e4SLinus Torvalds| 261da177e4SLinus Torvalds| where exp = (true exp) - 1 271da177e4SLinus Torvalds| 281da177e4SLinus Torvalds| So, wbtemp and fptemp will contain the following on erroneously 291da177e4SLinus Torvalds| signalled operr: 301da177e4SLinus Torvalds| fpts = 1 311da177e4SLinus Torvalds| fpte = $4000 (15 bit externally) 321da177e4SLinus Torvalds| byte fptm = $ffffffff ffffff80 331da177e4SLinus Torvalds| word fptm = $ffffffff ffff8000 341da177e4SLinus Torvalds| long fptm = $ffffffff 80000000 351da177e4SLinus Torvalds| 361da177e4SLinus Torvalds| Note 2: For trap enabled 040 does the following: 371da177e4SLinus Torvalds| If the inst is move_out, then same as Note 1. 381da177e4SLinus Torvalds| If the inst is not move_out, the dest is not modified. 391da177e4SLinus Torvalds| The exceptional operand is not defined for integer overflow 401da177e4SLinus Torvalds| during a move_out. 411da177e4SLinus Torvalds| 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds| Copyright (C) Motorola, Inc. 1990 441da177e4SLinus Torvalds| All Rights Reserved 451da177e4SLinus Torvalds| 46*e00d82d0SMatt Waddel| For details on the license for this file, please see the 47*e00d82d0SMatt Waddel| file, README, in this same directory. 481da177e4SLinus Torvalds 491da177e4SLinus TorvaldsX_OPERR: |idnt 2,1 | Motorola 040 Floating Point Software Package 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds |section 8 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds#include "fpsp.h" 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds |xref mem_write 561da177e4SLinus Torvalds |xref real_operr 571da177e4SLinus Torvalds |xref real_inex 581da177e4SLinus Torvalds |xref get_fline 591da177e4SLinus Torvalds |xref fpsp_done 601da177e4SLinus Torvalds |xref reg_dest 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds .global fpsp_operr 631da177e4SLinus Torvaldsfpsp_operr: 641da177e4SLinus Torvalds| 651da177e4SLinus Torvalds link %a6,#-LOCAL_SIZE 661da177e4SLinus Torvalds fsave -(%a7) 671da177e4SLinus Torvalds moveml %d0-%d1/%a0-%a1,USER_DA(%a6) 681da177e4SLinus Torvalds fmovemx %fp0-%fp3,USER_FP0(%a6) 691da177e4SLinus Torvalds fmoveml %fpcr/%fpsr/%fpiar,USER_FPCR(%a6) 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds| 721da177e4SLinus Torvalds| Check if this is an opclass 3 instruction. 731da177e4SLinus Torvalds| If so, fall through, else branch to operr_end 741da177e4SLinus Torvalds| 751da177e4SLinus Torvalds btstb #TFLAG,T_BYTE(%a6) 761da177e4SLinus Torvalds beqs operr_end 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds| 791da177e4SLinus Torvalds| If the destination size is B,W,or L, the operr must be 801da177e4SLinus Torvalds| handled here. 811da177e4SLinus Torvalds| 821da177e4SLinus Torvalds movel CMDREG1B(%a6),%d0 831da177e4SLinus Torvalds bfextu %d0{#3:#3},%d0 |0=long, 4=word, 6=byte 841da177e4SLinus Torvalds cmpib #0,%d0 |determine size; check long 851da177e4SLinus Torvalds beq operr_long 861da177e4SLinus Torvalds cmpib #4,%d0 |check word 871da177e4SLinus Torvalds beq operr_word 881da177e4SLinus Torvalds cmpib #6,%d0 |check byte 891da177e4SLinus Torvalds beq operr_byte 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds| 921da177e4SLinus Torvalds| The size is not B,W,or L, so the operr is handled by the 931da177e4SLinus Torvalds| kernel handler. Set the operr bits and clean up, leaving 941da177e4SLinus Torvalds| only the integer exception frame on the stack, and the 951da177e4SLinus Torvalds| fpu in the original exceptional state. 961da177e4SLinus Torvalds| 971da177e4SLinus Torvaldsoperr_end: 981da177e4SLinus Torvalds bsetb #operr_bit,FPSR_EXCEPT(%a6) 991da177e4SLinus Torvalds bsetb #aiop_bit,FPSR_AEXCEPT(%a6) 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds moveml USER_DA(%a6),%d0-%d1/%a0-%a1 1021da177e4SLinus Torvalds fmovemx USER_FP0(%a6),%fp0-%fp3 1031da177e4SLinus Torvalds fmoveml USER_FPCR(%a6),%fpcr/%fpsr/%fpiar 1041da177e4SLinus Torvalds frestore (%a7)+ 1051da177e4SLinus Torvalds unlk %a6 1061da177e4SLinus Torvalds bral real_operr 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvaldsoperr_long: 1091da177e4SLinus Torvalds moveql #4,%d1 |write size to d1 1101da177e4SLinus Torvalds moveb STAG(%a6),%d0 |test stag for nan 1111da177e4SLinus Torvalds andib #0xe0,%d0 |clr all but tag 1121da177e4SLinus Torvalds cmpib #0x60,%d0 |check for nan 1131da177e4SLinus Torvalds beq operr_nan 1141da177e4SLinus Torvalds cmpil #0x80000000,FPTEMP_LO(%a6) |test if ls lword is special 1151da177e4SLinus Torvalds bnes chklerr |if not equal, check for incorrect operr 1161da177e4SLinus Torvalds bsr check_upper |check if exp and ms mant are special 1171da177e4SLinus Torvalds tstl %d0 1181da177e4SLinus Torvalds bnes chklerr |if d0 is true, check for incorrect operr 1191da177e4SLinus Torvalds movel #0x80000000,%d0 |store special case result 1201da177e4SLinus Torvalds bsr operr_store 1211da177e4SLinus Torvalds bra not_enabled |clean and exit 1221da177e4SLinus Torvalds| 1231da177e4SLinus Torvalds| CHECK FOR INCORRECTLY GENERATED OPERR EXCEPTION HERE 1241da177e4SLinus Torvalds| 1251da177e4SLinus Torvaldschklerr: 1261da177e4SLinus Torvalds movew FPTEMP_EX(%a6),%d0 1271da177e4SLinus Torvalds andw #0x7FFF,%d0 |ignore sign bit 1281da177e4SLinus Torvalds cmpw #0x3FFE,%d0 |this is the only possible exponent value 1291da177e4SLinus Torvalds bnes chklerr2 1301da177e4SLinus Torvaldsfixlong: 1311da177e4SLinus Torvalds movel FPTEMP_LO(%a6),%d0 1321da177e4SLinus Torvalds bsr operr_store 1331da177e4SLinus Torvalds bra not_enabled 1341da177e4SLinus Torvaldschklerr2: 1351da177e4SLinus Torvalds movew FPTEMP_EX(%a6),%d0 1361da177e4SLinus Torvalds andw #0x7FFF,%d0 |ignore sign bit 1371da177e4SLinus Torvalds cmpw #0x4000,%d0 1381da177e4SLinus Torvalds bcc store_max |exponent out of range 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds movel FPTEMP_LO(%a6),%d0 1411da177e4SLinus Torvalds andl #0x7FFF0000,%d0 |look for all 1's on bits 30-16 1421da177e4SLinus Torvalds cmpl #0x7FFF0000,%d0 1431da177e4SLinus Torvalds beqs fixlong 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds tstl FPTEMP_LO(%a6) 1461da177e4SLinus Torvalds bpls chklepos 1471da177e4SLinus Torvalds cmpl #0xFFFFFFFF,FPTEMP_HI(%a6) 1481da177e4SLinus Torvalds beqs fixlong 1491da177e4SLinus Torvalds bra store_max 1501da177e4SLinus Torvaldschklepos: 1511da177e4SLinus Torvalds tstl FPTEMP_HI(%a6) 1521da177e4SLinus Torvalds beqs fixlong 1531da177e4SLinus Torvalds bra store_max 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvaldsoperr_word: 1561da177e4SLinus Torvalds moveql #2,%d1 |write size to d1 1571da177e4SLinus Torvalds moveb STAG(%a6),%d0 |test stag for nan 1581da177e4SLinus Torvalds andib #0xe0,%d0 |clr all but tag 1591da177e4SLinus Torvalds cmpib #0x60,%d0 |check for nan 1601da177e4SLinus Torvalds beq operr_nan 1611da177e4SLinus Torvalds cmpil #0xffff8000,FPTEMP_LO(%a6) |test if ls lword is special 1621da177e4SLinus Torvalds bnes chkwerr |if not equal, check for incorrect operr 1631da177e4SLinus Torvalds bsr check_upper |check if exp and ms mant are special 1641da177e4SLinus Torvalds tstl %d0 1651da177e4SLinus Torvalds bnes chkwerr |if d0 is true, check for incorrect operr 1661da177e4SLinus Torvalds movel #0x80000000,%d0 |store special case result 1671da177e4SLinus Torvalds bsr operr_store 1681da177e4SLinus Torvalds bra not_enabled |clean and exit 1691da177e4SLinus Torvalds| 1701da177e4SLinus Torvalds| CHECK FOR INCORRECTLY GENERATED OPERR EXCEPTION HERE 1711da177e4SLinus Torvalds| 1721da177e4SLinus Torvaldschkwerr: 1731da177e4SLinus Torvalds movew FPTEMP_EX(%a6),%d0 1741da177e4SLinus Torvalds andw #0x7FFF,%d0 |ignore sign bit 1751da177e4SLinus Torvalds cmpw #0x3FFE,%d0 |this is the only possible exponent value 1761da177e4SLinus Torvalds bnes store_max 1771da177e4SLinus Torvalds movel FPTEMP_LO(%a6),%d0 1781da177e4SLinus Torvalds swap %d0 1791da177e4SLinus Torvalds bsr operr_store 1801da177e4SLinus Torvalds bra not_enabled 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvaldsoperr_byte: 1831da177e4SLinus Torvalds moveql #1,%d1 |write size to d1 1841da177e4SLinus Torvalds moveb STAG(%a6),%d0 |test stag for nan 1851da177e4SLinus Torvalds andib #0xe0,%d0 |clr all but tag 1861da177e4SLinus Torvalds cmpib #0x60,%d0 |check for nan 1871da177e4SLinus Torvalds beqs operr_nan 1881da177e4SLinus Torvalds cmpil #0xffffff80,FPTEMP_LO(%a6) |test if ls lword is special 1891da177e4SLinus Torvalds bnes chkberr |if not equal, check for incorrect operr 1901da177e4SLinus Torvalds bsr check_upper |check if exp and ms mant are special 1911da177e4SLinus Torvalds tstl %d0 1921da177e4SLinus Torvalds bnes chkberr |if d0 is true, check for incorrect operr 1931da177e4SLinus Torvalds movel #0x80000000,%d0 |store special case result 1941da177e4SLinus Torvalds bsr operr_store 1951da177e4SLinus Torvalds bra not_enabled |clean and exit 1961da177e4SLinus Torvalds| 1971da177e4SLinus Torvalds| CHECK FOR INCORRECTLY GENERATED OPERR EXCEPTION HERE 1981da177e4SLinus Torvalds| 1991da177e4SLinus Torvaldschkberr: 2001da177e4SLinus Torvalds movew FPTEMP_EX(%a6),%d0 2011da177e4SLinus Torvalds andw #0x7FFF,%d0 |ignore sign bit 2021da177e4SLinus Torvalds cmpw #0x3FFE,%d0 |this is the only possible exponent value 2031da177e4SLinus Torvalds bnes store_max 2041da177e4SLinus Torvalds movel FPTEMP_LO(%a6),%d0 2051da177e4SLinus Torvalds asll #8,%d0 2061da177e4SLinus Torvalds swap %d0 2071da177e4SLinus Torvalds bsr operr_store 2081da177e4SLinus Torvalds bra not_enabled 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds| 2111da177e4SLinus Torvalds| This operr condition is not of the special case. Set operr 2121da177e4SLinus Torvalds| and aiop and write the portion of the nan to memory for the 2131da177e4SLinus Torvalds| given size. 2141da177e4SLinus Torvalds| 2151da177e4SLinus Torvaldsoperr_nan: 2161da177e4SLinus Torvalds orl #opaop_mask,USER_FPSR(%a6) |set operr & aiop 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds movel ETEMP_HI(%a6),%d0 |output will be from upper 32 bits 2191da177e4SLinus Torvalds bsr operr_store 2201da177e4SLinus Torvalds bra end_operr 2211da177e4SLinus Torvalds| 2221da177e4SLinus Torvalds| Store_max loads the max pos or negative for the size, sets 2231da177e4SLinus Torvalds| the operr and aiop bits, and clears inex and ainex, incorrectly 2241da177e4SLinus Torvalds| set by the 040. 2251da177e4SLinus Torvalds| 2261da177e4SLinus Torvaldsstore_max: 2271da177e4SLinus Torvalds orl #opaop_mask,USER_FPSR(%a6) |set operr & aiop 2281da177e4SLinus Torvalds bclrb #inex2_bit,FPSR_EXCEPT(%a6) 2291da177e4SLinus Torvalds bclrb #ainex_bit,FPSR_AEXCEPT(%a6) 2301da177e4SLinus Torvalds fmovel #0,%FPSR 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds tstw FPTEMP_EX(%a6) |check sign 2331da177e4SLinus Torvalds blts load_neg 2341da177e4SLinus Torvalds movel #0x7fffffff,%d0 2351da177e4SLinus Torvalds bsr operr_store 2361da177e4SLinus Torvalds bra end_operr 2371da177e4SLinus Torvaldsload_neg: 2381da177e4SLinus Torvalds movel #0x80000000,%d0 2391da177e4SLinus Torvalds bsr operr_store 2401da177e4SLinus Torvalds bra end_operr 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds| 2431da177e4SLinus Torvalds| This routine stores the data in d0, for the given size in d1, 2441da177e4SLinus Torvalds| to memory or data register as required. A read of the fline 2451da177e4SLinus Torvalds| is required to determine the destination. 2461da177e4SLinus Torvalds| 2471da177e4SLinus Torvaldsoperr_store: 2481da177e4SLinus Torvalds movel %d0,L_SCR1(%a6) |move write data to L_SCR1 2491da177e4SLinus Torvalds movel %d1,-(%a7) |save register size 2501da177e4SLinus Torvalds bsrl get_fline |fline returned in d0 2511da177e4SLinus Torvalds movel (%a7)+,%d1 2521da177e4SLinus Torvalds bftst %d0{#26:#3} |if mode is zero, dest is Dn 2531da177e4SLinus Torvalds bnes dest_mem 2541da177e4SLinus Torvalds| 2551da177e4SLinus Torvalds| Destination is Dn. Get register number from d0. Data is on 2561da177e4SLinus Torvalds| the stack at (a7). D1 has size: 1=byte,2=word,4=long/single 2571da177e4SLinus Torvalds| 2581da177e4SLinus Torvalds andil #7,%d0 |isolate register number 2591da177e4SLinus Torvalds cmpil #4,%d1 2601da177e4SLinus Torvalds beqs op_long |the most frequent case 2611da177e4SLinus Torvalds cmpil #2,%d1 2621da177e4SLinus Torvalds bnes op_con 2631da177e4SLinus Torvalds orl #8,%d0 2641da177e4SLinus Torvalds bras op_con 2651da177e4SLinus Torvaldsop_long: 2661da177e4SLinus Torvalds orl #0x10,%d0 2671da177e4SLinus Torvaldsop_con: 2681da177e4SLinus Torvalds movel %d0,%d1 |format size:reg for reg_dest 2691da177e4SLinus Torvalds bral reg_dest |call to reg_dest returns to caller 2701da177e4SLinus Torvalds| ;of operr_store 2711da177e4SLinus Torvalds| 2721da177e4SLinus Torvalds| Destination is memory. Get <ea> from integer exception frame 2731da177e4SLinus Torvalds| and call mem_write. 2741da177e4SLinus Torvalds| 2751da177e4SLinus Torvaldsdest_mem: 2761da177e4SLinus Torvalds leal L_SCR1(%a6),%a0 |put ptr to write data in a0 2771da177e4SLinus Torvalds movel EXC_EA(%a6),%a1 |put user destination address in a1 2781da177e4SLinus Torvalds movel %d1,%d0 |put size in d0 2791da177e4SLinus Torvalds bsrl mem_write 2801da177e4SLinus Torvalds rts 2811da177e4SLinus Torvalds| 2821da177e4SLinus Torvalds| Check the exponent for $c000 and the upper 32 bits of the 2831da177e4SLinus Torvalds| mantissa for $ffffffff. If both are true, return d0 clr 2841da177e4SLinus Torvalds| and store the lower n bits of the least lword of FPTEMP 2851da177e4SLinus Torvalds| to d0 for write out. If not, it is a real operr, and set d0. 2861da177e4SLinus Torvalds| 2871da177e4SLinus Torvaldscheck_upper: 2881da177e4SLinus Torvalds cmpil #0xffffffff,FPTEMP_HI(%a6) |check if first byte is all 1's 2891da177e4SLinus Torvalds bnes true_operr |if not all 1's then was true operr 2901da177e4SLinus Torvalds cmpiw #0xc000,FPTEMP_EX(%a6) |check if incorrectly signalled 2911da177e4SLinus Torvalds beqs not_true_operr |branch if not true operr 2921da177e4SLinus Torvalds cmpiw #0xbfff,FPTEMP_EX(%a6) |check if incorrectly signalled 2931da177e4SLinus Torvalds beqs not_true_operr |branch if not true operr 2941da177e4SLinus Torvaldstrue_operr: 2951da177e4SLinus Torvalds movel #1,%d0 |signal real operr 2961da177e4SLinus Torvalds rts 2971da177e4SLinus Torvaldsnot_true_operr: 2981da177e4SLinus Torvalds clrl %d0 |signal no real operr 2991da177e4SLinus Torvalds rts 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds| 3021da177e4SLinus Torvalds| End_operr tests for operr enabled. If not, it cleans up the stack 3031da177e4SLinus Torvalds| and does an rte. If enabled, it cleans up the stack and branches 3041da177e4SLinus Torvalds| to the kernel operr handler with only the integer exception 3051da177e4SLinus Torvalds| frame on the stack and the fpu in the original exceptional state 3061da177e4SLinus Torvalds| with correct data written to the destination. 3071da177e4SLinus Torvalds| 3081da177e4SLinus Torvaldsend_operr: 3091da177e4SLinus Torvalds btstb #operr_bit,FPCR_ENABLE(%a6) 3101da177e4SLinus Torvalds beqs not_enabled 3111da177e4SLinus Torvaldsenabled: 3121da177e4SLinus Torvalds moveml USER_DA(%a6),%d0-%d1/%a0-%a1 3131da177e4SLinus Torvalds fmovemx USER_FP0(%a6),%fp0-%fp3 3141da177e4SLinus Torvalds fmoveml USER_FPCR(%a6),%fpcr/%fpsr/%fpiar 3151da177e4SLinus Torvalds frestore (%a7)+ 3161da177e4SLinus Torvalds unlk %a6 3171da177e4SLinus Torvalds bral real_operr 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvaldsnot_enabled: 3201da177e4SLinus Torvalds| 3211da177e4SLinus Torvalds| It is possible to have either inex2 or inex1 exceptions with the 3221da177e4SLinus Torvalds| operr. If the inex enable bit is set in the FPCR, and either 3231da177e4SLinus Torvalds| inex2 or inex1 occurred, we must clean up and branch to the 3241da177e4SLinus Torvalds| real inex handler. 3251da177e4SLinus Torvalds| 3261da177e4SLinus Torvaldsck_inex: 3271da177e4SLinus Torvalds moveb FPCR_ENABLE(%a6),%d0 3281da177e4SLinus Torvalds andb FPSR_EXCEPT(%a6),%d0 3291da177e4SLinus Torvalds andib #0x3,%d0 3301da177e4SLinus Torvalds beq operr_exit 3311da177e4SLinus Torvalds| 3321da177e4SLinus Torvalds| Inexact enabled and reported, and we must take an inexact exception. 3331da177e4SLinus Torvalds| 3341da177e4SLinus Torvaldstake_inex: 3351da177e4SLinus Torvalds moveb #INEX_VEC,EXC_VEC+1(%a6) 3361da177e4SLinus Torvalds movel USER_FPSR(%a6),FPSR_SHADOW(%a6) 3371da177e4SLinus Torvalds orl #sx_mask,E_BYTE(%a6) 3381da177e4SLinus Torvalds moveml USER_DA(%a6),%d0-%d1/%a0-%a1 3391da177e4SLinus Torvalds fmovemx USER_FP0(%a6),%fp0-%fp3 3401da177e4SLinus Torvalds fmoveml USER_FPCR(%a6),%fpcr/%fpsr/%fpiar 3411da177e4SLinus Torvalds frestore (%a7)+ 3421da177e4SLinus Torvalds unlk %a6 3431da177e4SLinus Torvalds bral real_inex 3441da177e4SLinus Torvalds| 3451da177e4SLinus Torvalds| Since operr is only an E1 exception, there is no need to frestore 3461da177e4SLinus Torvalds| any state back to the fpu. 3471da177e4SLinus Torvalds| 3481da177e4SLinus Torvaldsoperr_exit: 3491da177e4SLinus Torvalds moveml USER_DA(%a6),%d0-%d1/%a0-%a1 3501da177e4SLinus Torvalds fmovemx USER_FP0(%a6),%fp0-%fp3 3511da177e4SLinus Torvalds fmoveml USER_FPCR(%a6),%fpcr/%fpsr/%fpiar 3521da177e4SLinus Torvalds unlk %a6 3531da177e4SLinus Torvalds bral fpsp_done 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds |end 356