summaryrefslogtreecommitdiff
path: root/sys/src/libc
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libc
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libc')
-rwxr-xr-xsys/src/libc/386/387/asin.s40
-rwxr-xr-xsys/src/libc/386/387/atan.s5
-rwxr-xr-xsys/src/libc/386/387/atan2.s5
-rwxr-xr-xsys/src/libc/386/387/exp.s13
-rwxr-xr-xsys/src/libc/386/387/log.s31
-rwxr-xr-xsys/src/libc/386/387/mkfile14
-rwxr-xr-xsys/src/libc/386/387/sin.s9
-rwxr-xr-xsys/src/libc/386/387/sqrt.s16
-rwxr-xr-xsys/src/libc/386/387/tan.s5
-rwxr-xr-xsys/src/libc/386/argv0.s4
-rwxr-xr-xsys/src/libc/386/atom.s75
-rwxr-xr-xsys/src/libc/386/cycles.s8
-rwxr-xr-xsys/src/libc/386/getcallerpc.s4
-rwxr-xr-xsys/src/libc/386/getfcr.s27
-rwxr-xr-xsys/src/libc/386/main9.s21
-rwxr-xr-xsys/src/libc/386/main9p.s38
-rwxr-xr-xsys/src/libc/386/memccpy.s53
-rwxr-xr-xsys/src/libc/386/memchr.s23
-rwxr-xr-xsys/src/libc/386/memcmp.s47
-rwxr-xr-xsys/src/libc/386/memcpy.s57
-rwxr-xr-xsys/src/libc/386/memmove.s71
-rwxr-xr-xsys/src/libc/386/memset.s35
-rwxr-xr-xsys/src/libc/386/mkfile42
-rwxr-xr-xsys/src/libc/386/muldiv.s12
-rwxr-xr-xsys/src/libc/386/notejmp.c16
-rwxr-xr-xsys/src/libc/386/setjmp.s18
-rwxr-xr-xsys/src/libc/386/sqrt.s16
-rwxr-xr-xsys/src/libc/386/strcat.s43
-rwxr-xr-xsys/src/libc/386/strchr.s38
-rwxr-xr-xsys/src/libc/386/strcpy.s35
-rwxr-xr-xsys/src/libc/386/strlen.s16
-rwxr-xr-xsys/src/libc/386/tas.s6
-rwxr-xr-xsys/src/libc/386/vlop.s54
-rwxr-xr-xsys/src/libc/386/vlrt.c746
-rwxr-xr-xsys/src/libc/68000/argv0.s4
-rwxr-xr-xsys/src/libc/68000/cycles.c7
-rwxr-xr-xsys/src/libc/68000/doprint.c617
-rwxr-xr-xsys/src/libc/68000/getfcr.s19
-rwxr-xr-xsys/src/libc/68000/main9.s18
-rwxr-xr-xsys/src/libc/68000/main9p.s31
-rwxr-xr-xsys/src/libc/68000/memccpy.s30
-rwxr-xr-xsys/src/libc/68000/memchr.s15
-rwxr-xr-xsys/src/libc/68000/memcmp.s18
-rwxr-xr-xsys/src/libc/68000/memcpy.s120
-rwxr-xr-xsys/src/libc/68000/memmove.s120
-rwxr-xr-xsys/src/libc/68000/memset.s57
-rwxr-xr-xsys/src/libc/68000/mkfile40
-rwxr-xr-xsys/src/libc/68000/muldivrt.s172
-rwxr-xr-xsys/src/libc/68000/notejmp.c17
-rwxr-xr-xsys/src/libc/68000/scale.s4
-rwxr-xr-xsys/src/libc/68000/setjmp.s15
-rwxr-xr-xsys/src/libc/68000/sqrt.s3
-rwxr-xr-xsys/src/libc/68000/strcat.s15
-rwxr-xr-xsys/src/libc/68000/strchr.s27
-rwxr-xr-xsys/src/libc/68000/strcmp.s20
-rwxr-xr-xsys/src/libc/68000/strcpy.s10
-rwxr-xr-xsys/src/libc/68000/strlen.s18
-rwxr-xr-xsys/src/libc/68000/vlrt.c771
-rwxr-xr-xsys/src/libc/68020/68881/acos.s3
-rwxr-xr-xsys/src/libc/68020/68881/asin.s3
-rwxr-xr-xsys/src/libc/68020/68881/atan.s3
-rwxr-xr-xsys/src/libc/68020/68881/cos.s3
-rwxr-xr-xsys/src/libc/68020/68881/cosh.s3
-rwxr-xr-xsys/src/libc/68020/68881/exp.s3
-rwxr-xr-xsys/src/libc/68020/68881/fabs.s3
-rwxr-xr-xsys/src/libc/68020/68881/log.s3
-rwxr-xr-xsys/src/libc/68020/68881/log10.s3
-rwxr-xr-xsys/src/libc/68020/68881/mkfile31
-rwxr-xr-xsys/src/libc/68020/68881/pow10.s3
-rwxr-xr-xsys/src/libc/68020/68881/sin.s3
-rwxr-xr-xsys/src/libc/68020/68881/sinh.s3
-rwxr-xr-xsys/src/libc/68020/68881/sqrt.s3
-rwxr-xr-xsys/src/libc/68020/68881/tan.s3
-rwxr-xr-xsys/src/libc/68020/68881/tanh.s3
-rwxr-xr-xsys/src/libc/68020/argv0.s4
-rwxr-xr-xsys/src/libc/68020/getcallerpc.s3
-rwxr-xr-xsys/src/libc/68020/getfcr.s19
-rwxr-xr-xsys/src/libc/68020/main9.s19
-rwxr-xr-xsys/src/libc/68020/main9p.s32
-rwxr-xr-xsys/src/libc/68020/memccpy.s30
-rwxr-xr-xsys/src/libc/68020/memchr.s15
-rwxr-xr-xsys/src/libc/68020/memcmp.s18
-rwxr-xr-xsys/src/libc/68020/memcpy.s98
-rwxr-xr-xsys/src/libc/68020/memmove.s99
-rwxr-xr-xsys/src/libc/68020/memset.s47
-rwxr-xr-xsys/src/libc/68020/mkfile53
-rwxr-xr-xsys/src/libc/68020/notejmp.c17
-rwxr-xr-xsys/src/libc/68020/scale.s4
-rwxr-xr-xsys/src/libc/68020/setjmp.s15
-rwxr-xr-xsys/src/libc/68020/sqrt.s3
-rwxr-xr-xsys/src/libc/68020/strcat.s15
-rwxr-xr-xsys/src/libc/68020/strchr.s27
-rwxr-xr-xsys/src/libc/68020/strcmp.s20
-rwxr-xr-xsys/src/libc/68020/strcpy.s10
-rwxr-xr-xsys/src/libc/68020/strlen.s18
-rwxr-xr-xsys/src/libc/68020/tas.s9
-rwxr-xr-xsys/src/libc/68020/vlop.s23
-rwxr-xr-xsys/src/libc/68020/vlrt.c730
-rwxr-xr-xsys/src/libc/9sys/abort.c8
-rwxr-xr-xsys/src/libc/9sys/access.c33
-rwxr-xr-xsys/src/libc/9sys/announce.c274
-rwxr-xr-xsys/src/libc/9sys/convD2M.c95
-rwxr-xr-xsys/src/libc/9sys/convM2D.c94
-rwxr-xr-xsys/src/libc/9sys/convM2S.c315
-rwxr-xr-xsys/src/libc/9sys/convS2M.c389
-rwxr-xr-xsys/src/libc/9sys/cputime.c17
-rwxr-xr-xsys/src/libc/9sys/ctime.c304
-rwxr-xr-xsys/src/libc/9sys/dial.c492
-rwxr-xr-xsys/src/libc/9sys/dirfstat.c37
-rwxr-xr-xsys/src/libc/9sys/dirfwstat.c19
-rwxr-xr-xsys/src/libc/9sys/dirmodefmt.c48
-rwxr-xr-xsys/src/libc/9sys/dirread.c97
-rwxr-xr-xsys/src/libc/9sys/dirstat.c37
-rwxr-xr-xsys/src/libc/9sys/dirwstat.c19
-rwxr-xr-xsys/src/libc/9sys/fcallfmt.c234
-rwxr-xr-xsys/src/libc/9sys/fork.c8
-rwxr-xr-xsys/src/libc/9sys/getenv.c36
-rwxr-xr-xsys/src/libc/9sys/getnetconninfo.c133
-rwxr-xr-xsys/src/libc/9sys/getpid.c17
-rwxr-xr-xsys/src/libc/9sys/getppid.c17
-rwxr-xr-xsys/src/libc/9sys/getwd.c19
-rwxr-xr-xsys/src/libc/9sys/iounit.c27
-rwxr-xr-xsys/src/libc/9sys/mkfile64
-rwxr-xr-xsys/src/libc/9sys/nsec.c75
-rwxr-xr-xsys/src/libc/9sys/nulldir.c9
-rwxr-xr-xsys/src/libc/9sys/postnote.c32
-rwxr-xr-xsys/src/libc/9sys/privalloc.c45
-rwxr-xr-xsys/src/libc/9sys/pushssl.c44
-rwxr-xr-xsys/src/libc/9sys/pushtls.c99
-rwxr-xr-xsys/src/libc/9sys/putenv.c26
-rwxr-xr-xsys/src/libc/9sys/qlock.c363
-rwxr-xr-xsys/src/libc/9sys/read.c8
-rwxr-xr-xsys/src/libc/9sys/read9pmsg.c31
-rwxr-xr-xsys/src/libc/9sys/readv.c49
-rwxr-xr-xsys/src/libc/9sys/rerrstr.c13
-rwxr-xr-xsys/src/libc/9sys/sbrk.c35
-rwxr-xr-xsys/src/libc/9sys/setnetmtpt.c16
-rwxr-xr-xsys/src/libc/9sys/sysfatal.c28
-rwxr-xr-xsys/src/libc/9sys/syslog.c117
-rwxr-xr-xsys/src/libc/9sys/sysname.c21
-rwxr-xr-xsys/src/libc/9sys/time.c51
-rwxr-xr-xsys/src/libc/9sys/times.c60
-rwxr-xr-xsys/src/libc/9sys/tm2sec.c194
-rwxr-xr-xsys/src/libc/9sys/truerand.c17
-rwxr-xr-xsys/src/libc/9sys/wait.c32
-rwxr-xr-xsys/src/libc/9sys/waitpid.c21
-rwxr-xr-xsys/src/libc/9sys/werrstr.c14
-rwxr-xr-xsys/src/libc/9sys/write.c8
-rwxr-xr-xsys/src/libc/9sys/writev.c42
-rwxr-xr-xsys/src/libc/9syscall/mkfile145
-rwxr-xr-xsys/src/libc/9syscall/sys.h50
-rwxr-xr-xsys/src/libc/alpha/_seek.c14
-rwxr-xr-xsys/src/libc/alpha/argv0.s4
-rwxr-xr-xsys/src/libc/alpha/atom.s67
-rwxr-xr-xsys/src/libc/alpha/cycles.c7
-rwxr-xr-xsys/src/libc/alpha/divl.s189
-rwxr-xr-xsys/src/libc/alpha/divq.s191
-rwxr-xr-xsys/src/libc/alpha/getcallerpc.s4
-rwxr-xr-xsys/src/libc/alpha/getfcr.s57
-rwxr-xr-xsys/src/libc/alpha/main9.s27
-rwxr-xr-xsys/src/libc/alpha/main9p.s37
-rwxr-xr-xsys/src/libc/alpha/memmove.s201
-rwxr-xr-xsys/src/libc/alpha/memset.s61
-rwxr-xr-xsys/src/libc/alpha/mkfile33
-rwxr-xr-xsys/src/libc/alpha/notejmp.c16
-rwxr-xr-xsys/src/libc/alpha/setjmp.s14
-rwxr-xr-xsys/src/libc/alpha/tas.s10
-rwxr-xr-xsys/src/libc/arm/argv0.s4
-rwxr-xr-xsys/src/libc/arm/atom.s55
-rwxr-xr-xsys/src/libc/arm/cas.s28
-rwxr-xr-xsys/src/libc/arm/cycles.c7
-rwxr-xr-xsys/src/libc/arm/div.s118
-rwxr-xr-xsys/src/libc/arm/doprint.xc617
-rwxr-xr-xsys/src/libc/arm/getcallerpc.s3
-rwxr-xr-xsys/src/libc/arm/getfcr.s12
-rwxr-xr-xsys/src/libc/arm/main9.s29
-rwxr-xr-xsys/src/libc/arm/main9p.s42
-rwxr-xr-xsys/src/libc/arm/memmove.s212
-rwxr-xr-xsys/src/libc/arm/memset.s60
-rwxr-xr-xsys/src/libc/arm/mkfile36
-rwxr-xr-xsys/src/libc/arm/notejmp.c16
-rwxr-xr-xsys/src/libc/arm/setjmp.s19
-rwxr-xr-xsys/src/libc/arm/strchr.s56
-rwxr-xr-xsys/src/libc/arm/strcmp.s67
-rwxr-xr-xsys/src/libc/arm/strcpy.s46
-rwxr-xr-xsys/src/libc/arm/tas.s5
-rwxr-xr-xsys/src/libc/arm/vlop.s13
-rwxr-xr-xsys/src/libc/arm/vlrt.c708
-rwxr-xr-xsys/src/libc/fmt/dofmt.c523
-rwxr-xr-xsys/src/libc/fmt/dorfmt.c45
-rwxr-xr-xsys/src/libc/fmt/errfmt.c12
-rwxr-xr-xsys/src/libc/fmt/fltfmt.c318
-rwxr-xr-xsys/src/libc/fmt/fmt.c202
-rwxr-xr-xsys/src/libc/fmt/fmtdef.h85
-rwxr-xr-xsys/src/libc/fmt/fmtfd.c31
-rwxr-xr-xsys/src/libc/fmt/fmtlock.c16
-rwxr-xr-xsys/src/libc/fmt/fmtprint.c32
-rwxr-xr-xsys/src/libc/fmt/fmtquote.c249
-rwxr-xr-xsys/src/libc/fmt/fmtrune.c25
-rwxr-xr-xsys/src/libc/fmt/fmtstr.c11
-rwxr-xr-xsys/src/libc/fmt/fmtvprint.c31
-rwxr-xr-xsys/src/libc/fmt/fprint.c14
-rwxr-xr-xsys/src/libc/fmt/mkfile46
-rwxr-xr-xsys/src/libc/fmt/print.c14
-rwxr-xr-xsys/src/libc/fmt/runefmtstr.c11
-rwxr-xr-xsys/src/libc/fmt/runeseprint.c14
-rwxr-xr-xsys/src/libc/fmt/runesmprint.c14
-rwxr-xr-xsys/src/libc/fmt/runesnprint.c15
-rwxr-xr-xsys/src/libc/fmt/runesprint.c14
-rwxr-xr-xsys/src/libc/fmt/runevseprint.c23
-rwxr-xr-xsys/src/libc/fmt/runevsmprint.c70
-rwxr-xr-xsys/src/libc/fmt/runevsnprint.c22
-rwxr-xr-xsys/src/libc/fmt/seprint.c14
-rwxr-xr-xsys/src/libc/fmt/smprint.c15
-rwxr-xr-xsys/src/libc/fmt/snprint.c15
-rwxr-xr-xsys/src/libc/fmt/sprint.c14
-rwxr-xr-xsys/src/libc/fmt/vfprint.c34
-rwxr-xr-xsys/src/libc/fmt/vseprint.c23
-rwxr-xr-xsys/src/libc/fmt/vsmprint.c70
-rwxr-xr-xsys/src/libc/fmt/vsnprint.c22
-rwxr-xr-xsys/src/libc/mips/argv0.s4
-rwxr-xr-xsys/src/libc/mips/atom.s57
-rwxr-xr-xsys/src/libc/mips/cycles.c7
-rwxr-xr-xsys/src/libc/mips/getcallerpc.s4
-rwxr-xr-xsys/src/libc/mips/getfcr.s15
-rwxr-xr-xsys/src/libc/mips/lock.c169
-rwxr-xr-xsys/src/libc/mips/main9.s25
-rwxr-xr-xsys/src/libc/mips/main9p.s36
-rwxr-xr-xsys/src/libc/mips/memccpy.s20
-rwxr-xr-xsys/src/libc/mips/memchr.s39
-rwxr-xr-xsys/src/libc/mips/memcmp.s114
-rwxr-xr-xsys/src/libc/mips/memmove.s237
-rwxr-xr-xsys/src/libc/mips/memset.s88
-rwxr-xr-xsys/src/libc/mips/mkfile40
-rwxr-xr-xsys/src/libc/mips/notejmp.c16
-rwxr-xr-xsys/src/libc/mips/setjmp.s14
-rwxr-xr-xsys/src/libc/mips/sqrt.c103
-rwxr-xr-xsys/src/libc/mips/strchr.s63
-rwxr-xr-xsys/src/libc/mips/strcmp.s21
-rwxr-xr-xsys/src/libc/mips/strcpy.s96
-rwxr-xr-xsys/src/libc/mips/tas.s33
-rwxr-xr-xsys/src/libc/mips/vlop.s17
-rwxr-xr-xsys/src/libc/mips/vlrt.c723
-rwxr-xr-xsys/src/libc/mkfile51
-rwxr-xr-xsys/src/libc/port/_assert.c13
-rwxr-xr-xsys/src/libc/port/abs.c18
-rwxr-xr-xsys/src/libc/port/asin.c40
-rwxr-xr-xsys/src/libc/port/atan.c78
-rwxr-xr-xsys/src/libc/port/atan2.c24
-rwxr-xr-xsys/src/libc/port/atexit.c60
-rwxr-xr-xsys/src/libc/port/atnotify.c58
-rwxr-xr-xsys/src/libc/port/atof.c8
-rwxr-xr-xsys/src/libc/port/atol.c53
-rwxr-xr-xsys/src/libc/port/atoll.c8
-rwxr-xr-xsys/src/libc/port/charstod.c81
-rwxr-xr-xsys/src/libc/port/cistrcmp.c26
-rwxr-xr-xsys/src/libc/port/cistrncmp.c28
-rwxr-xr-xsys/src/libc/port/cistrstr.c23
-rwxr-xr-xsys/src/libc/port/cleanname.c63
-rwxr-xr-xsys/src/libc/port/crypt.c68
-rwxr-xr-xsys/src/libc/port/ctype.c25
-rwxr-xr-xsys/src/libc/port/encodefmt.c77
-rwxr-xr-xsys/src/libc/port/execl.c9
-rwxr-xr-xsys/src/libc/port/exp.c40
-rwxr-xr-xsys/src/libc/port/fabs.c11
-rwxr-xr-xsys/src/libc/port/floor.c27
-rwxr-xr-xsys/src/libc/port/fmod.c31
-rwxr-xr-xsys/src/libc/port/frand.c17
-rwxr-xr-xsys/src/libc/port/frexp.c105
-rwxr-xr-xsys/src/libc/port/getcallerpc.c8
-rwxr-xr-xsys/src/libc/port/getfields.c37
-rwxr-xr-xsys/src/libc/port/getuser.c21
-rwxr-xr-xsys/src/libc/port/hangup.c12
-rwxr-xr-xsys/src/libc/port/hypot.c41
-rwxr-xr-xsys/src/libc/port/lnrand.c18
-rwxr-xr-xsys/src/libc/port/lock.c41
-rwxr-xr-xsys/src/libc/port/log.c58
-rwxr-xr-xsys/src/libc/port/lrand.c83
-rwxr-xr-xsys/src/libc/port/malloc.acid366
-rwxr-xr-xsys/src/libc/port/malloc.c338
-rwxr-xr-xsys/src/libc/port/memccpy.c18
-rwxr-xr-xsys/src/libc/port/memchr.c17
-rwxr-xr-xsys/src/libc/port/memcmp.c23
-rwxr-xr-xsys/src/libc/port/memmove.c35
-rwxr-xr-xsys/src/libc/port/memset.c15
-rwxr-xr-xsys/src/libc/port/mkfile127
-rwxr-xr-xsys/src/libc/port/mktemp.c31
-rwxr-xr-xsys/src/libc/port/muldiv.c38
-rwxr-xr-xsys/src/libc/port/nan.c54
-rwxr-xr-xsys/src/libc/port/needsrcquote.c12
-rwxr-xr-xsys/src/libc/port/netcrypt.c18
-rwxr-xr-xsys/src/libc/port/netmkaddr.c52
-rwxr-xr-xsys/src/libc/port/nrand.c21
-rwxr-xr-xsys/src/libc/port/ntruerand.c23
-rwxr-xr-xsys/src/libc/port/perror.c15
-rwxr-xr-xsys/src/libc/port/pool.acid628
-rwxr-xr-xsys/src/libc/port/pool.c1466
-rwxr-xr-xsys/src/libc/port/pow.c69
-rwxr-xr-xsys/src/libc/port/pow10.c49
-rwxr-xr-xsys/src/libc/port/profile.c280
-rwxr-xr-xsys/src/libc/port/qsort.c124
-rwxr-xr-xsys/src/libc/port/quote.c135
-rwxr-xr-xsys/src/libc/port/rand.c8
-rwxr-xr-xsys/src/libc/port/readn.c22
-rwxr-xr-xsys/src/libc/port/reduce16
-rwxr-xr-xsys/src/libc/port/rune.c162
-rwxr-xr-xsys/src/libc/port/runestrcat.c10
-rwxr-xr-xsys/src/libc/port/runestrchr.c20
-rwxr-xr-xsys/src/libc/port/runestrcmp.c20
-rwxr-xr-xsys/src/libc/port/runestrcpy.c13
-rwxr-xr-xsys/src/libc/port/runestrdup.c14
-rwxr-xr-xsys/src/libc/port/runestrecpy.c17
-rwxr-xr-xsys/src/libc/port/runestrlen.c9
-rwxr-xr-xsys/src/libc/port/runestrncat.c17
-rwxr-xr-xsys/src/libc/port/runestrncmp.c22
-rwxr-xr-xsys/src/libc/port/runestrncpy.c18
-rwxr-xr-xsys/src/libc/port/runestrrchr.c15
-rwxr-xr-xsys/src/libc/port/runestrstr.c29
-rwxr-xr-xsys/src/libc/port/runetype.c1181
-rwxr-xr-xsys/src/libc/port/sin.c68
-rwxr-xr-xsys/src/libc/port/sinh.c62
-rwxr-xr-xsys/src/libc/port/sqrt.c54
-rwxr-xr-xsys/src/libc/port/strcat.c10
-rwxr-xr-xsys/src/libc/port/strchr.c20
-rwxr-xr-xsys/src/libc/port/strcmp.c20
-rwxr-xr-xsys/src/libc/port/strcpy.c16
-rwxr-xr-xsys/src/libc/port/strcspn.c21
-rwxr-xr-xsys/src/libc/port/strdup.c14
-rwxr-xr-xsys/src/libc/port/strecpy.c17
-rwxr-xr-xsys/src/libc/port/strlen.c9
-rwxr-xr-xsys/src/libc/port/strncat.c19
-rwxr-xr-xsys/src/libc/port/strncmp.c22
-rwxr-xr-xsys/src/libc/port/strncpy.c18
-rwxr-xr-xsys/src/libc/port/strpbrk.c22
-rwxr-xr-xsys/src/libc/port/strrchr.c15
-rwxr-xr-xsys/src/libc/port/strspn.c18
-rwxr-xr-xsys/src/libc/port/strstr.c29
-rwxr-xr-xsys/src/libc/port/strtod.c520
-rwxr-xr-xsys/src/libc/port/strtok.c30
-rwxr-xr-xsys/src/libc/port/strtol.c101
-rwxr-xr-xsys/src/libc/port/strtoll.c101
-rwxr-xr-xsys/src/libc/port/strtoul.c97
-rwxr-xr-xsys/src/libc/port/strtoull.c97
-rwxr-xr-xsys/src/libc/port/tan.c67
-rwxr-xr-xsys/src/libc/port/tanh.c25
-rwxr-xr-xsys/src/libc/port/tokenize.c107
-rwxr-xr-xsys/src/libc/port/toupper.c19
-rwxr-xr-xsys/src/libc/port/u16.c53
-rwxr-xr-xsys/src/libc/port/u32.c110
-rwxr-xr-xsys/src/libc/port/u64.c127
-rwxr-xr-xsys/src/libc/port/utfecpy.c21
-rwxr-xr-xsys/src/libc/port/utflen.c22
-rwxr-xr-xsys/src/libc/port/utfnlen.c26
-rwxr-xr-xsys/src/libc/port/utfrrune.c30
-rwxr-xr-xsys/src/libc/port/utfrune.c29
-rwxr-xr-xsys/src/libc/port/utfutf.c26
-rwxr-xr-xsys/src/libc/power/argv0.s4
-rwxr-xr-xsys/src/libc/power/atom.s65
-rwxr-xr-xsys/src/libc/power/cycles.s17
-rwxr-xr-xsys/src/libc/power/getcallerpc.s4
-rwxr-xr-xsys/src/libc/power/getfcr.s28
-rwxr-xr-xsys/src/libc/power/main9.s25
-rwxr-xr-xsys/src/libc/power/main9p.s37
-rwxr-xr-xsys/src/libc/power/memccpy.s23
-rwxr-xr-xsys/src/libc/power/memcmp.s110
-rwxr-xr-xsys/src/libc/power/memmove.s170
-rwxr-xr-xsys/src/libc/power/memset.s73
-rwxr-xr-xsys/src/libc/power/mkfile37
-rwxr-xr-xsys/src/libc/power/notejmp.c22
-rwxr-xr-xsys/src/libc/power/setjmp.s26
-rwxr-xr-xsys/src/libc/power/sqrt.c103
-rwxr-xr-xsys/src/libc/power/strcmp.s21
-rwxr-xr-xsys/src/libc/power/strncmp.s29
-rwxr-xr-xsys/src/libc/power/tas.s14
-rwxr-xr-xsys/src/libc/power/vlop.s132
-rwxr-xr-xsys/src/libc/power/vlrt.c254
-rwxr-xr-xsys/src/libc/sparc/argv0.s4
-rwxr-xr-xsys/src/libc/sparc/getcallerpc.s3
-rwxr-xr-xsys/src/libc/sparc/getfcr.s27
-rwxr-xr-xsys/src/libc/sparc/main9.s31
-rwxr-xr-xsys/src/libc/sparc/main9p.s44
-rwxr-xr-xsys/src/libc/sparc/memccpy.s27
-rwxr-xr-xsys/src/libc/sparc/memchr.s26
-rwxr-xr-xsys/src/libc/sparc/memcmp.s120
-rwxr-xr-xsys/src/libc/sparc/memmove.s162
-rwxr-xr-xsys/src/libc/sparc/memset.s88
-rwxr-xr-xsys/src/libc/sparc/mkfile39
-rwxr-xr-xsys/src/libc/sparc/muldivrt.s310
-rwxr-xr-xsys/src/libc/sparc/notejmp.c23
-rwxr-xr-xsys/src/libc/sparc/setjmp.s25
-rwxr-xr-xsys/src/libc/sparc/sqrt.c103
-rwxr-xr-xsys/src/libc/sparc/strchr.s73
-rwxr-xr-xsys/src/libc/sparc/strcmp.s27
-rwxr-xr-xsys/src/libc/sparc/strcpy.s84
-rwxr-xr-xsys/src/libc/sparc/tas.s7
-rwxr-xr-xsys/src/libc/sparc/vlop.s112
-rwxr-xr-xsys/src/libc/sparc/vlrt.c722
397 files changed, 28984 insertions, 0 deletions
diff --git a/sys/src/libc/386/387/asin.s b/sys/src/libc/386/387/asin.s
new file mode 100755
index 000000000..6c91d1fe3
--- /dev/null
+++ b/sys/src/libc/386/387/asin.s
@@ -0,0 +1,40 @@
+TEXT asin(SB), $0
+ FMOVD a+0(FP), F0 /* a */
+ FMOVD F0, F0 /* a,a */
+ FMULD F0, F0 /* a*a,a */
+ FLD1 /* 1,a*a,a */
+ FSUBRDP F0, F1 /* 1-a*a,a */
+
+ FTST
+ WAIT
+ FSTSW AX
+ SAHF
+ JLO bad
+
+ FSQRT /* sqrt(1-a*a),a */
+ FPATAN /* atan2(sqrt(1-a*a),a) */
+ RET
+
+TEXT acos(SB), $0
+ FMOVD a+0(FP), F0
+ FMOVD F0, F0
+ FMULD F0, F0
+ FLD1
+ FSUBRDP F0, F1
+
+ FTST
+ WAIT
+ FSTSW AX
+ SAHF
+ JLO bad
+
+ FSQRT
+ FXCHD F0, F1 /* identical except this */
+ FPATAN
+ RET
+
+bad:
+ FMOVDP F0, F0
+ FMOVDP F0, F0
+ CALL NaN(SB)
+ RET
diff --git a/sys/src/libc/386/387/atan.s b/sys/src/libc/386/387/atan.s
new file mode 100755
index 000000000..65496f2b6
--- /dev/null
+++ b/sys/src/libc/386/387/atan.s
@@ -0,0 +1,5 @@
+TEXT atan(SB), $0
+ FMOVD a+0(FP), F0
+ FLD1
+ FPATAN
+ RET
diff --git a/sys/src/libc/386/387/atan2.s b/sys/src/libc/386/387/atan2.s
new file mode 100755
index 000000000..00ca36e01
--- /dev/null
+++ b/sys/src/libc/386/387/atan2.s
@@ -0,0 +1,5 @@
+TEXT atan2(SB), $0
+ FMOVD a+0(FP), F0
+ FMOVD b+8(FP), F0
+ FPATAN
+ RET
diff --git a/sys/src/libc/386/387/exp.s b/sys/src/libc/386/387/exp.s
new file mode 100755
index 000000000..0c73465ab
--- /dev/null
+++ b/sys/src/libc/386/387/exp.s
@@ -0,0 +1,13 @@
+TEXT exp(SB), $0
+ FLDL2E
+ FMULD a+0(FP), F0 /* now we want 2^ this number */
+
+ FMOVD F0, F0 /* x, x */
+ FRNDINT /* ix, x -- this is best in round mode */
+ FSUBD F0, F1 /* ix, fx */
+ FXCHD F0, F1 /* fx, ix */
+ F2XM1 /* 2^fx-1, ix */
+ FADDD $1.0, F0 /* 2^fx, ix */
+ FSCALE /* 2^(fx+ix), ix */
+ FMOVDP F0, F1 /* 2^(fx+ix) == 2^x */
+ RET
diff --git a/sys/src/libc/386/387/log.s b/sys/src/libc/386/387/log.s
new file mode 100755
index 000000000..f53c05eb1
--- /dev/null
+++ b/sys/src/libc/386/387/log.s
@@ -0,0 +1,31 @@
+TEXT log(SB), $0
+ FMOVD $0.69314718055994530941, F0
+ FMOVD a+0(FP), F0
+
+ FTST
+ WAIT
+ FSTSW AX
+ SAHF
+ JLO bad
+
+ FYL2X
+ RET
+
+TEXT log10(SB), $0
+ FMOVD $0.30102999566398119521, F0
+ FMOVD a+0(FP), F0
+
+ FTST
+ WAIT
+ FSTSW AX
+ SAHF
+ JLO bad
+
+ FYL2X
+ RET
+
+bad:
+ FMOVDP F0, F0
+ FMOVDP F0, F0
+ CALL NaN(SB)
+ RET
diff --git a/sys/src/libc/386/387/mkfile b/sys/src/libc/386/387/mkfile
new file mode 100755
index 000000000..05343b63e
--- /dev/null
+++ b/sys/src/libc/386/387/mkfile
@@ -0,0 +1,14 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/lib387.a
+
+OFILES=\
+ atan.$O\
+ tan.$O\
+ atan2.$O\
+ exp.$O\
+ asin.$O\
+ log.$O\
+ sin.$O\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/386/387/sin.s b/sys/src/libc/386/387/sin.s
new file mode 100755
index 000000000..998a63ff4
--- /dev/null
+++ b/sys/src/libc/386/387/sin.s
@@ -0,0 +1,9 @@
+TEXT sin(SB), $0
+ FMOVD a+0(FP), F0
+ FSIN
+ RET
+
+TEXT cos(SB), $0
+ FMOVD a+0(FP), F0
+ FCOS
+ RET
diff --git a/sys/src/libc/386/387/sqrt.s b/sys/src/libc/386/387/sqrt.s
new file mode 100755
index 000000000..219a8ac6b
--- /dev/null
+++ b/sys/src/libc/386/387/sqrt.s
@@ -0,0 +1,16 @@
+TEXT sqrt(SB), $0
+ FMOVD a+0(FP), F0
+
+ FTST
+ WAIT
+ FSTSW AX
+ SAHF
+ JLO bad
+
+ FSQRT
+ RET
+
+bad:
+ FMOVDP F0, F0
+ CALL NaN(SB)
+ RET
diff --git a/sys/src/libc/386/387/tan.s b/sys/src/libc/386/387/tan.s
new file mode 100755
index 000000000..01d9cdab1
--- /dev/null
+++ b/sys/src/libc/386/387/tan.s
@@ -0,0 +1,5 @@
+TEXT tan(SB), $0
+ FMOVD a+0(FP), F0
+ FPTAN
+ FMOVDP F0, F0 /* get rid of extra 1.0 */
+ RET
diff --git a/sys/src/libc/386/argv0.s b/sys/src/libc/386/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/386/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/386/atom.s b/sys/src/libc/386/atom.s
new file mode 100755
index 000000000..8fd8c75d0
--- /dev/null
+++ b/sys/src/libc/386/atom.s
@@ -0,0 +1,75 @@
+TEXT ainc(SB), $0 /* long ainc(long *); */
+ MOVL addr+0(FP), BX
+ainclp:
+ MOVL (BX), AX
+ MOVL AX, CX
+ INCL CX
+ LOCK
+ BYTE $0x0F; BYTE $0xB1; BYTE $0x0B /* CMPXCHGL CX, (BX) */
+ JNZ ainclp
+ MOVL CX, AX
+ RET
+
+TEXT adec(SB), $0 /* long adec(long*); */
+ MOVL addr+0(FP), BX
+adeclp:
+ MOVL (BX), AX
+ MOVL AX, CX
+ DECL CX
+ LOCK
+ BYTE $0x0F; BYTE $0xB1; BYTE $0x0B /* CMPXCHGL CX, (BX) */
+ JNZ adeclp
+ MOVL CX, AX
+ RET
+
+/*
+ * int cas32(u32int *p, u32int ov, u32int nv);
+ * int cas(uint *p, int ov, int nv);
+ * int casp(void **p, void *ov, void *nv);
+ * int casl(ulong *p, ulong ov, ulong nv);
+ */
+
+/*
+ * CMPXCHG (CX), DX: 0000 1111 1011 000w oorr rmmm,
+ * mmm = CX = 001; rrr = DX = 010
+ */
+
+#define CMPXCHG BYTE $0x0F; BYTE $0xB1; BYTE $0x11
+
+TEXT cas32+0(SB),0,$0
+TEXT cas+0(SB),0,$0
+TEXT casp+0(SB),0,$0
+TEXT casl+0(SB),0,$0
+ MOVL p+0(FP), CX
+ MOVL ov+4(FP), AX
+ MOVL nv+8(FP), DX
+ LOCK
+ CMPXCHG
+ JNE fail
+ MOVL $1,AX
+ RET
+fail:
+ MOVL $0,AX
+ RET
+
+/*
+ * int cas64(u64int *p, u64int ov, u64int nv);
+ */
+
+/*
+ * CMPXCHG64 (DI): 0000 1111 1100 0111 0000 1110,
+ */
+
+#define CMPXCHG64 BYTE $0x0F; BYTE $0xC7; BYTE $0x0F
+
+TEXT cas64+0(SB),0,$0
+ MOVL p+0(FP), DI
+ MOVL ov+0x4(FP), AX
+ MOVL ov+0x8(FP), DX
+ MOVL nv+0xc(FP), BX
+ MOVL nv+0x10(FP), CX
+ LOCK
+ CMPXCHG64
+ JNE fail
+ MOVL $1,AX
+ RET
diff --git a/sys/src/libc/386/cycles.s b/sys/src/libc/386/cycles.s
new file mode 100755
index 000000000..7f8810261
--- /dev/null
+++ b/sys/src/libc/386/cycles.s
@@ -0,0 +1,8 @@
+#define RDTSC BYTE $0x0F; BYTE $0x31
+
+TEXT cycles(SB),1,$0 /* time stamp counter; cycles since power up */
+ RDTSC
+ MOVL vlong+0(FP), CX /* &vlong */
+ MOVL AX, 0(CX) /* lo */
+ MOVL DX, 4(CX) /* hi */
+ RET
diff --git a/sys/src/libc/386/getcallerpc.s b/sys/src/libc/386/getcallerpc.s
new file mode 100755
index 000000000..bc4a5f1ea
--- /dev/null
+++ b/sys/src/libc/386/getcallerpc.s
@@ -0,0 +1,4 @@
+TEXT getcallerpc(SB), $0
+ MOVL v+0(FP), AX
+ MOVL -4(AX), AX
+ RET
diff --git a/sys/src/libc/386/getfcr.s b/sys/src/libc/386/getfcr.s
new file mode 100755
index 000000000..f646c369c
--- /dev/null
+++ b/sys/src/libc/386/getfcr.s
@@ -0,0 +1,27 @@
+
+TEXT setfcr(SB), $4
+ MOVL p+0(FP),AX
+ XORB $0x3f,AX
+ MOVW AX, 0(SP)
+ WAIT
+ FLDCW 0(SP)
+ MOVW 0(SP), AX
+ RET
+
+TEXT getfcr(SB), $4
+ MOVW AX, 0(SP)
+ WAIT
+ FSTCW 0(SP)
+ MOVW 0(SP), AX
+ XORB $0x3f,AX
+ RET
+
+TEXT getfsr(SB), $0
+ WAIT
+ FSTSW AX
+ RET
+
+TEXT setfsr(SB), $0
+ WAIT
+ FCLEX
+ RET
diff --git a/sys/src/libc/386/main9.s b/sys/src/libc/386/main9.s
new file mode 100755
index 000000000..ced3efde6
--- /dev/null
+++ b/sys/src/libc/386/main9.s
@@ -0,0 +1,21 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(8+NPRIVATES*4)
+ MOVL AX, _tos(SB)
+ LEAL 8(SP), AX
+ MOVL AX, _privates(SB)
+ MOVL $NPRIVATES, _nprivates(SB)
+ MOVL inargc-4(FP), AX
+ MOVL AX, 0(SP)
+ LEAL inargv+0(FP), AX
+ MOVL AX, 4(SP)
+ CALL main(SB)
+
+loop:
+ MOVL $_exits<>(SB), AX
+ MOVL AX, 0(SP)
+ CALL exits(SB)
+ JMP loop
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/386/main9p.s b/sys/src/libc/386/main9p.s
new file mode 100755
index 000000000..a924d0aff
--- /dev/null
+++ b/sys/src/libc/386/main9p.s
@@ -0,0 +1,38 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $(8+NPRIVATES*4)
+ /* _tos = arg */
+ MOVL AX, _tos(SB)
+ LEAL 8(SP), AX
+ MOVL AX, _privates(SB)
+ MOVL $NPRIVATES, _nprivates(SB)
+ /* _profmain(); */
+ CALL _profmain(SB)
+ /* _tos->prof.pp = _tos->prof.next; */
+ MOVL _tos+0(SB),DX
+ MOVL 4(DX),CX
+ MOVL CX,(DX)
+ /* main(argc, argv); */
+ MOVL inargc-4(FP), AX
+ MOVL AX, 0(SP)
+ LEAL inargv+0(FP), AX
+ MOVL AX, 4(SP)
+ CALL main(SB)
+
+loop:
+ MOVL $_exits<>(SB), AX
+ MOVL AX, 0(SP)
+ CALL exits(SB)
+ MOVL $_profin(SB), AX /* force loading of profile */
+ JMP loop
+
+TEXT _savearg(SB), 1, $0
+ RET
+
+TEXT _callpc(SB), 1, $0
+ MOVL argp+0(FP), AX
+ MOVL 4(AX), AX
+ RET
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/386/memccpy.s b/sys/src/libc/386/memccpy.s
new file mode 100755
index 000000000..52af6a9cf
--- /dev/null
+++ b/sys/src/libc/386/memccpy.s
@@ -0,0 +1,53 @@
+ TEXT memccpy(SB),$0
+
+ MOVL n+12(FP), CX
+ CMPL CX, $0
+ JEQ none
+ MOVL p2+4(FP), DI
+ MOVBLZX c+8(FP), AX
+ CLD
+/*
+ * find the character in the second string
+ */
+
+ REPN; SCASB
+ JEQ found
+
+/*
+ * if not found, set count to 'n'
+ */
+none:
+ MOVL $0, AX
+ MOVL n+12(FP), BX
+ JMP memcpy
+
+/*
+ * if found, set count to bytes thru character
+ */
+found:
+ MOVL DI, AX
+ SUBL p2+4(FP), AX
+ MOVL AX, BX
+ ADDL p1+0(FP), AX
+
+/*
+ * copy the memory
+ */
+
+memcpy:
+ MOVL p1+0(FP), DI
+ MOVL p2+4(FP), SI
+/*
+ * copy whole longs
+ */
+ MOVL BX, CX
+ SHRL $2, CX
+ REP; MOVSL
+/*
+ * copy the rest, by bytes
+ */
+ ANDL $3, BX
+ MOVL BX, CX
+ REP; MOVSB
+
+ RET
diff --git a/sys/src/libc/386/memchr.s b/sys/src/libc/386/memchr.s
new file mode 100755
index 000000000..23a1982a3
--- /dev/null
+++ b/sys/src/libc/386/memchr.s
@@ -0,0 +1,23 @@
+ TEXT memchr(SB),$0
+
+ MOVL n+8(FP), CX
+ CMPL CX, $0
+ JEQ none
+ MOVL p+0(FP), DI
+ MOVBLZX c+4(FP), AX
+ CLD
+/*
+ * SCASB is memchr instruction
+ */
+
+ REPN; SCASB
+ JEQ found
+
+none:
+ MOVL $0, AX
+ RET
+
+found:
+ MOVL DI, AX
+ SUBL $1, AX
+ RET
diff --git a/sys/src/libc/386/memcmp.s b/sys/src/libc/386/memcmp.s
new file mode 100755
index 000000000..4e9d090cf
--- /dev/null
+++ b/sys/src/libc/386/memcmp.s
@@ -0,0 +1,47 @@
+ TEXT memcmp(SB),$0
+
+ MOVL n+8(FP), BX
+ CMPL BX, $0
+ JEQ none
+ MOVL p1+0(FP), DI
+ MOVL p2+4(FP), SI
+ CLD
+/*
+ * first by longs
+ */
+
+ MOVL BX, CX
+ SHRL $2, CX
+
+ REP; CMPSL
+ JNE found
+
+/*
+ * then by bytes
+ */
+ ANDL $3, BX
+ MOVL BX, CX
+ REP; CMPSB
+ JNE found1
+
+none:
+ MOVL $0, AX
+ RET
+
+/*
+ * if long found,
+ * back up and look by bytes
+ */
+found:
+ MOVL $4, CX
+ SUBL CX, DI
+ SUBL CX, SI
+ REP; CMPSB
+
+found1:
+ JLS lt
+ MOVL $-1, AX
+ RET
+lt:
+ MOVL $1, AX
+ RET
diff --git a/sys/src/libc/386/memcpy.s b/sys/src/libc/386/memcpy.s
new file mode 100755
index 000000000..bb9cfe40c
--- /dev/null
+++ b/sys/src/libc/386/memcpy.s
@@ -0,0 +1,57 @@
+ TEXT memcpy(SB), $0
+
+ MOVL p1+0(FP), DI
+ MOVL p2+4(FP), SI
+ MOVL n+8(FP), BX
+ CMPL BX, $0
+ JGE ok
+ MOVL $0, SI
+ok:
+ CLD
+/*
+ * check and set for backwards
+ */
+ CMPL SI, DI
+ JLS back
+/*
+ * copy whole longs
+ */
+ MOVL BX, CX
+ SHRL $2, CX
+ REP; MOVSL
+/*
+ * copy the rest, by bytes
+ */
+ ANDL $3, BX
+ MOVL BX, CX
+ REP; MOVSB
+
+ MOVL p+0(FP),AX
+ RET
+/*
+ * whole thing backwards has
+ * adjusted addresses
+ */
+back:
+ ADDL BX, DI
+ ADDL BX, SI
+ SUBL $4, DI
+ SUBL $4, SI
+ STD
+/*
+ * copy whole longs
+ */
+ MOVL BX, CX
+ SHRL $2, CX
+ ANDL $3, BX
+ REP; MOVSL
+/*
+ * copy the rest, by bytes
+ */
+ ADDL $3, DI
+ ADDL $3, SI
+ MOVL BX, CX
+ REP; MOVSB
+
+ MOVL p+0(FP),AX
+ RET
diff --git a/sys/src/libc/386/memmove.s b/sys/src/libc/386/memmove.s
new file mode 100755
index 000000000..90eee75c1
--- /dev/null
+++ b/sys/src/libc/386/memmove.s
@@ -0,0 +1,71 @@
+TEXT memmove(SB), $0
+ MOVL p1+0(FP), DI
+ MOVL DI, AX /* return value */
+ MOVL p2+4(FP), SI
+ MOVL n+8(FP), BX
+ CMPL BX, $0
+ JGT _ok
+ JEQ _return /* nothing to do if n == 0 */
+ MOVL $0, SI /* fault if n < 0 */
+
+/*
+ * check and set for backwards:
+ * (p2 < p1) && ((p2+n) > p1)
+ */
+_ok:
+ CMPL SI, DI
+ JGT _forward
+ JEQ _return /* nothing to do if p2 == p1 */
+ MOVL SI, DX
+ ADDL BX, DX
+ CMPL DX, DI
+ JGT _back
+
+/*
+ * copy whole longs
+ */
+_forward:
+ MOVL BX, CX
+ CLD
+ SHRL $2, CX
+ ANDL $3, BX
+ REP; MOVSL
+
+/*
+ * copy the rest, by bytes
+ */
+ JEQ _return /* flags set by above ANDL */
+ MOVL BX, CX
+ REP; MOVSB
+
+ RET
+
+/*
+ * whole thing backwards has
+ * adjusted addresses
+ */
+_back:
+ ADDL BX, DI
+ ADDL BX, SI
+ STD
+ SUBL $4, DI
+ SUBL $4, SI
+/*
+ * copy whole longs
+ */
+ MOVL BX, CX
+ SHRL $2, CX
+ ANDL $3, BX
+ REP; MOVSL
+/*
+ * copy the rest, by bytes
+ */
+ JEQ _return /* flags set by above ANDL */
+
+ ADDL $3, DI
+ ADDL $3, SI
+ MOVL BX, CX
+ REP; MOVSB
+
+_return:
+ RET
diff --git a/sys/src/libc/386/memset.s b/sys/src/libc/386/memset.s
new file mode 100755
index 000000000..6e482abee
--- /dev/null
+++ b/sys/src/libc/386/memset.s
@@ -0,0 +1,35 @@
+ TEXT memset(SB),$0
+
+ CLD
+ MOVL p+0(FP), DI
+ MOVBLZX c+4(FP), AX
+ MOVL n+8(FP), BX
+/*
+ * if not enough bytes, just set bytes
+ */
+ CMPL BX, $9
+ JLS c3
+/*
+ * build word in AX
+ */
+ MOVB AL, AH
+ MOVL AX, CX
+ SHLL $16, CX
+ ORL CX, AX
+/*
+ * set whole longs
+ */
+c1:
+ MOVL BX, CX
+ SHRL $2, CX
+ ANDL $3, BX
+ REP; STOSL
+/*
+ * set the rest, by bytes
+ */
+c3:
+ MOVL BX, CX
+ REP; STOSB
+ret:
+ MOVL p+0(FP),AX
+ RET
diff --git a/sys/src/libc/386/mkfile b/sys/src/libc/386/mkfile
new file mode 100755
index 000000000..417ea5ffb
--- /dev/null
+++ b/sys/src/libc/386/mkfile
@@ -0,0 +1,42 @@
+objtype=386
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ atom.s\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memccpy.s\
+ memchr.s\
+ memcmp.s\
+ memcpy.s\
+ memmove.s\
+ memset.s\
+ muldiv.s\
+ cycles.s\
+ setjmp.s\
+ sqrt.s\
+ strcat.s\
+ strchr.s\
+ strcpy.s\
+ strlen.s\
+ tas.s\
+ vlop.s\
+
+CFILES=\
+ getcallerpc.c\
+ notejmp.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/386/muldiv.s b/sys/src/libc/386/muldiv.s
new file mode 100755
index 000000000..5f11a97e6
--- /dev/null
+++ b/sys/src/libc/386/muldiv.s
@@ -0,0 +1,12 @@
+TEXT umuldiv(SB), $0
+ MOVL a+0(FP), AX
+ MULL b+4(FP)
+ DIVL c+8(FP)
+ RET
+
+TEXT muldiv(SB), $0
+ MOVL a+0(FP), AX
+ IMULL b+4(FP)
+ IDIVL c+8(FP)
+ RET
+ END
diff --git a/sys/src/libc/386/notejmp.c b/sys/src/libc/386/notejmp.c
new file mode 100755
index 000000000..5d912767d
--- /dev/null
+++ b/sys/src/libc/386/notejmp.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ r->ax = ret;
+ if(ret == 0)
+ r->ax = 1;
+ r->pc = j[JMPBUFPC];
+ r->sp = j[JMPBUFSP] + 4;
+ noted(NCONT);
+}
diff --git a/sys/src/libc/386/setjmp.s b/sys/src/libc/386/setjmp.s
new file mode 100755
index 000000000..697fa7359
--- /dev/null
+++ b/sys/src/libc/386/setjmp.s
@@ -0,0 +1,18 @@
+TEXT longjmp(SB), $0
+ MOVL r+4(FP), AX
+ CMPL AX, $0
+ JNE ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVL $1, AX /* bless their pointed heads */
+ok: MOVL l+0(FP), BX
+ MOVL 0(BX), SP /* restore sp */
+ MOVL 4(BX), BX /* put return pc on the stack */
+ MOVL BX, 0(SP)
+ RET
+
+TEXT setjmp(SB), $0
+ MOVL l+0(FP), AX
+ MOVL SP, 0(AX) /* store sp */
+ MOVL 0(SP), BX /* store return pc */
+ MOVL BX, 4(AX)
+ MOVL $0, AX /* return 0 */
+ RET
diff --git a/sys/src/libc/386/sqrt.s b/sys/src/libc/386/sqrt.s
new file mode 100755
index 000000000..219a8ac6b
--- /dev/null
+++ b/sys/src/libc/386/sqrt.s
@@ -0,0 +1,16 @@
+TEXT sqrt(SB), $0
+ FMOVD a+0(FP), F0
+
+ FTST
+ WAIT
+ FSTSW AX
+ SAHF
+ JLO bad
+
+ FSQRT
+ RET
+
+bad:
+ FMOVDP F0, F0
+ CALL NaN(SB)
+ RET
diff --git a/sys/src/libc/386/strcat.s b/sys/src/libc/386/strcat.s
new file mode 100755
index 000000000..3f41fefa5
--- /dev/null
+++ b/sys/src/libc/386/strcat.s
@@ -0,0 +1,43 @@
+ TEXT strcat(SB),$0
+
+ MOVL $0, AX
+ MOVL $-1, CX
+ CLD
+
+/*
+ * find length of second string
+ */
+
+ MOVL p2+4(FP), DI
+ REPN; SCASB
+
+ MOVL DI, BX
+ SUBL p2+4(FP), BX
+
+/*
+ * find end of first string
+ */
+
+ MOVL p1+0(FP), DI
+ REPN; SCASB
+
+/*
+ * copy the memory
+ */
+ SUBL $1, DI
+ MOVL p2+4(FP), SI
+/*
+ * copy whole longs
+ */
+ MOVL BX, CX
+ SHRL $2, CX
+ REP; MOVSL
+/*
+ * copy the rest, by bytes
+ */
+ ANDL $3, BX
+ MOVL BX, CX
+ REP; MOVSB
+
+ MOVL p1+0(FP), AX
+ RET
diff --git a/sys/src/libc/386/strchr.s b/sys/src/libc/386/strchr.s
new file mode 100755
index 000000000..873bdcf12
--- /dev/null
+++ b/sys/src/libc/386/strchr.s
@@ -0,0 +1,38 @@
+ TEXT strchr(SB), $0
+
+ MOVL s+0(FP), DI
+ MOVB c+4(FP), AX
+ CMPB AX, $0
+ JEQ l2 /**/
+
+/*
+ * char is not null
+ */
+l1:
+ MOVB (DI), BX
+ CMPB BX, $0
+ JEQ ret0
+ ADDL $1, DI
+ CMPB AX, BX
+ JNE l1
+
+ MOVL DI, AX
+ SUBL $1, AX
+ RET
+
+/*
+ * char is null
+ */
+l2:
+ MOVL $-1, CX
+ CLD
+
+ REPN; SCASB
+
+ MOVL DI, AX
+ SUBL $1, AX
+ RET
+
+ret0:
+ MOVL $0, AX
+ RET
diff --git a/sys/src/libc/386/strcpy.s b/sys/src/libc/386/strcpy.s
new file mode 100755
index 000000000..83482e8a2
--- /dev/null
+++ b/sys/src/libc/386/strcpy.s
@@ -0,0 +1,35 @@
+ TEXT strcpy(SB),$0
+
+ MOVL $0, AX
+ MOVL $-1, CX
+ CLD
+/*
+ * find end of second string
+ */
+
+ MOVL p2+4(FP), DI
+ REPN; SCASB
+
+ MOVL DI, BX
+ SUBL p2+4(FP), BX
+
+/*
+ * copy the memory
+ */
+ MOVL p1+0(FP), DI
+ MOVL p2+4(FP), SI
+/*
+ * copy whole longs
+ */
+ MOVL BX, CX
+ SHRL $2, CX
+ REP; MOVSL
+/*
+ * copy the rest, by bytes
+ */
+ ANDL $3, BX
+ MOVL BX, CX
+ REP; MOVSB
+
+ MOVL p1+0(FP), AX
+ RET
diff --git a/sys/src/libc/386/strlen.s b/sys/src/libc/386/strlen.s
new file mode 100755
index 000000000..e0330ffcc
--- /dev/null
+++ b/sys/src/libc/386/strlen.s
@@ -0,0 +1,16 @@
+ TEXT strlen(SB),$0
+
+ MOVL $0, AX
+ MOVL $-1, CX
+ CLD
+/*
+ * look for end of string
+ */
+
+ MOVL p+0(FP), DI
+ REPN; SCASB
+
+ MOVL DI, AX
+ SUBL p+0(FP), AX
+ SUBL $1, AX
+ RET
diff --git a/sys/src/libc/386/tas.s b/sys/src/libc/386/tas.s
new file mode 100755
index 000000000..9649dcacf
--- /dev/null
+++ b/sys/src/libc/386/tas.s
@@ -0,0 +1,6 @@
+TEXT _tas(SB),$0
+
+ MOVL $0xdeadead,AX
+ MOVL l+0(FP),BX
+ XCHGL AX,(BX)
+ RET
diff --git a/sys/src/libc/386/vlop.s b/sys/src/libc/386/vlop.s
new file mode 100755
index 000000000..1ad69f274
--- /dev/null
+++ b/sys/src/libc/386/vlop.s
@@ -0,0 +1,54 @@
+TEXT _mulv(SB), $0
+ MOVL r+0(FP), CX
+ MOVL a+4(FP), AX
+ MULL b+12(FP)
+ MOVL AX, 0(CX)
+ MOVL DX, BX
+ MOVL a+4(FP), AX
+ MULL b+16(FP)
+ ADDL AX, BX
+ MOVL a+8(FP), AX
+ MULL b+12(FP)
+ ADDL AX, BX
+ MOVL BX, 4(CX)
+ RET
+
+TEXT _mul64by32(SB), $0
+ MOVL r+0(FP), CX
+ MOVL a+4(FP), AX
+ MULL b+12(FP)
+ MOVL AX, 0(CX)
+ MOVL DX, BX
+ MOVL a+8(FP), AX
+ MULL b+12(FP)
+ ADDL AX, BX
+ MOVL BX, 4(CX)
+ RET
+
+TEXT _div64by32(SB), $0
+ MOVL r+12(FP), CX
+ MOVL a+0(FP), AX
+ MOVL a+4(FP), DX
+ DIVL b+8(FP)
+ MOVL DX, 0(CX)
+ RET
+
+TEXT _addv(SB),1,$0 /* used in profiler, can't be profiled */
+ MOVL r+0(FP), CX
+ MOVL a+4(FP), AX
+ MOVL a+8(FP), BX
+ ADDL b+12(FP), AX
+ ADCL b+16(FP), BX
+ MOVL AX, 0(CX)
+ MOVL BX, 4(CX)
+ RET
+
+TEXT _subv(SB),1,$0 /* used in profiler, can't be profiled */
+ MOVL r+0(FP), CX
+ MOVL a+4(FP), AX
+ MOVL a+8(FP), BX
+ SUBL b+12(FP), AX
+ SBBL b+16(FP), BX
+ MOVL AX, 0(CX)
+ MOVL BX, 4(CX)
+ RET
diff --git a/sys/src/libc/386/vlrt.c b/sys/src/libc/386/vlrt.c
new file mode 100755
index 000000000..83fd09683
--- /dev/null
+++ b/sys/src/libc/386/vlrt.c
@@ -0,0 +1,746 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ union
+ {
+ struct
+ {
+ ulong lo;
+ ulong hi;
+ };
+ struct
+ {
+ ushort lols;
+ ushort loms;
+ ushort hils;
+ ushort hims;
+ };
+ };
+};
+
+void abort(void);
+
+void _subv(Vlong*, Vlong, Vlong);
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+ulong _div64by32(Vlong, ulong, ulong*);
+void _mul64by32(Vlong*, Vlong, ulong);
+
+static void
+slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
+{
+ ulong n;
+ Vlong x, q, r;
+
+ if(den.hi > num.hi || (den.hi == num.hi && den.lo > num.lo)){
+ if(qp) {
+ qp->hi = 0;
+ qp->lo = 0;
+ }
+ if(rp) {
+ rp->hi = num.hi;
+ rp->lo = num.lo;
+ }
+ return;
+ }
+
+ if(den.hi != 0){
+ q.hi = 0;
+ n = num.hi/den.hi;
+ _mul64by32(&x, den, n);
+ if(x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo))
+ slowdodiv(num, den, &q, &r);
+ else {
+ q.lo = n;
+ _subv(&r, num, x);
+ }
+ } else {
+ if(num.hi >= den.lo){
+ q.hi = n = num.hi/den.lo;
+ num.hi -= den.lo*n;
+ } else {
+ q.hi = 0;
+ }
+ q.lo = _div64by32(num, den.lo, &r.lo);
+ r.hi = 0;
+ }
+ if(qp) {
+ qp->lo = q.lo;
+ qp->hi = q.hi;
+ }
+ if(rp) {
+ rp->lo = r.lo;
+ rp->hi = r.hi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u.lo = 0;
+ u.hi = 0;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
diff --git a/sys/src/libc/68000/argv0.s b/sys/src/libc/68000/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/68000/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/68000/cycles.c b/sys/src/libc/68000/cycles.c
new file mode 100755
index 000000000..9bad3a989
--- /dev/null
+++ b/sys/src/libc/68000/cycles.c
@@ -0,0 +1,7 @@
+#include <u.h>
+#include <libc.h>
+
+void cycles(uvlong*u)
+{
+ *u = 0LL;
+}
diff --git a/sys/src/libc/68000/doprint.c b/sys/src/libc/68000/doprint.c
new file mode 100755
index 000000000..e3846a8d8
--- /dev/null
+++ b/sys/src/libc/68000/doprint.c
@@ -0,0 +1,617 @@
+#include <u.h>
+#include <libc.h>
+
+enum
+{
+ SIZE = 1024,
+ IDIGIT = 40,
+ MAXCONV = 40,
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NONE = -1000,
+ MAXFMT = 512,
+
+ FPLUS = 1<<0,
+ FMINUS = 1<<1,
+ FSHARP = 1<<2,
+ FLONG = 1<<3,
+ FSHORT = 1<<4,
+ FUNSIGN = 1<<5,
+ FVLONG = 1<<6,
+};
+
+int printcol;
+
+static int convcount;
+static char fmtindex[MAXFMT];
+
+static int noconv(va_list*, Fconv*);
+static int flags(va_list*, Fconv*);
+
+static int cconv(va_list*, Fconv*);
+static int rconv(va_list*, Fconv*);
+static int sconv(va_list*, Fconv*);
+static int percent(va_list*, Fconv*);
+static int column(va_list*, Fconv*);
+
+int numbconv(va_list*, Fconv*);
+
+static
+int (*fmtconv[MAXCONV])(va_list*, Fconv*) =
+{
+ noconv
+};
+
+static
+void
+initfmt(void)
+{
+ int cc;
+
+ cc = 0;
+ fmtconv[cc] = noconv;
+ cc++;
+
+ fmtconv[cc] = flags;
+ fmtindex['+'] = cc;
+ fmtindex['-'] = cc;
+ fmtindex['#'] = cc;
+ fmtindex['h'] = cc;
+ fmtindex['l'] = cc;
+ fmtindex['u'] = cc;
+ cc++;
+
+ fmtconv[cc] = numbconv;
+ fmtindex['d'] = cc;
+ fmtindex['o'] = cc;
+ fmtindex['x'] = cc;
+ fmtindex['X'] = cc;
+ cc++;
+
+ fmtconv[cc] = cconv;
+ fmtindex['c'] = cc;
+ fmtindex['C'] = cc;
+ cc++;
+
+ fmtconv[cc] = rconv;
+ fmtindex['r'] = cc;
+ cc++;
+
+ fmtconv[cc] = sconv;
+ fmtindex['s'] = cc;
+ fmtindex['S'] = cc;
+ cc++;
+
+ fmtconv[cc] = percent;
+ fmtindex['%'] = cc;
+ cc++;
+
+ fmtconv[cc] = column;
+ fmtindex['|'] = cc;
+ cc++;
+
+ convcount = cc;
+}
+
+int
+fmtinstall(int c, int (*f)(va_list*, Fconv*))
+{
+
+ if(convcount == 0)
+ initfmt();
+ if(c < 0 || c >= MAXFMT)
+ return -1;
+ if(convcount >= MAXCONV)
+ return -1;
+ fmtconv[convcount] = f;
+ fmtindex[c] = convcount;
+ convcount++;
+ return 0;
+}
+
+char*
+doprint(char *s, char *es, char *fmt, va_list argp)
+{
+ int n, c;
+ Rune rune;
+ Fconv local;
+
+ if(s >= es)
+ return s;
+ local.out = s;
+ local.eout = es-UTFmax-1;
+
+loop:
+ c = *fmt & 0xff;
+ if(c >= Runeself) {
+ n = chartorune(&rune, fmt);
+ fmt += n;
+ c = rune;
+ } else
+ fmt++;
+ switch(c) {
+ case 0:
+ *local.out = 0;
+ return local.out;
+
+ default:
+ printcol++;
+ goto common;
+
+ case '\n':
+ printcol = 0;
+ goto common;
+
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ goto common;
+
+ common:
+ if(local.out < local.eout)
+ if(c >= Runeself) {
+ rune = c;
+ n = runetochar(local.out, &rune);
+ local.out += n;
+ } else
+ *local.out++ = c;
+ goto loop;
+
+ case '%':
+ break;
+ }
+ local.f1 = NONE;
+ local.f2 = NONE;
+ local.f3 = 0;
+
+ /*
+ * read one of the following
+ * 1. number, => f1, f2 in order.
+ * 2. '*' same as number (from args)
+ * 3. '.' ignored (separates numbers)
+ * 4. flag => f3
+ * 5. verb and terminate
+ */
+l0:
+ c = *fmt & 0xff;
+ if(c >= Runeself) {
+ n = chartorune(&rune, fmt);
+ fmt += n;
+ c = rune;
+ } else
+ fmt++;
+
+l1:
+ if(c == 0) {
+ fmt--;
+ goto loop;
+ }
+ if(c == '.') {
+ if(local.f1 == NONE)
+ local.f1 = 0;
+ local.f2 = 0;
+ goto l0;
+ }
+ if((c >= '1' && c <= '9') ||
+ (c == '0' && local.f1 != NONE)) { /* '0' is a digit for f2 */
+ n = 0;
+ while(c >= '0' && c <= '9') {
+ n = n*10 + c-'0';
+ c = *fmt++;
+ }
+ if(local.f1 == NONE)
+ local.f1 = n;
+ else
+ local.f2 = n;
+ goto l1;
+ }
+ if(c == '*') {
+ n = va_arg(argp, int);
+ if(local.f1 == NONE)
+ local.f1 = n;
+ else
+ local.f2 = n;
+ goto l0;
+ }
+ n = 0;
+ if(c >= 0 && c < MAXFMT)
+ n = fmtindex[c];
+ local.chr = c;
+ n = (*fmtconv[n])(&argp, &local);
+ if(n < 0) {
+ local.f3 |= -n;
+ goto l0;
+ }
+ goto loop;
+}
+
+int
+numbconv(va_list *arg, Fconv *fp)
+{
+ char s[IDIGIT];
+ int i, f, n, b, ucase;
+ short h;
+ long v;
+ vlong vl;
+
+ SET(v);
+ SET(vl);
+
+ ucase = 0;
+ b = fp->chr;
+ switch(fp->chr) {
+ case 'u':
+ fp->f3 |= FUNSIGN;
+ case 'd':
+ b = 10;
+ break;
+
+ case 'o':
+ b = 8;
+ break;
+
+ case 'X':
+ ucase = 1;
+ case 'x':
+ b = 16;
+ break;
+ }
+
+ f = 0;
+ switch(fp->f3 & (FVLONG|FLONG|FSHORT|FUNSIGN)) {
+ case FVLONG|FLONG:
+ vl = va_arg(*arg, vlong);
+ break;
+
+ case FUNSIGN|FVLONG|FLONG:
+ vl = va_arg(*arg, uvlong);
+ break;
+
+ case FLONG:
+ v = va_arg(*arg, long);
+ break;
+
+ case FUNSIGN|FLONG:
+ v = va_arg(*arg, ulong);
+ break;
+
+ case FSHORT:
+ h = va_arg(*arg, int);
+ v = h;
+ break;
+
+ case FUNSIGN|FSHORT:
+ h = va_arg(*arg, int);
+ v = (ushort)h;
+ break;
+
+ default:
+ v = va_arg(*arg, int);
+ break;
+
+ case FUNSIGN:
+ v = va_arg(*arg, unsigned);
+ break;
+ }
+ if(fp->f3 & FVLONG) {
+ if(!(fp->f3 & FUNSIGN) && vl < 0) {
+ vl = -vl;
+ f = 1;
+ }
+ } else {
+ if(!(fp->f3 & FUNSIGN) && v < 0) {
+ v = -v;
+ f = 1;
+ }
+ }
+ s[IDIGIT-1] = 0;
+ for(i = IDIGIT-2;; i--) {
+ if(fp->f3 & FVLONG)
+ n = (uvlong)vl % b;
+ else
+ n = (ulong)v % b;
+ n += '0';
+ if(n > '9') {
+ n += 'a' - ('9'+1);
+ if(ucase)
+ n += 'A'-'a';
+ }
+ s[i] = n;
+ if(i < 2)
+ break;
+ if(fp->f3 & FVLONG)
+ vl = (uvlong)vl / b;
+ else
+ v = (ulong)v / b;
+ if(fp->f2 != NONE && i >= IDIGIT-fp->f2)
+ continue;
+ if(fp->f3 & FVLONG) {
+ if(vl <= 0)
+ break;
+ continue;
+ }
+ if(v <= 0)
+ break;
+ }
+
+ if(fp->f3 & FSHARP) {
+ if(b == 8 && s[i] != '0')
+ s[--i] = '0';
+ if(b == 16) {
+ if(ucase)
+ s[--i] = 'X';
+ else
+ s[--i] = 'x';
+ s[--i] = '0';
+ }
+ }
+ if(f)
+ s[--i] = '-';
+ fp->f2 = NONE;
+ strconv(s+i, fp);
+ return 0;
+}
+
+void
+Strconv(Rune *s, Fconv *fp)
+{
+ int n, c, i;
+ Rune rune;
+
+ if(fp->f3 & FMINUS)
+ fp->f1 = -fp->f1;
+ n = 0;
+ if(fp->f1 != NONE && fp->f1 >= 0) {
+ for(; s[n]; n++)
+ ;
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+ for(;;) {
+ c = *s++;
+ if(c == 0)
+ break;
+ n++;
+ if(fp->f2 == NONE || fp->f2 > 0) {
+ if(fp->out < fp->eout)
+ if(c >= Runeself) {
+ rune = c;
+ i = runetochar(fp->out, &rune);
+ fp->out += i;
+ } else
+ *fp->out++ = c;
+ if(fp->f2 != NONE)
+ fp->f2--;
+ switch(c) {
+ default:
+ printcol++;
+ break;
+ case '\n':
+ printcol = 0;
+ break;
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ break;
+ }
+ }
+ }
+ if(fp->f1 != NONE && fp->f1 < 0) {
+ fp->f1 = -fp->f1;
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+}
+
+void
+strconv(char *s, Fconv *fp)
+{
+ int n, c, i;
+ Rune rune;
+
+ if(fp->f3 & FMINUS)
+ fp->f1 = -fp->f1;
+ n = 0;
+ if(fp->f1 != NONE && fp->f1 >= 0) {
+ n = utflen(s);
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+ for(;;) {
+ c = *s & 0xff;
+ if(c >= Runeself) {
+ i = chartorune(&rune, s);
+ s += i;
+ c = rune;
+ } else
+ s++;
+ if(c == 0)
+ break;
+ n++;
+ if(fp->f2 == NONE || fp->f2 > 0) {
+ if(fp->out < fp->eout)
+ if(c >= Runeself) {
+ rune = c;
+ i = runetochar(fp->out, &rune);
+ fp->out += i;
+ } else
+ *fp->out++ = c;
+ if(fp->f2 != NONE)
+ fp->f2--;
+ switch(c) {
+ default:
+ printcol++;
+ break;
+ case '\n':
+ printcol = 0;
+ break;
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ break;
+ }
+ }
+ }
+ if(fp->f1 != NONE && fp->f1 < 0) {
+ fp->f1 = -fp->f1;
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+}
+
+static
+int
+noconv(va_list *arg, Fconv *fp)
+{
+ int n;
+ char s[10];
+
+ if(convcount == 0) {
+ initfmt();
+ n = 0;
+ if(fp->chr >= 0 && fp->chr < MAXFMT)
+ n = fmtindex[fp->chr];
+ return (*fmtconv[n])(arg, fp);
+ }
+ s[0] = '*';
+ s[1] = fp->chr;
+ s[2] = '*';
+ s[3] = 0;
+ fp->f1 = 0;
+ fp->f2 = NONE;
+ fp->f3 = 0;
+ strconv(s, fp);
+ return 0;
+}
+
+static
+int
+rconv(va_list*, Fconv *fp)
+{
+ char s[ERRLEN];
+
+ s[0] = 0;
+ errstr(s);
+ fp->f2 = NONE;
+ strconv(s, fp);
+ return 0;
+}
+
+static
+int
+cconv(va_list *arg, Fconv *fp)
+{
+ char s[10];
+ Rune rune;
+
+ rune = va_arg(*arg, int);
+ if(fp->chr == 'c')
+ rune &= 0xff;
+ s[runetochar(s, &rune)] = 0;
+
+ fp->f2 = NONE;
+ strconv(s, fp);
+ return 0;
+}
+
+static
+int
+sconv(va_list *arg, Fconv *fp)
+{
+ char *s;
+ Rune *r;
+
+ if(fp->chr == 's') {
+ s = va_arg(*arg, char*);
+ if(s == 0)
+ s = "<null>";
+ strconv(s, fp);
+ } else {
+ r = va_arg(*arg, Rune*);
+ if(r == 0)
+ r = L"<null>";
+ Strconv(r, fp);
+ }
+ return 0;
+}
+
+static
+int
+percent(va_list*, Fconv *fp)
+{
+
+ if(fp->out < fp->eout)
+ *fp->out++ = '%';
+ printcol++;
+ return 0;
+}
+
+static
+int
+column(va_list *arg, Fconv *fp)
+{
+ int col, pc;
+
+ col = va_arg(*arg, int);
+ while(fp->out < fp->eout && printcol < col) {
+ pc = (printcol+8) & ~7;
+ if(pc <= col) {
+ *fp->out++ = '\t';
+ printcol = pc;
+ } else {
+ *fp->out++ = ' ';
+ printcol++;
+ }
+ }
+ return 0;
+}
+
+static
+int
+flags(va_list*, Fconv *fp)
+{
+ int f;
+
+ f = 0;
+ switch(fp->chr) {
+ case '+':
+ f = FPLUS;
+ break;
+
+ case '-':
+ f = FMINUS;
+ break;
+
+ case '#':
+ f = FSHARP;
+ break;
+
+ case 'h':
+ f = FSHORT;
+ break;
+
+ case 'l':
+ f = FLONG;
+ if(fp->f3 & FLONG)
+ f = FVLONG;
+ break;
+
+ case 'u':
+ f = FUNSIGN;
+ break;
+ }
+ return -f;
+}
diff --git a/sys/src/libc/68000/getfcr.s b/sys/src/libc/68000/getfcr.s
new file mode 100755
index 000000000..7f67d038f
--- /dev/null
+++ b/sys/src/libc/68000/getfcr.s
@@ -0,0 +1,19 @@
+TEXT getfsr(SB), $0
+ MOVL $0, R0
+ MOVL FPSR, R0
+ RTS
+
+TEXT setfsr(SB), $0
+ MOVL new+0(FP), R1
+ MOVL R1, FPSR
+ RTS
+
+TEXT getfcr(SB), $0
+ MOVL $0, R0
+ MOVL FPCR, R0
+ RTS
+
+TEXT setfcr(SB), $0
+ MOVL new+0(FP), R1
+ MOVL R1, FPCR
+ RTS
diff --git a/sys/src/libc/68000/main9.s b/sys/src/libc/68000/main9.s
new file mode 100755
index 000000000..86b23d257
--- /dev/null
+++ b/sys/src/libc/68000/main9.s
@@ -0,0 +1,18 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+ MOVL $a6base(SB), A6
+ MOVL R0, _tos(SB)
+ LEA p-64(SP),A0
+ MOVL A0,_privates+0(SB)
+ MOVL $16,R0
+ MOVL R0,_nprivates+0(SB)
+ PEA inargv+0(FP)
+ MOVL inargc-4(FP), TOS
+ BSR main(SB)
+ PEA _exits<>+0(SB)
+ BSR exits(SB)
+ RTS
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/68000/main9p.s b/sys/src/libc/68000/main9p.s
new file mode 100755
index 000000000..61e0294d5
--- /dev/null
+++ b/sys/src/libc/68000/main9p.s
@@ -0,0 +1,31 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $(16 + NPRIVATES*4)
+ MOVL $a6base(SB), A6
+ MOVL R0, _tos(SB) /* return value of sys exec!! */
+ LEA p-64(SP),A0
+ MOVL A0,_privates+0(SB)
+ MOVL $16,R0
+ MOVL R0,_nprivates+0(SB)
+ BSR _profmain(SB)
+ MOVL __prof+4(SB), __prof+0(SB)
+ PEA inargv+0(FP)
+ MOVL inargc-4(FP), TOS
+ BSR main(SB)
+
+loop:
+ PEA _exits<>+0(SB)
+ BSR exits(SB)
+ LEA _profin(SB), A0 /* force loading of profile */
+ BRA loop
+
+TEXT _savearg(SB), 1, $0
+ RTS
+
+TEXT _callpc(SB), 1, $0
+ MOVL argp+0(FP), A0
+ MOVL 4(A0), R0
+ RTS
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/68000/memccpy.s b/sys/src/libc/68000/memccpy.s
new file mode 100755
index 000000000..3e494e915
--- /dev/null
+++ b/sys/src/libc/68000/memccpy.s
@@ -0,0 +1,30 @@
+ TEXT memccpy(SB),$0
+ MOVL n+12(FP),R0
+ BEQ ret
+ MOVL s1+0(FP),A2
+ MOVL s2+4(FP),A1
+ MOVL c+8(FP),R1
+ BEQ l2
+
+/*
+ * general case
+ */
+l1: MOVB (A1)+,R2
+ MOVB R2,(A2)+
+ CMPB R2,R1
+ BEQ eq
+ SUBL $1,R0
+ BNE l1
+ RTS
+
+/*
+ * special case for null character
+ */
+l2: MOVB (A1)+,(A2)+
+ BEQ eq
+ SUBL $1,R0
+ BNE l2
+ RTS
+
+eq: MOVL A2,R0
+ret: RTS
diff --git a/sys/src/libc/68000/memchr.s b/sys/src/libc/68000/memchr.s
new file mode 100755
index 000000000..ea2584df8
--- /dev/null
+++ b/sys/src/libc/68000/memchr.s
@@ -0,0 +1,15 @@
+ TEXT memchr(SB),$0
+ MOVL n+8(FP),R0
+ BEQ ret
+ MOVL s1+0(FP),A1
+ MOVL c+4(FP),R1
+
+l1: CMPB R1,(A1)+
+ BEQ eq
+ SUBL $1,R0
+ BNE l1
+ RTS
+
+eq: MOVL A1,R0
+ SUBL $1,R0
+ret: RTS
diff --git a/sys/src/libc/68000/memcmp.s b/sys/src/libc/68000/memcmp.s
new file mode 100755
index 000000000..97d208e3b
--- /dev/null
+++ b/sys/src/libc/68000/memcmp.s
@@ -0,0 +1,18 @@
+ TEXT memcmp(SB),$0
+ MOVL n+8(FP),R0
+ BEQ ret
+ MOVL s1+0(FP),A2
+ MOVL s2+4(FP),A1
+
+l1: CMPB (A1)+,(A2)+
+ BNE neq
+ SUBL $1,R0
+ BNE l1
+ RTS
+
+neq: BCS gtr
+ MOVL $-1,R0
+ RTS
+
+gtr: MOVL $1,R0
+ret: RTS
diff --git a/sys/src/libc/68000/memcpy.s b/sys/src/libc/68000/memcpy.s
new file mode 100755
index 000000000..bf4b1d5df
--- /dev/null
+++ b/sys/src/libc/68000/memcpy.s
@@ -0,0 +1,120 @@
+ TEXT memcpy(SB), $0
+
+ MOVL n+8(FP), R0 /* count */
+ BEQ return
+ BGT ok
+ MOVL 0, R0
+ok:
+ MOVL s1+0(FP), A2 /* dest pointer */
+ MOVL s2+4(FP), A1 /* source pointer */
+
+ CMPL A2,A1
+ BHI back
+
+/*
+ * byte-at-a-time foreward copy to
+ * get source (A1) alligned.
+ */
+f1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ f2
+ SUBL $1, R0
+ BLT return
+ MOVB (A1)+, (A2)+
+ BRA f1
+
+/*
+ * check that dest is alligned
+ * if not, just go byte-at-a-time
+ */
+f2:
+ MOVL A2, R1
+ ANDL $3, R1
+ BEQ f3
+ SUBL $1, R0
+ BLT return
+ BRA f5
+/*
+ * quad-long-at-a-time forward copy
+ */
+f3:
+ SUBL $16, R0
+ BLT f4
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ BRA f3
+
+/*
+ * cleanup byte-at-a-time
+ */
+f4:
+ ADDL $15, R0
+ BLT return
+f5:
+ MOVB (A1)+, (A2)+
+ SUBL $1, R0
+ BGE f5
+ BRA return
+
+return:
+ MOVL s1+0(FP),R0
+ RTS
+
+/*
+ * everything the same, but
+ * copy backwards
+ */
+back:
+ ADDL R0, A1
+ ADDL R0, A2
+
+/*
+ * byte-at-a-time backward copy to
+ * get source (A1) alligned.
+ */
+b1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ b2
+ SUBL $1, R0
+ BLT return
+ MOVB -(A1), -(A2)
+ BRA b1
+
+/*
+ * check that dest is alligned
+ * if not, just go byte-at-a-time
+ */
+b2:
+ MOVL A2, R1
+ ANDL $3, R1
+ BEQ b3
+ SUBL $1, R0
+ BLT return
+ BRA b5
+/*
+ * quad-long-at-a-time backward copy
+ */
+b3:
+ SUBL $16, R0
+ BLT b4
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ BRA b3
+
+/*
+ * cleanup byte-at-a-time backward
+ */
+b4:
+ ADDL $15, R0
+ BLT return
+b5:
+ MOVB -(A1), -(A2)
+ SUBL $1, R0
+ BGE b5
+ BRA return
diff --git a/sys/src/libc/68000/memmove.s b/sys/src/libc/68000/memmove.s
new file mode 100755
index 000000000..618e368d3
--- /dev/null
+++ b/sys/src/libc/68000/memmove.s
@@ -0,0 +1,120 @@
+ TEXT memmove(SB), $0
+
+ MOVL n+8(FP), R0 /* count */
+ BEQ return
+ BGT ok
+ MOVL 0, R0
+ok:
+ MOVL s1+0(FP), A2 /* dest pointer */
+ MOVL s2+4(FP), A1 /* source pointer */
+
+ CMPL A2,A1
+ BHI back
+
+/*
+ * byte-at-a-time foreward copy to
+ * get source (A1) alligned.
+ */
+f1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ f2
+ SUBL $1, R0
+ BLT return
+ MOVB (A1)+, (A2)+
+ BRA f1
+
+/*
+ * check that dest is alligned
+ * if not, just go byte-at-a-time
+ */
+f2:
+ MOVL A2, R1
+ ANDL $3, R1
+ BEQ f3
+ SUBL $1, R0
+ BLT return
+ BRA f5
+/*
+ * quad-long-at-a-time forward copy
+ */
+f3:
+ SUBL $16, R0
+ BLT f4
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ BRA f3
+
+/*
+ * cleanup byte-at-a-time
+ */
+f4:
+ ADDL $15, R0
+ BLT return
+f5:
+ MOVB (A1)+, (A2)+
+ SUBL $1, R0
+ BGE f5
+ BRA return
+
+return:
+ MOVL s1+0(FP),R0
+ RTS
+
+/*
+ * everything the same, but
+ * copy backwards
+ */
+back:
+ ADDL R0, A1
+ ADDL R0, A2
+
+/*
+ * byte-at-a-time backward copy to
+ * get source (A1) alligned.
+ */
+b1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ b2
+ SUBL $1, R0
+ BLT return
+ MOVB -(A1), -(A2)
+ BRA b1
+
+/*
+ * check that dest is alligned
+ * if not, just go byte-at-a-time
+ */
+b2:
+ MOVL A2, R1
+ ANDL $3, R1
+ BEQ b3
+ SUBL $1, R0
+ BLT return
+ BRA b5
+/*
+ * quad-long-at-a-time backward copy
+ */
+b3:
+ SUBL $16, R0
+ BLT b4
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ BRA b3
+
+/*
+ * cleanup byte-at-a-time backward
+ */
+b4:
+ ADDL $15, R0
+ BLT return
+b5:
+ MOVB -(A1), -(A2)
+ SUBL $1, R0
+ BGE b5
+ BRA return
diff --git a/sys/src/libc/68000/memset.s b/sys/src/libc/68000/memset.s
new file mode 100755
index 000000000..318f61a7c
--- /dev/null
+++ b/sys/src/libc/68000/memset.s
@@ -0,0 +1,57 @@
+ TEXT memset(SB), $0
+ MOVL n+8(FP), R0
+ BLE return
+ MOVL s1+0(FP), A1
+ CLRL R1
+ MOVB c+7(FP), R1
+ BEQ l1
+
+/*
+ * create 4 replicated copies
+ * of the byte in R1
+ */
+ MOVL R1, R2
+ ASLL $8, R2
+ ORL R2, R1
+ MOVL R1, R2
+ SWAP R2
+ ORL R2, R1
+
+/*
+ * byte-at-a-time until alligned
+ */
+l1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ l2
+ SUBL $1, R0
+ BLT return
+ MOVB R1, (A1)+
+ BRA l1
+
+/*
+ * quad-long-at-a-time set
+ */
+l2:
+ SUBL $16, R0
+ BLT l3
+ MOVL R1, (A1)+
+ MOVL R1, (A1)+
+ MOVL R1, (A1)+
+ MOVL R1, (A1)+
+ BRA l2
+
+/*
+ * cleanup byte-at-a-time
+ */
+l3:
+ ADDL $15, R0
+ BLT return
+l4:
+ MOVB R1, (A1)+
+ SUBL $1, R0
+ BGE l4
+
+return:
+ MOVL s1+0(FP),R0
+ RTS
diff --git a/sys/src/libc/68000/mkfile b/sys/src/libc/68000/mkfile
new file mode 100755
index 000000000..573d49268
--- /dev/null
+++ b/sys/src/libc/68000/mkfile
@@ -0,0 +1,40 @@
+objtype=68000
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memccpy.s\
+ memchr.s\
+ memcmp.s\
+ memcpy.s\
+ memmove.s\
+ memset.s\
+ muldivrt.s\
+ scale.s\
+ setjmp.s\
+ sqrt.s\
+ strcat.s\
+ strchr.s\
+ strcmp.s\
+ strcpy.s\
+ strlen.s\
+
+CFILES=\
+ cycles.c\
+ notejmp.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/68000/muldivrt.s b/sys/src/libc/68000/muldivrt.s
new file mode 100755
index 000000000..10ef6f46b
--- /dev/null
+++ b/sys/src/libc/68000/muldivrt.s
@@ -0,0 +1,172 @@
+/*
+ * calls _divul with
+ * absolute value arguments
+ */
+TEXT _divsl(SB), $0
+ MOVL R0, TOS
+
+ MOVL b+4(FP), R0
+ BPL y1
+ NEGL R0
+ MOVL R0, TOS
+
+ MOVL a+0(FP), R0
+ BPL y3
+ NEGL R0
+ MOVL R0, TOS
+
+ /* neg/neg */
+ JSR _divul(SB)
+ MOVL TOS, R0
+ MOVL R0, a+0(FP)
+ MOVL TOS, R0
+ NEGL R0
+ MOVL R0, b+4(FP)
+ MOVL TOS, R0
+ RTS
+
+y1: MOVL R0, TOS
+
+ MOVL a+0(FP), R0
+ BPL y2
+ NEGL R0
+ MOVL R0, TOS
+
+ /* neg/pos */
+ JSR _divul(SB)
+ MOVL TOS, R0
+ NEGL R0
+ MOVL R0, a+0(FP)
+ MOVL TOS, R0
+ NEGL R0
+ MOVL R0, b+4(FP)
+ MOVL TOS, R0
+ RTS
+
+y2: MOVL R0, TOS
+
+ /* pos/pos */
+ JSR _divul(SB)
+ MOVL TOS, R0
+ MOVL R0, a+0(FP)
+ MOVL TOS, R0
+ MOVL R0, b+4(FP)
+ MOVL TOS, R0
+ RTS
+
+y3: MOVL R0, TOS
+
+ /* pos/neg */
+ JSR _divul(SB)
+ MOVL TOS, R0
+ NEGL R0
+ MOVL R0, a+0(FP)
+ MOVL TOS, R0
+ MOVL R0, b+4(FP)
+ MOVL TOS, R0
+ RTS
+
+/*
+ * for(i=1;; i++) {
+ * if(den & (1<<31))
+ * break;
+ * den <<= 1;
+ * }
+ *
+ * for(; i; i--) {
+ * quo <<= 1;
+ * if(num >= den) {
+ * num -= den;
+ * quo |= 1;
+ * }
+ * den >>= 1;
+ * }
+ */
+TEXT _divul(SB), $0
+ MOVL R0, TOS /* i */
+ MOVL R1, TOS /* num */
+ MOVL R2, TOS /* den */
+ MOVL R3, TOS /* quo */
+
+ MOVL $0, R0
+ MOVL $0, R3
+ MOVL a+0(FP), R1
+ MOVL b+4(FP), R2
+ BEQ xout
+ BMI x1
+
+ ADDL $1, R0
+ LSLL $1, R2
+ BPL -2(PC)
+
+x1: LSLL $1, R3
+ CMPL R1, R2
+ BCS 3(PC)
+ SUBL R2, R1
+ ORL $1, R3
+ LSRL $1, R2
+ DBMI R0, x1
+
+ MOVL R3, a+0(FP)
+ MOVL R1, b+4(FP)
+
+xout:
+ MOVL TOS, R3
+ MOVL TOS, R2
+ MOVL TOS, R1
+ MOVL TOS, R0
+ RTS
+
+/*
+ * x = 0;
+ * for(i=0; i<32; i++) {
+ * if(a & 1)
+ * x += b;
+ * a >>= 1;
+ * b <<= 1;
+ * }
+ * a = x;
+ */
+TEXT _mull(SB), $0
+ MOVL R0, TOS /* i */
+ MOVL R1, TOS /* a */
+ MOVL R2, TOS /* b */
+ MOVL R3, TOS /* x */
+
+ MOVL a+0(FP), R1
+ MOVL b+4(FP), R2
+ MOVL $32, R0
+ CLRL R3
+
+z1: ROTRL $1, R1
+ BCC 2(PC)
+ ADDL R2, R3
+ LSLL $1, R2
+ DBEQ R0, z1
+
+ MOVL R3, b+4(FP)
+ MOVL TOS, R3
+ MOVL TOS, R2
+ MOVL TOS, R1
+ MOVL TOS, R0
+ RTS
+
+TEXT _ccr(SB), $0
+ PEA (A0)
+ SUBL A0, A0
+
+ BCC 2(PC)
+ LEA 1(A0), A0
+
+ BVC 2(PC)
+ LEA 2(A0), A0
+
+ BNE 2(PC)
+ LEA 4(A0), A0
+
+ BPL 2(PC)
+ LEA 8(A0), A0
+
+ MOVW A0, a+0(FP)
+ MOVL TOS, A0
+ RTS
diff --git a/sys/src/libc/68000/notejmp.c b/sys/src/libc/68000/notejmp.c
new file mode 100755
index 000000000..61562d441
--- /dev/null
+++ b/sys/src/libc/68000/notejmp.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#define UREGVARSZ 4 /* not right but doesn't matter */
+#include <ureg.h>
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ r->r0 = ret;
+ if(ret == 0)
+ r->r0 = 1;
+ r->pc = j[JMPBUFPC];
+ r->usp = j[JMPBUFSP] + 4;
+ noted(NCONT);
+}
diff --git a/sys/src/libc/68000/scale.s b/sys/src/libc/68000/scale.s
new file mode 100755
index 000000000..0e0e50a86
--- /dev/null
+++ b/sys/src/libc/68000/scale.s
@@ -0,0 +1,4 @@
+ TEXT fscale(SB), $0
+ FMOVED a+0(FP), F0
+ FSCALEL x+8(FP), F0
+ RTS
diff --git a/sys/src/libc/68000/setjmp.s b/sys/src/libc/68000/setjmp.s
new file mode 100755
index 000000000..827b71fc1
--- /dev/null
+++ b/sys/src/libc/68000/setjmp.s
@@ -0,0 +1,15 @@
+TEXT setjmp(SB), 1, $0
+ MOVL b+0(FP), A0
+ MOVL A7, (A0)+
+ MOVL (A7), (A0)
+ CLRL R0
+ RTS
+
+TEXT longjmp(SB), 1, $0
+ MOVL b+0(FP), A0
+ MOVL r+4(FP), R0
+ BNE ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVL $1, R0 /* bless their pointed heads */
+ok: MOVL (A0)+, A7
+ MOVL (A0), (A7)
+ RTS
diff --git a/sys/src/libc/68000/sqrt.s b/sys/src/libc/68000/sqrt.s
new file mode 100755
index 000000000..5109bc255
--- /dev/null
+++ b/sys/src/libc/68000/sqrt.s
@@ -0,0 +1,3 @@
+ TEXT sqrt(SB),$0
+ FSQRTD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68000/strcat.s b/sys/src/libc/68000/strcat.s
new file mode 100755
index 000000000..95821408b
--- /dev/null
+++ b/sys/src/libc/68000/strcat.s
@@ -0,0 +1,15 @@
+ TEXT strcat(SB), $0
+ MOVL s1+0(FP), A2
+ MOVL s2+4(FP), A1
+
+l1: TSTB (A2)+
+ BNE l1
+
+ MOVB (A1)+, -1(A2)
+ BEQ done
+
+l2: MOVB (A1)+, (A2)+
+ BNE l2
+
+done: MOVL s1+0(FP), R0
+ RTS
diff --git a/sys/src/libc/68000/strchr.s b/sys/src/libc/68000/strchr.s
new file mode 100755
index 000000000..a5d4be2c6
--- /dev/null
+++ b/sys/src/libc/68000/strchr.s
@@ -0,0 +1,27 @@
+ TEXT strchr(SB), $0
+
+ MOVL s+0(FP), A0
+ MOVB c+7(FP), R2
+ BEQ null
+
+l:
+ MOVB (A0)+, R1
+ BEQ out
+ CMPB R1, R2
+ BNE l
+
+ MOVL A0, R0
+ ADDL $-1, R0
+ RTS
+
+out:
+ CLRL R0
+ RTS
+
+null:
+ TSTB (A0)+
+ BNE null
+
+ MOVL A0, R0
+ ADDL $-1, R0
+ RTS
diff --git a/sys/src/libc/68000/strcmp.s b/sys/src/libc/68000/strcmp.s
new file mode 100755
index 000000000..c6072efd7
--- /dev/null
+++ b/sys/src/libc/68000/strcmp.s
@@ -0,0 +1,20 @@
+ TEXT strcmp(SB), $0
+ MOVL s1+0(FP), A2
+ MOVL s2+4(FP), A1
+
+l1: MOVB (A1)+, R0
+ BEQ end
+ CMPB R0, (A2)+
+ BEQ l1
+
+ BCS gtr
+ MOVL $-1, R0
+ RTS
+
+gtr: MOVL $1, R0
+ RTS
+
+end: TSTB (A2)
+ BNE gtr
+ CLRL R0
+ RTS
diff --git a/sys/src/libc/68000/strcpy.s b/sys/src/libc/68000/strcpy.s
new file mode 100755
index 000000000..ce5c30bdb
--- /dev/null
+++ b/sys/src/libc/68000/strcpy.s
@@ -0,0 +1,10 @@
+ TEXT strcpy(SB), $0
+
+ MOVL s1+0(FP), A2
+ MOVL s2+4(FP), A1
+
+l1: MOVB (A1)+, (A2)+
+ BNE l1
+
+ MOVL s1+0(FP), R0
+ RTS
diff --git a/sys/src/libc/68000/strlen.s b/sys/src/libc/68000/strlen.s
new file mode 100755
index 000000000..4d787c704
--- /dev/null
+++ b/sys/src/libc/68000/strlen.s
@@ -0,0 +1,18 @@
+ TEXT strlen(SB), $0
+ MOVL s+0(FP), A1
+
+ TSTB (A1)+
+ BEQ null
+ MOVL A1, A2
+
+l1:
+ TSTB (A1)+
+ BNE l1
+
+ SUBL A2, A1
+ MOVL A1, R0
+ RTS
+
+null:
+ MOVL $0, R0
+ RTS
diff --git a/sys/src/libc/68000/vlrt.c b/sys/src/libc/68000/vlrt.c
new file mode 100755
index 000000000..071aa180d
--- /dev/null
+++ b/sys/src/libc/68000/vlrt.c
@@ -0,0 +1,771 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ union
+ {
+ struct
+ {
+ ulong hi;
+ ulong lo;
+ };
+ struct
+ {
+ ushort hims;
+ ushort hils;
+ ushort loms;
+ ushort lols;
+ };
+ };
+};
+
+void abort(void);
+
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_negv(Vlong *r, Vlong a)
+{
+ if(a.lo == 0) {
+ r->hi = -a.hi;
+ r->lo = 0;
+ return;
+ }
+ r->hi = ~a.hi;
+ r->lo = -a.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u = *ret;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+void
+_mulv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong ahi, alo, chi, clo, x;
+ int i;
+
+ ahi = a.hi;
+ alo = a.lo;
+ chi = 0;
+ clo = 0;
+
+ x = b.lo;
+ for(i=0; i<32; i++) {
+ if(x & 1) {
+ chi += ahi;
+ clo += alo;
+ if(clo < alo)
+ chi++;
+ }
+ ahi <<= 1;
+ if(alo & SIGN(32))
+ ahi += 1;
+ alo <<= 1;
+ x >>= 1;
+ }
+
+ /*
+ * same, but
+ * alo is known to be 0
+ * can stop when x == 0
+ */
+ for(x=b.hi; x; x>>=1) {
+ if(x & 1)
+ chi += ahi;
+ ahi <<= 1;
+ }
+
+ r->hi = chi;
+ r->lo = clo;
+}
diff --git a/sys/src/libc/68020/68881/acos.s b/sys/src/libc/68020/68881/acos.s
new file mode 100755
index 000000000..94c995c1f
--- /dev/null
+++ b/sys/src/libc/68020/68881/acos.s
@@ -0,0 +1,3 @@
+ TEXT acos(SB),$0
+ FACOSD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/asin.s b/sys/src/libc/68020/68881/asin.s
new file mode 100755
index 000000000..068c4ce2e
--- /dev/null
+++ b/sys/src/libc/68020/68881/asin.s
@@ -0,0 +1,3 @@
+ TEXT asin(SB),$0
+ FASIND a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/atan.s b/sys/src/libc/68020/68881/atan.s
new file mode 100755
index 000000000..33f7bd8a4
--- /dev/null
+++ b/sys/src/libc/68020/68881/atan.s
@@ -0,0 +1,3 @@
+ TEXT atan(SB),$0
+ FATAND a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/cos.s b/sys/src/libc/68020/68881/cos.s
new file mode 100755
index 000000000..faacddf30
--- /dev/null
+++ b/sys/src/libc/68020/68881/cos.s
@@ -0,0 +1,3 @@
+ TEXT cos(SB),$0
+ FCOSD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/cosh.s b/sys/src/libc/68020/68881/cosh.s
new file mode 100755
index 000000000..5269184ec
--- /dev/null
+++ b/sys/src/libc/68020/68881/cosh.s
@@ -0,0 +1,3 @@
+ TEXT cosh(SB),$0
+ FCOSHD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/exp.s b/sys/src/libc/68020/68881/exp.s
new file mode 100755
index 000000000..530334413
--- /dev/null
+++ b/sys/src/libc/68020/68881/exp.s
@@ -0,0 +1,3 @@
+ TEXT exp(SB),$0
+ FETOXD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/fabs.s b/sys/src/libc/68020/68881/fabs.s
new file mode 100755
index 000000000..e29620f25
--- /dev/null
+++ b/sys/src/libc/68020/68881/fabs.s
@@ -0,0 +1,3 @@
+ TEXT fabs(SB),$0
+ FABSD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/log.s b/sys/src/libc/68020/68881/log.s
new file mode 100755
index 000000000..3e39372da
--- /dev/null
+++ b/sys/src/libc/68020/68881/log.s
@@ -0,0 +1,3 @@
+ TEXT log(SB),$0
+ FLOGND a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/log10.s b/sys/src/libc/68020/68881/log10.s
new file mode 100755
index 000000000..92ec78005
--- /dev/null
+++ b/sys/src/libc/68020/68881/log10.s
@@ -0,0 +1,3 @@
+ TEXT log10(SB),$0
+ FLOG10D a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/mkfile b/sys/src/libc/68020/68881/mkfile
new file mode 100755
index 000000000..08ce52c0a
--- /dev/null
+++ b/sys/src/libc/68020/68881/mkfile
@@ -0,0 +1,31 @@
+objtype=68020
+</68020/mkfile
+CFLAGS=-I/sys/ninclude $CFLAGS
+
+LIB=/$objtype/lib/lib68881.a
+SFILES=\
+ acos.s\
+ asin.s\
+ atan.s\
+ cos.s\
+ cosh.s\
+ exp.s\
+ fabs.s\
+ log.s\
+ log10.s\
+ pow10.s\
+ sin.s\
+ sinh.s\
+ sqrt.s\
+ tan.s\
+ tanh.s\
+
+OFILES=${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
+
+installall:V:
+ mk install
diff --git a/sys/src/libc/68020/68881/pow10.s b/sys/src/libc/68020/68881/pow10.s
new file mode 100755
index 000000000..cdb83ec51
--- /dev/null
+++ b/sys/src/libc/68020/68881/pow10.s
@@ -0,0 +1,3 @@
+ TEXT pow10(SB),$0
+ FTENTOXL n+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/sin.s b/sys/src/libc/68020/68881/sin.s
new file mode 100755
index 000000000..568fab952
--- /dev/null
+++ b/sys/src/libc/68020/68881/sin.s
@@ -0,0 +1,3 @@
+ TEXT sin(SB),$0
+ FSIND a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/sinh.s b/sys/src/libc/68020/68881/sinh.s
new file mode 100755
index 000000000..b05186374
--- /dev/null
+++ b/sys/src/libc/68020/68881/sinh.s
@@ -0,0 +1,3 @@
+ TEXT sinh(SB),$0
+ FSINHD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/sqrt.s b/sys/src/libc/68020/68881/sqrt.s
new file mode 100755
index 000000000..5109bc255
--- /dev/null
+++ b/sys/src/libc/68020/68881/sqrt.s
@@ -0,0 +1,3 @@
+ TEXT sqrt(SB),$0
+ FSQRTD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/tan.s b/sys/src/libc/68020/68881/tan.s
new file mode 100755
index 000000000..9cedef2e4
--- /dev/null
+++ b/sys/src/libc/68020/68881/tan.s
@@ -0,0 +1,3 @@
+ TEXT tan(SB),$0
+ FTAND a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/68881/tanh.s b/sys/src/libc/68020/68881/tanh.s
new file mode 100755
index 000000000..2f4e42728
--- /dev/null
+++ b/sys/src/libc/68020/68881/tanh.s
@@ -0,0 +1,3 @@
+ TEXT tanh(SB),$0
+ FTANHD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/argv0.s b/sys/src/libc/68020/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/68020/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/68020/getcallerpc.s b/sys/src/libc/68020/getcallerpc.s
new file mode 100755
index 000000000..78f2f9db9
--- /dev/null
+++ b/sys/src/libc/68020/getcallerpc.s
@@ -0,0 +1,3 @@
+TEXT getcallerpc(SB), $0
+ MOVL (a+0(FP)), R0
+ RTS
diff --git a/sys/src/libc/68020/getfcr.s b/sys/src/libc/68020/getfcr.s
new file mode 100755
index 000000000..7f67d038f
--- /dev/null
+++ b/sys/src/libc/68020/getfcr.s
@@ -0,0 +1,19 @@
+TEXT getfsr(SB), $0
+ MOVL $0, R0
+ MOVL FPSR, R0
+ RTS
+
+TEXT setfsr(SB), $0
+ MOVL new+0(FP), R1
+ MOVL R1, FPSR
+ RTS
+
+TEXT getfcr(SB), $0
+ MOVL $0, R0
+ MOVL FPCR, R0
+ RTS
+
+TEXT setfcr(SB), $0
+ MOVL new+0(FP), R1
+ MOVL R1, FPCR
+ RTS
diff --git a/sys/src/libc/68020/main9.s b/sys/src/libc/68020/main9.s
new file mode 100755
index 000000000..38ed7322f
--- /dev/null
+++ b/sys/src/libc/68020/main9.s
@@ -0,0 +1,19 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+ MOVL $a6base(SB), A6
+ MOVL R0, _tos(SB)
+ LEA p-64(SP),A0
+ MOVL A0,_privates+0(SB)
+ MOVL $16,R0
+ MOVL R0,_nprivates+0(SB)
+ PEA inargv+0(FP)
+ MOVL inargc-4(FP), TOS
+ BSR main(SB)
+ MOVL $_exits<>+0(SB), R0
+ MOVL R0,TOS
+ BSR exits(SB)
+ RTS
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/68020/main9p.s b/sys/src/libc/68020/main9p.s
new file mode 100755
index 000000000..6c64f6404
--- /dev/null
+++ b/sys/src/libc/68020/main9p.s
@@ -0,0 +1,32 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $(16 + NPRIVATES*4)
+ MOVL $a6base(SB), A6
+ MOVL R0, _tos(SB) /* return value of sys exec!! */
+ LEA p-64(SP),A0
+ MOVL A0,_privates+0(SB)
+ MOVL $16,R0
+ MOVL R0,_nprivates+0(SB)
+ BSR _profmain(SB)
+ MOVL __prof+4(SB), __prof+0(SB)
+ PEA inargv+0(FP)
+ MOVL inargc-4(FP), TOS
+ BSR main(SB)
+
+loop:
+ MOVL $_exits<>+0(SB), R0
+ MOVL R0,TOS
+ BSR exits(SB)
+ LEA _profin(SB), A0 /* force loading of profile */
+ BRA loop
+
+TEXT _savearg(SB), 1, $0
+ RTS
+
+TEXT _callpc(SB), 1, $0
+ MOVL argp+0(FP), A0
+ MOVL 4(A0), R0
+ RTS
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/68020/memccpy.s b/sys/src/libc/68020/memccpy.s
new file mode 100755
index 000000000..3e494e915
--- /dev/null
+++ b/sys/src/libc/68020/memccpy.s
@@ -0,0 +1,30 @@
+ TEXT memccpy(SB),$0
+ MOVL n+12(FP),R0
+ BEQ ret
+ MOVL s1+0(FP),A2
+ MOVL s2+4(FP),A1
+ MOVL c+8(FP),R1
+ BEQ l2
+
+/*
+ * general case
+ */
+l1: MOVB (A1)+,R2
+ MOVB R2,(A2)+
+ CMPB R2,R1
+ BEQ eq
+ SUBL $1,R0
+ BNE l1
+ RTS
+
+/*
+ * special case for null character
+ */
+l2: MOVB (A1)+,(A2)+
+ BEQ eq
+ SUBL $1,R0
+ BNE l2
+ RTS
+
+eq: MOVL A2,R0
+ret: RTS
diff --git a/sys/src/libc/68020/memchr.s b/sys/src/libc/68020/memchr.s
new file mode 100755
index 000000000..ea2584df8
--- /dev/null
+++ b/sys/src/libc/68020/memchr.s
@@ -0,0 +1,15 @@
+ TEXT memchr(SB),$0
+ MOVL n+8(FP),R0
+ BEQ ret
+ MOVL s1+0(FP),A1
+ MOVL c+4(FP),R1
+
+l1: CMPB R1,(A1)+
+ BEQ eq
+ SUBL $1,R0
+ BNE l1
+ RTS
+
+eq: MOVL A1,R0
+ SUBL $1,R0
+ret: RTS
diff --git a/sys/src/libc/68020/memcmp.s b/sys/src/libc/68020/memcmp.s
new file mode 100755
index 000000000..97d208e3b
--- /dev/null
+++ b/sys/src/libc/68020/memcmp.s
@@ -0,0 +1,18 @@
+ TEXT memcmp(SB),$0
+ MOVL n+8(FP),R0
+ BEQ ret
+ MOVL s1+0(FP),A2
+ MOVL s2+4(FP),A1
+
+l1: CMPB (A1)+,(A2)+
+ BNE neq
+ SUBL $1,R0
+ BNE l1
+ RTS
+
+neq: BCS gtr
+ MOVL $-1,R0
+ RTS
+
+gtr: MOVL $1,R0
+ret: RTS
diff --git a/sys/src/libc/68020/memcpy.s b/sys/src/libc/68020/memcpy.s
new file mode 100755
index 000000000..2a37573da
--- /dev/null
+++ b/sys/src/libc/68020/memcpy.s
@@ -0,0 +1,98 @@
+ TEXT memcpy(SB), $0
+
+ MOVL n+8(FP),R0
+ BEQ return
+ BGT ok
+ MOVL 0, R0
+ok:
+ MOVL s1+0(FP),A2
+ MOVL s2+4(FP),A1
+
+ CMPL A2,A1
+ BHI back
+
+/*
+ * speed depends on source allignment
+ * destination allignment is secondary
+ * byte-at-a-time foreward copy to
+ * get source (A1) alligned.
+ */
+f1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ f2
+ SUBL $1, R0
+ BLT return
+ MOVB (A1)+, (A2)+
+ BRA f1
+/*
+ * quad-long-at-a-time forward copy
+ */
+f2:
+ SUBL $16, R0
+ BLT f3
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ BRA f2
+
+/*
+ * cleanup byte-at-a-time
+ */
+f3:
+ ADDL $15, R0
+ BLT return
+f4:
+ MOVB (A1)+, (A2)+
+ SUBL $1, R0
+ BGE f4
+ BRA return
+
+return:
+ MOVL s1+0(FP),R0
+ RTS
+
+/*
+ * everything the same, but
+ * copy backwards
+ */
+back:
+ ADDL R0, A1
+ ADDL R0, A2
+
+/*
+ * byte-at-a-time backward copy to
+ * get source (A1) alligned.
+ */
+b1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ b2
+ SUBL $1, R0
+ BLT return
+ MOVB -(A1), -(A2)
+ BRA b1
+/*
+ * quad-long-at-a-time backward copy
+ */
+b2:
+ SUBL $16, R0
+ BLT b3
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ BRA b2
+
+/*
+ * cleanup byte-at-a-time backward
+ */
+b3:
+ ADDL $15, R0
+ BLT return
+b4:
+ MOVB -(A1), -(A2)
+ SUBL $1, R0
+ BGE b4
+ BRA return
diff --git a/sys/src/libc/68020/memmove.s b/sys/src/libc/68020/memmove.s
new file mode 100755
index 000000000..482b3d690
--- /dev/null
+++ b/sys/src/libc/68020/memmove.s
@@ -0,0 +1,99 @@
+ TEXT memmove(SB), $0
+move:
+
+ MOVL n+8(FP),R0
+ BEQ return
+ BGT ok
+ MOVL 0, R0
+ok:
+ MOVL s1+0(FP),A2
+ MOVL s2+4(FP),A1
+
+ CMPL A2,A1
+ BHI back
+
+/*
+ * speed depends on source allignment
+ * destination allignment is secondary
+ * byte-at-a-time foreward copy to
+ * get source (A1) alligned.
+ */
+f1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ f2
+ SUBL $1, R0
+ BLT return
+ MOVB (A1)+, (A2)+
+ BRA f1
+/*
+ * quad-long-at-a-time forward copy
+ */
+f2:
+ SUBL $16, R0
+ BLT f3
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ MOVL (A1)+, (A2)+
+ BRA f2
+
+/*
+ * cleanup byte-at-a-time
+ */
+f3:
+ ADDL $15, R0
+ BLT return
+f4:
+ MOVB (A1)+, (A2)+
+ SUBL $1, R0
+ BGE f4
+ BRA return
+
+return:
+ MOVL s1+0(FP),R0
+ RTS
+
+/*
+ * everything the same, but
+ * copy backwards
+ */
+back:
+ ADDL R0, A1
+ ADDL R0, A2
+
+/*
+ * byte-at-a-time backward copy to
+ * get source (A1) alligned.
+ */
+b1:
+ MOVL A1, R1
+ ANDL $3, R1
+ BEQ b2
+ SUBL $1, R0
+ BLT return
+ MOVB -(A1), -(A2)
+ BRA b1
+/*
+ * quad-long-at-a-time backward copy
+ */
+b2:
+ SUBL $16, R0
+ BLT b3
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ MOVL -(A1), -(A2)
+ BRA b2
+
+/*
+ * cleanup byte-at-a-time backward
+ */
+b3:
+ ADDL $15, R0
+ BLT return
+b4:
+ MOVB -(A1), -(A2)
+ SUBL $1, R0
+ BGE b4
+ BRA return
diff --git a/sys/src/libc/68020/memset.s b/sys/src/libc/68020/memset.s
new file mode 100755
index 000000000..d4b773416
--- /dev/null
+++ b/sys/src/libc/68020/memset.s
@@ -0,0 +1,47 @@
+ TEXT memset(SB), $0
+ MOVL n+8(FP), R0
+ BLE return
+ MOVL s1+0(FP), A1
+ CLRL R1
+ MOVB c+7(FP), R1
+ BEQ l1
+
+/*
+ * create 4 replicated copies
+ * of the byte in R1
+ */
+ MOVL R1, R2
+ ASLL $8, R2
+ ORL R2, R1
+ MOVL R1, R2
+ SWAP R2
+ ORL R2, R1
+
+/*
+ * quad-long-at-a-time set
+ * destination allignment is not
+ * very important.
+ */
+l1:
+ SUBL $16, R0
+ BLT l2
+ MOVL R1, (A1)+
+ MOVL R1, (A1)+
+ MOVL R1, (A1)+
+ MOVL R1, (A1)+
+ BRA l1
+
+/*
+ * cleanup byte-at-a-time
+ */
+l2:
+ ADDL $15, R0
+ BLT return
+l3:
+ MOVB R1, (A1)+
+ SUBL $1, R0
+ BGE l3
+
+return:
+ MOVL s1+0(FP),R0
+ RTS
diff --git a/sys/src/libc/68020/mkfile b/sys/src/libc/68020/mkfile
new file mode 100755
index 000000000..bdd359164
--- /dev/null
+++ b/sys/src/libc/68020/mkfile
@@ -0,0 +1,53 @@
+objtype=68020
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ getcallerpc.$O\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memccpy.s\
+ memchr.s\
+ memcmp.s\
+ memcpy.s\
+ memmove.s\
+ memset.s\
+ scale.s\
+ setjmp.s\
+ sqrt.s\
+ strcat.s\
+ strchr.s\
+ strcmp.s\
+ strcpy.s\
+ strlen.s\
+ tas.s\
+ vlop.s\
+
+CFILES=\
+ cycles.c\
+ notejmp.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
+
+install:V: install.68881
+installall:V:
+ mk install
+clean:V: clean.68881
+nuke:V: clean.68881
+update:V: update.68881
+
+%.68881:V:
+ cd 68881
+ mk $stem
diff --git a/sys/src/libc/68020/notejmp.c b/sys/src/libc/68020/notejmp.c
new file mode 100755
index 000000000..61562d441
--- /dev/null
+++ b/sys/src/libc/68020/notejmp.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#define UREGVARSZ 4 /* not right but doesn't matter */
+#include <ureg.h>
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ r->r0 = ret;
+ if(ret == 0)
+ r->r0 = 1;
+ r->pc = j[JMPBUFPC];
+ r->usp = j[JMPBUFSP] + 4;
+ noted(NCONT);
+}
diff --git a/sys/src/libc/68020/scale.s b/sys/src/libc/68020/scale.s
new file mode 100755
index 000000000..0e0e50a86
--- /dev/null
+++ b/sys/src/libc/68020/scale.s
@@ -0,0 +1,4 @@
+ TEXT fscale(SB), $0
+ FMOVED a+0(FP), F0
+ FSCALEL x+8(FP), F0
+ RTS
diff --git a/sys/src/libc/68020/setjmp.s b/sys/src/libc/68020/setjmp.s
new file mode 100755
index 000000000..827b71fc1
--- /dev/null
+++ b/sys/src/libc/68020/setjmp.s
@@ -0,0 +1,15 @@
+TEXT setjmp(SB), 1, $0
+ MOVL b+0(FP), A0
+ MOVL A7, (A0)+
+ MOVL (A7), (A0)
+ CLRL R0
+ RTS
+
+TEXT longjmp(SB), 1, $0
+ MOVL b+0(FP), A0
+ MOVL r+4(FP), R0
+ BNE ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVL $1, R0 /* bless their pointed heads */
+ok: MOVL (A0)+, A7
+ MOVL (A0), (A7)
+ RTS
diff --git a/sys/src/libc/68020/sqrt.s b/sys/src/libc/68020/sqrt.s
new file mode 100755
index 000000000..5109bc255
--- /dev/null
+++ b/sys/src/libc/68020/sqrt.s
@@ -0,0 +1,3 @@
+ TEXT sqrt(SB),$0
+ FSQRTD a+0(FP),F0
+ RTS
diff --git a/sys/src/libc/68020/strcat.s b/sys/src/libc/68020/strcat.s
new file mode 100755
index 000000000..95821408b
--- /dev/null
+++ b/sys/src/libc/68020/strcat.s
@@ -0,0 +1,15 @@
+ TEXT strcat(SB), $0
+ MOVL s1+0(FP), A2
+ MOVL s2+4(FP), A1
+
+l1: TSTB (A2)+
+ BNE l1
+
+ MOVB (A1)+, -1(A2)
+ BEQ done
+
+l2: MOVB (A1)+, (A2)+
+ BNE l2
+
+done: MOVL s1+0(FP), R0
+ RTS
diff --git a/sys/src/libc/68020/strchr.s b/sys/src/libc/68020/strchr.s
new file mode 100755
index 000000000..a5d4be2c6
--- /dev/null
+++ b/sys/src/libc/68020/strchr.s
@@ -0,0 +1,27 @@
+ TEXT strchr(SB), $0
+
+ MOVL s+0(FP), A0
+ MOVB c+7(FP), R2
+ BEQ null
+
+l:
+ MOVB (A0)+, R1
+ BEQ out
+ CMPB R1, R2
+ BNE l
+
+ MOVL A0, R0
+ ADDL $-1, R0
+ RTS
+
+out:
+ CLRL R0
+ RTS
+
+null:
+ TSTB (A0)+
+ BNE null
+
+ MOVL A0, R0
+ ADDL $-1, R0
+ RTS
diff --git a/sys/src/libc/68020/strcmp.s b/sys/src/libc/68020/strcmp.s
new file mode 100755
index 000000000..c6072efd7
--- /dev/null
+++ b/sys/src/libc/68020/strcmp.s
@@ -0,0 +1,20 @@
+ TEXT strcmp(SB), $0
+ MOVL s1+0(FP), A2
+ MOVL s2+4(FP), A1
+
+l1: MOVB (A1)+, R0
+ BEQ end
+ CMPB R0, (A2)+
+ BEQ l1
+
+ BCS gtr
+ MOVL $-1, R0
+ RTS
+
+gtr: MOVL $1, R0
+ RTS
+
+end: TSTB (A2)
+ BNE gtr
+ CLRL R0
+ RTS
diff --git a/sys/src/libc/68020/strcpy.s b/sys/src/libc/68020/strcpy.s
new file mode 100755
index 000000000..ce5c30bdb
--- /dev/null
+++ b/sys/src/libc/68020/strcpy.s
@@ -0,0 +1,10 @@
+ TEXT strcpy(SB), $0
+
+ MOVL s1+0(FP), A2
+ MOVL s2+4(FP), A1
+
+l1: MOVB (A1)+, (A2)+
+ BNE l1
+
+ MOVL s1+0(FP), R0
+ RTS
diff --git a/sys/src/libc/68020/strlen.s b/sys/src/libc/68020/strlen.s
new file mode 100755
index 000000000..4d787c704
--- /dev/null
+++ b/sys/src/libc/68020/strlen.s
@@ -0,0 +1,18 @@
+ TEXT strlen(SB), $0
+ MOVL s+0(FP), A1
+
+ TSTB (A1)+
+ BEQ null
+ MOVL A1, A2
+
+l1:
+ TSTB (A1)+
+ BNE l1
+
+ SUBL A2, A1
+ MOVL A1, R0
+ RTS
+
+null:
+ MOVL $0, R0
+ RTS
diff --git a/sys/src/libc/68020/tas.s b/sys/src/libc/68020/tas.s
new file mode 100755
index 000000000..769472f71
--- /dev/null
+++ b/sys/src/libc/68020/tas.s
@@ -0,0 +1,9 @@
+TEXT _tas(SB), $0
+
+ MOVL $0, R0
+ MOVL a+0(FP), A0
+ TAS (A0)
+ BEQ tas_1
+ MOVL $1, R0
+tas_1:
+ RTS
diff --git a/sys/src/libc/68020/vlop.s b/sys/src/libc/68020/vlop.s
new file mode 100755
index 000000000..2f34fd645
--- /dev/null
+++ b/sys/src/libc/68020/vlop.s
@@ -0,0 +1,23 @@
+TEXT _mulv(SB), $0
+ MOVL r+0(FP), A0
+ MOVL a+8(FP), R0
+
+ WORD $0x4c2f
+ WORD $0x0401
+ WORD $0x0014
+/*
+ * MULUL b+16(FP), R0:R1
+ * philw made me do it!
+ */
+
+ MOVL a+4(FP), R2
+ MULUL b+16(FP), R2
+ ADDL R2, R1
+
+ MOVL a+8(FP), R2
+ MULUL b+12(FP), R2
+ ADDL R2, R1
+
+ MOVL R1, (A0)+
+ MOVL R0, (A0)
+ RTS
diff --git a/sys/src/libc/68020/vlrt.c b/sys/src/libc/68020/vlrt.c
new file mode 100755
index 000000000..cfad2afc0
--- /dev/null
+++ b/sys/src/libc/68020/vlrt.c
@@ -0,0 +1,730 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ union
+ {
+ struct
+ {
+ ulong hi;
+ ulong lo;
+ };
+ struct
+ {
+ ushort hims;
+ ushort hils;
+ ushort loms;
+ ushort lols;
+ };
+ };
+};
+
+void abort(void);
+
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_negv(Vlong *r, Vlong a)
+{
+ if(a.lo == 0) {
+ r->hi = -a.hi;
+ r->lo = 0;
+ return;
+ }
+ r->hi = ~a.hi;
+ r->lo = -a.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u = *ret;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
diff --git a/sys/src/libc/9sys/abort.c b/sys/src/libc/9sys/abort.c
new file mode 100755
index 000000000..b0fd74cd5
--- /dev/null
+++ b/sys/src/libc/9sys/abort.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+void
+abort(void)
+{
+ while(*(int*)0)
+ ;
+}
diff --git a/sys/src/libc/9sys/access.c b/sys/src/libc/9sys/access.c
new file mode 100755
index 000000000..c9fee3432
--- /dev/null
+++ b/sys/src/libc/9sys/access.c
@@ -0,0 +1,33 @@
+#include <u.h>
+#include <libc.h>
+
+int
+access(char *name, int mode)
+{
+ int fd;
+ Dir *db;
+ static char omode[] = {
+ 0,
+ OEXEC,
+ OWRITE,
+ ORDWR,
+ OREAD,
+ OEXEC, /* only approximate */
+ ORDWR,
+ ORDWR /* only approximate */
+ };
+
+ if(mode == AEXIST){
+ db = dirstat(name);
+ free(db);
+ if(db != nil)
+ return 0;
+ return -1;
+ }
+ fd = open(name, omode[mode&7]);
+ if(fd >= 0){
+ close(fd);
+ return 0;
+ }
+ return -1;
+}
diff --git a/sys/src/libc/9sys/announce.c b/sys/src/libc/9sys/announce.c
new file mode 100755
index 000000000..b2252ed21
--- /dev/null
+++ b/sys/src/libc/9sys/announce.c
@@ -0,0 +1,274 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+static int nettrans(char*, char*, int na, char*, int);
+
+enum
+{
+ Maxpath= 256,
+};
+
+/*
+ * announce a network service.
+ */
+int
+announce(char *addr, char *dir)
+{
+ int ctl, n, m;
+ char buf[Maxpath];
+ char buf2[Maxpath];
+ char netdir[Maxpath];
+ char naddr[Maxpath];
+ char *cp;
+
+ /*
+ * translate the address
+ */
+ if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
+ return -1;
+
+ /*
+ * get a control channel
+ */
+ ctl = open(netdir, ORDWR);
+ if(ctl<0){
+ werrstr("announce opening %s: %r", netdir);
+ return -1;
+ }
+ cp = strrchr(netdir, '/');
+ if(cp == nil){
+ werrstr("announce arg format %s", netdir);
+ close(ctl);
+ return -1;
+ }
+ *cp = 0;
+
+ /*
+ * find out which line we have
+ */
+ n = snprint(buf, sizeof(buf), "%s/", netdir);
+ m = read(ctl, &buf[n], sizeof(buf)-n-1);
+ if(m <= 0){
+ werrstr("announce reading %s: %r", netdir);
+ close(ctl);
+ return -1;
+ }
+ buf[n+m] = 0;
+
+ /*
+ * make the call
+ */
+ n = snprint(buf2, sizeof(buf2), "announce %s", naddr);
+ if(write(ctl, buf2, n)!=n){
+ werrstr("announce writing %s: %r", netdir);
+ close(ctl);
+ return -1;
+ }
+
+ /*
+ * return directory etc.
+ */
+ if(dir){
+ strncpy(dir, buf, NETPATHLEN);
+ dir[NETPATHLEN-1] = 0;
+ }
+ return ctl;
+}
+
+/*
+ * listen for an incoming call
+ */
+int
+listen(char *dir, char *newdir)
+{
+ int ctl, n, m;
+ char buf[Maxpath];
+ char *cp;
+
+ /*
+ * open listen, wait for a call
+ */
+ snprint(buf, sizeof(buf), "%s/listen", dir);
+ ctl = open(buf, ORDWR);
+ if(ctl < 0){
+ werrstr("listen opening %s: %r", buf);
+ return -1;
+ }
+
+ /*
+ * find out which line we have
+ */
+ strncpy(buf, dir, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
+ cp = strrchr(buf, '/');
+ if(cp == nil){
+ close(ctl);
+ werrstr("listen arg format %s", dir);
+ return -1;
+ }
+ *++cp = 0;
+ n = cp-buf;
+ m = read(ctl, cp, sizeof(buf) - n - 1);
+ if(m <= 0){
+ close(ctl);
+ werrstr("listen reading %s/listen: %r", dir);
+ return -1;
+ }
+ buf[n+m] = 0;
+
+ /*
+ * return directory etc.
+ */
+ if(newdir){
+ strncpy(newdir, buf, NETPATHLEN);
+ newdir[NETPATHLEN-1] = 0;
+ }
+ return ctl;
+
+}
+
+/*
+ * accept a call, return an fd to the open data file
+ */
+int
+accept(int ctl, char *dir)
+{
+ char buf[Maxpath];
+ char *num;
+ long n;
+
+ num = strrchr(dir, '/');
+ if(num == nil)
+ num = dir;
+ else
+ num++;
+
+ n = snprint(buf, sizeof(buf), "accept %s", num);
+ write(ctl, buf, n); /* ignore return value, network might not need accepts */
+
+ snprint(buf, sizeof(buf), "%s/data", dir);
+ return open(buf, ORDWR);
+}
+
+/*
+ * reject a call, tell device the reason for the rejection
+ */
+int
+reject(int ctl, char *dir, char *cause)
+{
+ char buf[Maxpath];
+ char *num;
+ long n;
+
+ num = strrchr(dir, '/');
+ if(num == 0)
+ num = dir;
+ else
+ num++;
+ snprint(buf, sizeof(buf), "reject %s %s", num, cause);
+ n = strlen(buf);
+ if(write(ctl, buf, n) != n)
+ return -1;
+ return 0;
+}
+
+/*
+ * perform the identity translation (in case we can't reach cs)
+ */
+static int
+identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
+{
+ char proto[Maxpath];
+ char *p;
+
+ USED(nf);
+
+ /* parse the protocol */
+ strncpy(proto, addr, sizeof(proto));
+ proto[sizeof(proto)-1] = 0;
+ p = strchr(proto, '!');
+ if(p)
+ *p++ = 0;
+
+ snprint(file, nf, "%s/%s/clone", netdir, proto);
+ strncpy(naddr, p, na);
+ naddr[na-1] = 0;
+
+ return 1;
+}
+
+/*
+ * call up the connection server and get a translation
+ */
+static int
+nettrans(char *addr, char *naddr, int na, char *file, int nf)
+{
+ int i, fd;
+ char buf[Maxpath];
+ char netdir[Maxpath];
+ char *p, *p2;
+ long n;
+
+ /*
+ * parse, get network directory
+ */
+ p = strchr(addr, '!');
+ if(p == 0){
+ werrstr("bad dial string: %s", addr);
+ return -1;
+ }
+ if(*addr != '/'){
+ strncpy(netdir, "/net", sizeof(netdir));
+ netdir[sizeof(netdir) - 1] = 0;
+ } else {
+ for(p2 = p; *p2 != '/'; p2--)
+ ;
+ i = p2 - addr;
+ if(i == 0 || i >= sizeof(netdir)){
+ werrstr("bad dial string: %s", addr);
+ return -1;
+ }
+ strncpy(netdir, addr, i);
+ netdir[i] = 0;
+ addr = p2 + 1;
+ }
+
+ /*
+ * ask the connection server
+ */
+ snprint(buf, sizeof(buf), "%s/cs", netdir);
+ fd = open(buf, ORDWR);
+ if(fd < 0)
+ return identtrans(netdir, addr, naddr, na, file, nf);
+ if(write(fd, addr, strlen(addr)) < 0){
+ close(fd);
+ return -1;
+ }
+ seek(fd, 0, 0);
+ n = read(fd, buf, sizeof(buf)-1);
+ close(fd);
+ if(n <= 0)
+ return -1;
+ buf[n] = 0;
+
+ /*
+ * parse the reply
+ */
+ p = strchr(buf, ' ');
+ if(p == 0)
+ return -1;
+ *p++ = 0;
+ strncpy(naddr, p, na);
+ naddr[na-1] = 0;
+
+ if(buf[0] == '/'){
+ p = strchr(buf+1, '/');
+ if(p == nil)
+ p = buf;
+ else
+ p++;
+ }
+ snprint(file, nf, "%s/%s", netdir, p);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/convD2M.c b/sys/src/libc/9sys/convD2M.c
new file mode 100755
index 000000000..dfc0e5d79
--- /dev/null
+++ b/sys/src/libc/9sys/convD2M.c
@@ -0,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+uint
+sizeD2M(Dir *d)
+{
+ char *sv[4];
+ int i, ns;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++)
+ if(sv[i])
+ ns += strlen(sv[i]);
+
+ return STATFIXLEN + ns;
+}
+
+uint
+convD2M(Dir *d, uchar *buf, uint nbuf)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns, nsv[4], ss;
+
+ if(nbuf < BIT16SZ)
+ return 0;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++){
+ if(sv[i])
+ nsv[i] = strlen(sv[i]);
+ else
+ nsv[i] = 0;
+ ns += nsv[i];
+ }
+
+ ss = STATFIXLEN + ns;
+
+ /* set size before erroring, so user can know how much is needed */
+ /* note that length excludes count field itself */
+ PBIT16(p, ss-BIT16SZ);
+ p += BIT16SZ;
+
+ if(ss > nbuf)
+ return BIT16SZ;
+
+ PBIT16(p, d->type);
+ p += BIT16SZ;
+ PBIT32(p, d->dev);
+ p += BIT32SZ;
+ PBIT8(p, d->qid.type);
+ p += BIT8SZ;
+ PBIT32(p, d->qid.vers);
+ p += BIT32SZ;
+ PBIT64(p, d->qid.path);
+ p += BIT64SZ;
+ PBIT32(p, d->mode);
+ p += BIT32SZ;
+ PBIT32(p, d->atime);
+ p += BIT32SZ;
+ PBIT32(p, d->mtime);
+ p += BIT32SZ;
+ PBIT64(p, d->length);
+ p += BIT64SZ;
+
+ for(i = 0; i < 4; i++){
+ ns = nsv[i];
+ if(p + ns + BIT16SZ > ebuf)
+ return 0;
+ PBIT16(p, ns);
+ p += BIT16SZ;
+ if(ns)
+ memmove(p, sv[i], ns);
+ p += ns;
+ }
+
+ if(ss != p - buf)
+ return 0;
+
+ return p - buf;
+}
diff --git a/sys/src/libc/9sys/convM2D.c b/sys/src/libc/9sys/convM2D.c
new file mode 100755
index 000000000..6f4b4bd93
--- /dev/null
+++ b/sys/src/libc/9sys/convM2D.c
@@ -0,0 +1,94 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+statcheck(uchar *buf, uint nbuf)
+{
+ uchar *ebuf;
+ int i;
+
+ ebuf = buf + nbuf;
+
+ if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
+ return -1;
+
+ buf += STATFIXLEN - 4 * BIT16SZ;
+
+ for(i = 0; i < 4; i++){
+ if(buf + BIT16SZ > ebuf)
+ return -1;
+ buf += BIT16SZ + GBIT16(buf);
+ }
+
+ if(buf != ebuf)
+ return -1;
+
+ return 0;
+}
+
+static char nullstring[] = "";
+
+uint
+convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns;
+
+ if(nbuf < STATFIXLEN)
+ return 0;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ p += BIT16SZ; /* ignore size */
+ d->type = GBIT16(p);
+ p += BIT16SZ;
+ d->dev = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.type = GBIT8(p);
+ p += BIT8SZ;
+ d->qid.vers = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.path = GBIT64(p);
+ p += BIT64SZ;
+ d->mode = GBIT32(p);
+ p += BIT32SZ;
+ d->atime = GBIT32(p);
+ p += BIT32SZ;
+ d->mtime = GBIT32(p);
+ p += BIT32SZ;
+ d->length = GBIT64(p);
+ p += BIT64SZ;
+
+ for(i = 0; i < 4; i++){
+ if(p + BIT16SZ > ebuf)
+ return 0;
+ ns = GBIT16(p);
+ p += BIT16SZ;
+ if(p + ns > ebuf)
+ return 0;
+ if(strs){
+ sv[i] = strs;
+ memmove(strs, p, ns);
+ strs += ns;
+ *strs++ = '\0';
+ }
+ p += ns;
+ }
+
+ if(strs){
+ d->name = sv[0];
+ d->uid = sv[1];
+ d->gid = sv[2];
+ d->muid = sv[3];
+ }else{
+ d->name = nullstring;
+ d->uid = nullstring;
+ d->gid = nullstring;
+ d->muid = nullstring;
+ }
+
+ return p - buf;
+}
diff --git a/sys/src/libc/9sys/convM2S.c b/sys/src/libc/9sys/convM2S.c
new file mode 100755
index 000000000..fcdcd42d6
--- /dev/null
+++ b/sys/src/libc/9sys/convM2S.c
@@ -0,0 +1,315 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+ uint n;
+
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ - 1;
+ if(p+n+1 > ep)
+ return nil;
+ /* move it down, on top of count, to make room for '\0' */
+ memmove(p, p + 1, n);
+ p[n] = '\0';
+ *s = (char*)p;
+ p += n+1;
+ return p;
+}
+
+static
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+ if(p+QIDSZ > ep)
+ return nil;
+ q->type = GBIT8(p);
+ p += BIT8SZ;
+ q->vers = GBIT32(p);
+ p += BIT32SZ;
+ q->path = GBIT64(p);
+ p += BIT64SZ;
+ return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ * 1. message size field is incorrect
+ * 2. input buffer too short for its own data (counts too long, etc.)
+ * 3. too many names or qids
+ * gqid() and gstring() return nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+ uchar *p, *ep;
+ uint i, size;
+
+ p = ap;
+ ep = p + nap;
+
+ if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+ return 0;
+ size = GBIT32(p);
+ p += BIT32SZ;
+
+ if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+ return 0;
+
+ f->type = GBIT8(p);
+ p += BIT8SZ;
+ f->tag = GBIT16(p);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+ case Tflush:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->oldtag = GBIT16(p);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+ case Tattach:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+ case Twalk:
+ if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->newfid = GBIT32(p);
+ p += BIT32SZ;
+ f->nwname = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++){
+ p = gstring(p, ep, &f->wname[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Topen:
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->name);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->perm = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ if(p+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+/*
+ */
+ case Rversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+ case Rerror:
+ p = gstring(p, ep, &f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = gqid(p, ep, &f->aqid);
+ if(p == nil)
+ break;
+ break;
+
+ case Rattach:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ break;
+
+ case Rwalk:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nwqid = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++){
+ p = gqid(p, ep, &f->wqid[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->iounit = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Rwrite:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ case Rremove:
+ break;
+
+ case Rstat:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+
+ if(p==nil || p>ep)
+ return 0;
+ if(ap+size == p)
+ return size;
+ return 0;
+}
diff --git a/sys/src/libc/9sys/convS2M.c b/sys/src/libc/9sys/convS2M.c
new file mode 100755
index 000000000..fb5f596ce
--- /dev/null
+++ b/sys/src/libc/9sys/convS2M.c
@@ -0,0 +1,389 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+ uint n;
+
+ if(s == nil){
+ PBIT16(p, 0);
+ p += BIT16SZ;
+ return p;
+ }
+
+ n = strlen(s);
+ /*
+ * We are moving the string before the length,
+ * so you can S2M a struct into an existing message
+ */
+ memmove(p + BIT16SZ, s, n);
+ PBIT16(p, n);
+ p += n + BIT16SZ;
+ return p;
+}
+
+static
+uchar*
+pqid(uchar *p, Qid *q)
+{
+ PBIT8(p, q->type);
+ p += BIT8SZ;
+ PBIT32(p, q->vers);
+ p += BIT32SZ;
+ PBIT64(p, q->path);
+ p += BIT64SZ;
+ return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+ if(s == nil)
+ return BIT16SZ;
+
+ return BIT16SZ+strlen(s);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+ uint n;
+ int i;
+
+ n = 0;
+ n += BIT32SZ; /* size */
+ n += BIT8SZ; /* type */
+ n += BIT16SZ; /* tag */
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+ case Tflush:
+ n += BIT16SZ;
+ break;
+
+ case Tauth:
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Tattach:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Twalk:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += BIT16SZ;
+ for(i=0; i<f->nwname; i++)
+ n += stringsz(f->wname[i]);
+ break;
+
+ case Topen:
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tcreate:
+ n += BIT32SZ;
+ n += stringsz(f->name);
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tread:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ break;
+
+ case Twrite:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ n += BIT32SZ;
+ break;
+
+ case Tstat:
+ n += BIT32SZ;
+ break;
+
+ case Twstat:
+ n += BIT32SZ;
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+ case Rerror:
+ n += stringsz(f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ n += QIDSZ;
+ break;
+
+ case Rattach:
+ n += QIDSZ;
+ break;
+
+ case Rwalk:
+ n += BIT16SZ;
+ n += f->nwqid*QIDSZ;
+ break;
+
+ case Ropen:
+ case Rcreate:
+ n += QIDSZ;
+ n += BIT32SZ;
+ break;
+
+ case Rread:
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Rwrite:
+ n += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ return n;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+ uchar *p;
+ uint i, size;
+
+ size = sizeS2M(f);
+ if(size == 0)
+ return 0;
+ if(size > nap)
+ return 0;
+
+ p = (uchar*)ap;
+
+ PBIT32(p, size);
+ p += BIT32SZ;
+ PBIT8(p, f->type);
+ p += BIT8SZ;
+ PBIT16(p, f->tag);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+ case Tflush:
+ PBIT16(p, f->oldtag);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Tattach:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Twalk:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->newfid);
+ p += BIT32SZ;
+ PBIT16(p, f->nwname);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++)
+ p = pstring(p, f->wname[i]);
+ break;
+
+ case Topen:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ p = pstring(p, f->name);
+ PBIT32(p, f->perm);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+ case Rerror:
+ p = pstring(p, f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = pqid(p, &f->aqid);
+ break;
+
+ case Rattach:
+ p = pqid(p, &f->qid);
+ break;
+
+ case Rwalk:
+ PBIT16(p, f->nwqid);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++)
+ p = pqid(p, &f->wqid[i]);
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = pqid(p, &f->qid);
+ PBIT32(p, f->iounit);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Rwrite:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ if(size != p-ap)
+ return 0;
+ return size;
+}
diff --git a/sys/src/libc/9sys/cputime.c b/sys/src/libc/9sys/cputime.c
new file mode 100755
index 000000000..1e05634aa
--- /dev/null
+++ b/sys/src/libc/9sys/cputime.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+#define HZ 1000
+
+double
+cputime(void)
+{
+ long t[4];
+ long times(long*);
+ int i;
+
+ times(t);
+ for(i=1; i<4; i++)
+ t[0] += t[i];
+ return t[0] / (double)HZ;
+}
diff --git a/sys/src/libc/9sys/ctime.c b/sys/src/libc/9sys/ctime.c
new file mode 100755
index 000000000..d841b64e9
--- /dev/null
+++ b/sys/src/libc/9sys/ctime.c
@@ -0,0 +1,304 @@
+/*
+ * This routine converts time as follows.
+ * The epoch is 0000 Jan 1 1970 GMT.
+ * The argument time is in seconds since then.
+ * The localtime(t) entry returns a pointer to an array
+ * containing
+ *
+ * seconds (0-59)
+ * minutes (0-59)
+ * hours (0-23)
+ * day of month (1-31)
+ * month (0-11)
+ * year-1970
+ * weekday (0-6, Sun is 0)
+ * day of the year
+ * daylight savings flag
+ *
+ * The routine gets the daylight savings time from the environment.
+ *
+ * asctime(tvec))
+ * where tvec is produced by localtime
+ * returns a ptr to a character string
+ * that has the ascii time in the form
+ *
+ * \\
+ * Thu Jan 01 00:00:00 GMT 1970n0
+ * 012345678901234567890123456789
+ * 0 1 2
+ *
+ * ctime(t) just calls localtime, then asctime.
+ */
+
+#include <u.h>
+#include <libc.h>
+
+static char dmsize[12] =
+{
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * The following table is used for 1974 and 1975 and
+ * gives the day number of the first day after the Sunday of the
+ * change.
+ */
+
+static int dysize(int);
+static void ct_numb(char*, int);
+
+#define TZSIZE 150
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+char*
+ctime(long t)
+{
+ return asctime(localtime(t));
+}
+
+Tm*
+localtime(long tim)
+{
+ Tm *ct;
+ long t, *p;
+ int dlflag;
+
+ if(timezone.stname[0] == 0)
+ readtimezone();
+ t = tim + timezone.stdiff;
+ dlflag = 0;
+ for(p = timezone.dlpairs; *p; p += 2)
+ if(t >= p[0])
+ if(t < p[1]) {
+ t = tim + timezone.dldiff;
+ dlflag++;
+ break;
+ }
+ ct = gmtime(t);
+ if(dlflag){
+ strcpy(ct->zone, timezone.dlname);
+ ct->tzoff = timezone.dldiff;
+ } else {
+ strcpy(ct->zone, timezone.stname);
+ ct->tzoff = timezone.stdiff;
+ }
+ return ct;
+}
+
+Tm*
+gmtime(long tim)
+{
+ int d0, d1;
+ long hms, day;
+ static Tm xtime;
+
+ /*
+ * break initial number into days
+ */
+ hms = tim % 86400L;
+ day = tim / 86400L;
+ if(hms < 0) {
+ hms += 86400L;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ xtime.sec = hms % 60;
+ d1 = hms / 60;
+ xtime.min = d1 % 60;
+ d1 /= 60;
+ xtime.hour = d1;
+
+ /*
+ * day is the day number.
+ * generate day of the week.
+ * The addend is 4 mod 7 (1/1/1970 was Thursday)
+ */
+
+ xtime.wday = (day + 7340036L) % 7;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d1 = 1970; day >= dysize(d1); d1++)
+ day -= dysize(d1);
+ else
+ for (d1 = 1970; day < 0; d1--)
+ day += dysize(d1-1);
+ xtime.year = d1-1900;
+ xtime.yday = d0 = day;
+
+ /*
+ * generate month
+ */
+
+ if(dysize(d1) == 366)
+ dmsize[1] = 29;
+ for(d1 = 0; d0 >= dmsize[d1]; d1++)
+ d0 -= dmsize[d1];
+ dmsize[1] = 28;
+ xtime.mday = d0 + 1;
+ xtime.mon = d1;
+ strcpy(xtime.zone, "GMT");
+ return &xtime;
+}
+
+char*
+asctime(Tm *t)
+{
+ char *ncp;
+ static char cbuf[30];
+
+ strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
+ ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
+ cbuf[0] = *ncp++;
+ cbuf[1] = *ncp++;
+ cbuf[2] = *ncp;
+ ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
+ cbuf[4] = *ncp++;
+ cbuf[5] = *ncp++;
+ cbuf[6] = *ncp;
+ ct_numb(cbuf+8, t->mday);
+ ct_numb(cbuf+11, t->hour+100);
+ ct_numb(cbuf+14, t->min+100);
+ ct_numb(cbuf+17, t->sec+100);
+ ncp = t->zone;
+ cbuf[20] = *ncp++;
+ cbuf[21] = *ncp++;
+ cbuf[22] = *ncp;
+ if(t->year >= 100) {
+ cbuf[24] = '2';
+ cbuf[25] = '0';
+ }
+ ct_numb(cbuf+26, t->year+100);
+ return cbuf;
+}
+
+static
+dysize(int y)
+{
+
+ if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
+ return 366;
+ return 365;
+}
+
+static
+void
+ct_numb(char *cp, int n)
+{
+
+ cp[0] = ' ';
+ if(n >= 10)
+ cp[0] = (n/10)%10 + '0';
+ cp[1] = n%10 + '0';
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf)){
+ close(i);
+ goto error;
+ }
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
+}
diff --git a/sys/src/libc/9sys/dial.c b/sys/src/libc/9sys/dial.c
new file mode 100755
index 000000000..74fc6e6ae
--- /dev/null
+++ b/sys/src/libc/9sys/dial.c
@@ -0,0 +1,492 @@
+/*
+ * dial - connect to a service (parallel version)
+ */
+#include <u.h>
+#include <libc.h>
+
+typedef struct Conn Conn;
+typedef struct Dest Dest;
+typedef struct DS DS;
+
+enum
+{
+ Maxstring = 128,
+ Maxpath = 256,
+
+ Maxcsreply = 64*80, /* this is probably overly generous */
+ /*
+ * this should be a plausible slight overestimate for non-interactive
+ * use even if it's ridiculously long for interactive use.
+ */
+ Maxconnms = 20*60*1000, /* 20 minutes */
+};
+
+struct DS {
+ /* dist string */
+ char buf[Maxstring];
+ char *netdir;
+ char *proto;
+ char *rem;
+
+ /* other args */
+ char *local;
+ char *dir;
+ int *cfdp;
+};
+
+/*
+ * malloc these; they need to be writable by this proc & all children.
+ * the stack is private to each proc, and static allocation in the data
+ * segment would not permit concurrent dials within a multi-process program.
+ */
+struct Conn {
+ int pid;
+ int dead;
+
+ int dfd;
+ int cfd;
+ char dir[NETPATHLEN];
+ char err[ERRMAX];
+};
+struct Dest {
+ Conn *conn; /* allocated array */
+ Conn *connend;
+ int nkid;
+
+ QLock winlck;
+ int winner; /* index into conn[] */
+
+ char *nextaddr;
+ char addrlist[Maxcsreply];
+};
+
+static int call(char*, char*, DS*, Dest*, Conn*);
+static int csdial(DS*);
+static void _dial_string_parse(char*, DS*);
+
+
+/*
+ * the dialstring is of the form '[/net/]proto!dest'
+ */
+static int
+dialimpl(char *dest, char *local, char *dir, int *cfdp)
+{
+ DS ds;
+ int rv;
+ char err[ERRMAX], alterr[ERRMAX];
+
+ ds.local = local;
+ ds.dir = dir;
+ ds.cfdp = cfdp;
+
+ _dial_string_parse(dest, &ds);
+ if(ds.netdir)
+ return csdial(&ds);
+
+ ds.netdir = "/net";
+ rv = csdial(&ds);
+ if(rv >= 0)
+ return rv;
+ err[0] = '\0';
+ errstr(err, sizeof err);
+ if(strstr(err, "refused") != 0){
+ werrstr("%s", err);
+ return rv;
+ }
+ ds.netdir = "/net.alt";
+ rv = csdial(&ds);
+ if(rv >= 0)
+ return rv;
+
+ alterr[0] = 0;
+ errstr(alterr, sizeof alterr);
+ if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
+ werrstr("%s", err);
+ else
+ werrstr("%s", alterr);
+ return rv;
+}
+
+/*
+ * the thread library can't cope with rfork(RFMEM|RFPROC),
+ * so it must override this with a private version of dial.
+ */
+int (*_dial)(char *, char *, char *, int *) = dialimpl;
+
+int
+dial(char *dest, char *local, char *dir, int *cfdp)
+{
+ return (*_dial)(dest, local, dir, cfdp);
+}
+
+static int
+connsalloc(Dest *dp, int addrs)
+{
+ free(dp->conn);
+ dp->connend = nil;
+ assert(addrs > 0);
+
+ dp->conn = mallocz(addrs * sizeof *dp->conn, 1);
+ if(dp->conn == nil)
+ return -1;
+ dp->connend = dp->conn + addrs;
+ return 0;
+}
+
+static void
+freedest(Dest *dp)
+{
+ if (dp != nil) {
+ free(dp->conn);
+ free(dp);
+ }
+}
+
+static void
+closeopenfd(int *fdp)
+{
+ if (*fdp > 0) {
+ close(*fdp);
+ *fdp = -1;
+ }
+}
+
+static void
+notedeath(Dest *dp, char *exitsts)
+{
+ int i, n, pid;
+ char *fields[5]; /* pid + 3 times + error */
+ Conn *conn;
+
+ for (i = 0; i < nelem(fields); i++)
+ fields[i] = "";
+ n = tokenize(exitsts, fields, nelem(fields));
+ if (n < 4)
+ return;
+ pid = atoi(fields[0]);
+ if (pid <= 0)
+ return;
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (conn->pid == pid && !conn->dead) { /* it's one we know? */
+ if (conn - dp->conn != dp->winner) {
+ closeopenfd(&conn->dfd);
+ closeopenfd(&conn->cfd);
+ }
+ strncpy(conn->err, fields[4], sizeof conn->err);
+ conn->dead = 1;
+ return;
+ }
+ /* not a proc that we forked */
+}
+
+static int
+outstandingprocs(Dest *dp)
+{
+ Conn *conn;
+
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (!conn->dead)
+ return 1;
+ return 0;
+}
+
+static int
+reap(Dest *dp)
+{
+ char exitsts[2*ERRMAX];
+
+ if (outstandingprocs(dp) && await(exitsts, sizeof exitsts) >= 0) {
+ notedeath(dp, exitsts);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+fillinds(DS *ds, Dest *dp)
+{
+ Conn *conn;
+
+ if (dp->winner < 0)
+ return -1;
+ conn = &dp->conn[dp->winner];
+ if (ds->cfdp)
+ *ds->cfdp = conn->cfd;
+ if (ds->dir)
+ strncpy(ds->dir, conn->dir, NETPATHLEN);
+ return conn->dfd;
+}
+
+static int
+connectwait(Dest *dp, char *besterr)
+{
+ Conn *conn;
+
+ /* wait for a winner or all attempts to time out */
+ while (dp->winner < 0 && reap(dp) >= 0)
+ ;
+
+ /* kill all of our still-live kids & reap them */
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (!conn->dead)
+ postnote(PNPROC, conn->pid, "die");
+ while (reap(dp) >= 0)
+ ;
+
+ /* rummage about and report some error string */
+ for (conn = dp->conn; conn < dp->connend; conn++)
+ if (conn - dp->conn != dp->winner && conn->dead &&
+ conn->err[0]) {
+ strncpy(besterr, conn->err, ERRMAX);
+ break;
+ }
+ return dp->winner;
+}
+
+static int
+parsecs(Dest *dp, char **clonep, char **destp)
+{
+ char *dest, *p;
+
+ dest = strchr(dp->nextaddr, ' ');
+ if(dest == nil)
+ return -1;
+ *dest++ = '\0';
+ p = strchr(dest, '\n');
+ if(p == nil)
+ return -1;
+ *p++ = '\0';
+ *clonep = dp->nextaddr;
+ *destp = dest;
+ dp->nextaddr = p; /* advance to next line */
+ return 0;
+}
+
+static void
+pickuperr(char *besterr, char *err)
+{
+ err[0] = '\0';
+ errstr(err, ERRMAX);
+ if(strstr(err, "does not exist") == 0)
+ strcpy(besterr, err);
+}
+
+/*
+ * try all addresses in parallel and take the first one that answers;
+ * this helps when systems have ip v4 and v6 addresses but are
+ * only reachable from here on one (or some) of them.
+ */
+static int
+dialmulti(DS *ds, Dest *dp)
+{
+ int rv, kid, kidme;
+ char *clone, *dest;
+ char err[ERRMAX], besterr[ERRMAX];
+
+ dp->winner = -1;
+ dp->nkid = 0;
+ while(dp->winner < 0 && *dp->nextaddr != '\0' &&
+ parsecs(dp, &clone, &dest) >= 0) {
+ kidme = dp->nkid++; /* make private copy on stack */
+ kid = rfork(RFPROC|RFMEM); /* spin off a call attempt */
+ if (kid < 0)
+ --dp->nkid;
+ else if (kid == 0) {
+ alarm(Maxconnms);
+ *besterr = '\0';
+ rv = call(clone, dest, ds, dp, &dp->conn[kidme]);
+ if(rv < 0)
+ pickuperr(besterr, err);
+ _exits(besterr); /* avoid atexit callbacks */
+ }
+ }
+ rv = connectwait(dp, besterr);
+ if(rv < 0 && *besterr)
+ werrstr("%s", besterr);
+ else
+ werrstr("%s", err);
+ return rv;
+}
+
+static int
+csdial(DS *ds)
+{
+ int n, fd, rv, addrs, bleft;
+ char c;
+ char *addrp, *clone2, *dest;
+ char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
+ Dest *dp;
+
+ dp = mallocz(sizeof *dp, 1);
+ if(dp == nil)
+ return -1;
+ dp->winner = -1;
+ if (connsalloc(dp, 1) < 0) { /* room for a single conn. */
+ freedest(dp);
+ return -1;
+ }
+
+ /*
+ * open connection server
+ */
+ snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
+ fd = open(buf, ORDWR);
+ if(fd < 0){
+ /* no connection server, don't translate */
+ snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
+ rv = call(clone, ds->rem, ds, dp, &dp->conn[0]);
+ fillinds(ds, dp);
+ freedest(dp);
+ return rv;
+ }
+
+ /*
+ * ask connection server to translate
+ */
+ snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
+ if(write(fd, buf, strlen(buf)) < 0){
+ close(fd);
+ freedest(dp);
+ return -1;
+ }
+
+ /*
+ * read all addresses from the connection server.
+ */
+ seek(fd, 0, 0);
+ addrs = 0;
+ addrp = dp->nextaddr = dp->addrlist;
+ bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */
+ while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) {
+ if (addrp[n-1] != '\n')
+ addrp[n++] = '\n';
+ addrs++;
+ addrp += n;
+ bleft -= n;
+ }
+ /*
+ * if we haven't read all of cs's output, assume the last line might
+ * have been truncated and ignore it. we really don't expect this
+ * to happen.
+ */
+ if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1)
+ addrs--;
+ close(fd);
+
+ *besterr = 0;
+ rv = -1; /* pessimistic default */
+ if (addrs == 0)
+ werrstr("no address to dial");
+ else if (addrs == 1) {
+ /* common case: dial one address without forking */
+ if (parsecs(dp, &clone2, &dest) >= 0 &&
+ (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) {
+ pickuperr(besterr, err);
+ werrstr("%s", besterr);
+ }
+ } else if (connsalloc(dp, addrs) >= 0)
+ rv = dialmulti(ds, dp);
+
+ /* fill in results */
+ if (rv >= 0 && dp->winner >= 0)
+ rv = fillinds(ds, dp);
+
+ freedest(dp);
+ return rv;
+}
+
+static int
+call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn)
+{
+ int fd, cfd, n;
+ char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
+
+ /* because cs is in a different name space, replace the mount point */
+ if(*clone == '/'){
+ p = strchr(clone+1, '/');
+ if(p == nil)
+ p = clone;
+ else
+ p++;
+ } else
+ p = clone;
+ snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
+
+ conn->pid = getpid();
+ conn->cfd = cfd = open(cname, ORDWR);
+ if(cfd < 0)
+ return -1;
+
+ /* get directory name */
+ n = read(cfd, name, sizeof(name)-1);
+ if(n < 0){
+ closeopenfd(&conn->cfd);
+ return -1;
+ }
+ name[n] = 0;
+ for(p = name; *p == ' '; p++)
+ ;
+ snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
+ p = strrchr(cname, '/');
+ *p = 0;
+ if(ds->dir)
+ snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
+ snprint(data, sizeof(data), "%s/%s/data", cname, name);
+
+ /* connect */
+ if(ds->local)
+ snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
+ else
+ snprint(name, sizeof(name), "connect %s", dest);
+ if(write(cfd, name, strlen(name)) < 0){
+ closeopenfd(&conn->cfd);
+ return -1;
+ }
+
+ /* open data connection */
+ conn->dfd = fd = open(data, ORDWR);
+ if(fd < 0){
+ closeopenfd(&conn->cfd);
+ return -1;
+ }
+ if(ds->cfdp == nil)
+ closeopenfd(&conn->cfd);
+
+ qlock(&dp->winlck);
+ if (dp->winner < 0 && conn < dp->connend)
+ dp->winner = conn - dp->conn;
+ qunlock(&dp->winlck);
+ return fd;
+}
+
+/*
+ * parse a dial string
+ */
+static void
+_dial_string_parse(char *str, DS *ds)
+{
+ char *p, *p2;
+
+ strncpy(ds->buf, str, Maxstring);
+ ds->buf[Maxstring-1] = 0;
+
+ p = strchr(ds->buf, '!');
+ if(p == 0) {
+ ds->netdir = 0;
+ ds->proto = "net";
+ ds->rem = ds->buf;
+ } else {
+ if(*ds->buf != '/' && *ds->buf != '#'){
+ ds->netdir = 0;
+ ds->proto = ds->buf;
+ } else {
+ for(p2 = p; *p2 != '/'; p2--)
+ ;
+ *p2++ = 0;
+ ds->netdir = ds->buf;
+ ds->proto = p2;
+ }
+ *p = 0;
+ ds->rem = p + 1;
+ }
+}
diff --git a/sys/src/libc/9sys/dirfstat.c b/sys/src/libc/9sys/dirfstat.c
new file mode 100755
index 000000000..0534a2278
--- /dev/null
+++ b/sys/src/libc/9sys/dirfstat.c
@@ -0,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirfstat(int fd)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = malloc(sizeof(Dir) + BIT16SZ + nd);
+ if(d == nil)
+ return nil;
+ buf = (uchar*)&d[1];
+ n = fstat(fd, buf, BIT16SZ+nd);
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16(buf); /* upper bound on size of Dir + strings */
+ if(nd <= n){
+ convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+BIT16SZ+nd is plenty */
+ free(d);
+ }
+ return nil;
+}
diff --git a/sys/src/libc/9sys/dirfwstat.c b/sys/src/libc/9sys/dirfwstat.c
new file mode 100755
index 000000000..85803ff4c
--- /dev/null
+++ b/sys/src/libc/9sys/dirfwstat.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+dirfwstat(int fd, Dir *d)
+{
+ uchar *buf;
+ int r;
+
+ r = sizeD2M(d);
+ buf = malloc(r);
+ if(buf == nil)
+ return -1;
+ convD2M(d, buf, r);
+ r = fwstat(fd, buf, r);
+ free(buf);
+ return r;
+}
diff --git a/sys/src/libc/9sys/dirmodefmt.c b/sys/src/libc/9sys/dirmodefmt.c
new file mode 100755
index 000000000..82eb5a308
--- /dev/null
+++ b/sys/src/libc/9sys/dirmodefmt.c
@@ -0,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static char *modes[] =
+{
+ "---",
+ "--x",
+ "-w-",
+ "-wx",
+ "r--",
+ "r-x",
+ "rw-",
+ "rwx",
+};
+
+static void
+rwx(long m, char *s)
+{
+ strncpy(s, modes[m], 3);
+}
+
+int
+dirmodefmt(Fmt *f)
+{
+ static char buf[16];
+ ulong m;
+
+ m = va_arg(f->args, ulong);
+
+ if(m & DMDIR)
+ buf[0]='d';
+ else if(m & DMAPPEND)
+ buf[0]='a';
+ else if(m & DMAUTH)
+ buf[0]='A';
+ else
+ buf[0]='-';
+ if(m & DMEXCL)
+ buf[1]='l';
+ else
+ buf[1]='-';
+ rwx((m>>6)&7, buf+2);
+ rwx((m>>3)&7, buf+5);
+ rwx((m>>0)&7, buf+8);
+ buf[11] = 0;
+ return fmtstrcpy(f, buf);
+}
diff --git a/sys/src/libc/9sys/dirread.c b/sys/src/libc/9sys/dirread.c
new file mode 100755
index 000000000..0efd2f954
--- /dev/null
+++ b/sys/src/libc/9sys/dirread.c
@@ -0,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+ char *s;
+ long ss, i, n, nn, m;
+
+ *d = nil;
+ if(ts <= 0)
+ return 0;
+
+ /*
+ * first find number of all stats, check they look like stats, & size all associated strings
+ */
+ ss = 0;
+ n = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16(&buf[i]);
+ if(statcheck(&buf[i], m) < 0)
+ break;
+ ss += m;
+ n++;
+ }
+
+ if(i != ts)
+ return -1;
+
+ *d = malloc(n * sizeof(Dir) + ss);
+ if(*d == nil)
+ return -1;
+
+ /*
+ * then convert all buffers
+ */
+ s = (char*)*d + n * sizeof(Dir);
+ nn = 0;
+ for(i = 0; i < ts; i += m){
+ m = BIT16SZ + GBIT16((uchar*)&buf[i]);
+ if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
+ free(*d);
+ *d = nil;
+ return -1;
+ }
+ nn++;
+ s += m;
+ }
+
+ return nn;
+}
+
+long
+dirread(int fd, Dir **d)
+{
+ uchar *buf;
+ long ts;
+
+ buf = malloc(DIRMAX);
+ if(buf == nil)
+ return -1;
+ ts = read(fd, buf, DIRMAX);
+ if(ts >= 0)
+ ts = dirpackage(buf, ts, d);
+ free(buf);
+ return ts;
+}
+
+long
+dirreadall(int fd, Dir **d)
+{
+ uchar *buf, *nbuf;
+ long n, ts;
+
+ buf = nil;
+ ts = 0;
+ for(;;){
+ nbuf = realloc(buf, ts+DIRMAX);
+ if(nbuf == nil){
+ free(buf);
+ return -1;
+ }
+ buf = nbuf;
+ n = read(fd, buf+ts, DIRMAX);
+ if(n <= 0)
+ break;
+ ts += n;
+ }
+ if(ts >= 0)
+ ts = dirpackage(buf, ts, d);
+ free(buf);
+ if(ts == 0 && n < 0)
+ return -1;
+ return ts;
+}
diff --git a/sys/src/libc/9sys/dirstat.c b/sys/src/libc/9sys/dirstat.c
new file mode 100755
index 000000000..eb5171d2a
--- /dev/null
+++ b/sys/src/libc/9sys/dirstat.c
@@ -0,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirstat(char *name)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = malloc(sizeof(Dir) + BIT16SZ + nd);
+ if(d == nil)
+ return nil;
+ buf = (uchar*)&d[1];
+ n = stat(name, buf, BIT16SZ+nd);
+ if(n < BIT16SZ){
+ free(d);
+ return nil;
+ }
+ nd = GBIT16((uchar*)buf); /* upper bound on size of Dir + strings */
+ if(nd <= n){
+ convM2D(buf, n, d, (char*)&d[1]);
+ return d;
+ }
+ /* else sizeof(Dir)+BIT16SZ+nd is plenty */
+ free(d);
+ }
+ return nil;
+}
diff --git a/sys/src/libc/9sys/dirwstat.c b/sys/src/libc/9sys/dirwstat.c
new file mode 100755
index 000000000..cb62e3fc4
--- /dev/null
+++ b/sys/src/libc/9sys/dirwstat.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+dirwstat(char *name, Dir *d)
+{
+ uchar *buf;
+ int r;
+
+ r = sizeD2M(d);
+ buf = malloc(r);
+ if(buf == nil)
+ return -1;
+ convD2M(d, buf, r);
+ r = wstat(name, buf, r);
+ free(buf);
+ return r;
+}
diff --git a/sys/src/libc/9sys/fcallfmt.c b/sys/src/libc/9sys/fcallfmt.c
new file mode 100755
index 000000000..c5a8af614
--- /dev/null
+++ b/sys/src/libc/9sys/fcallfmt.c
@@ -0,0 +1,234 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static uint dumpsome(char*, char*, char*, long);
+static void fdirconv(char*, char*, Dir*);
+static char *qidtype(char*, uchar);
+
+#define QIDFMT "(%.16llux %lud %s)"
+
+int
+fcallfmt(Fmt *fmt)
+{
+ Fcall *f;
+ int fid, type, tag, i;
+ char buf[512], tmp[200];
+ char *p, *e;
+ Dir *d;
+ Qid *q;
+
+ e = buf+sizeof(buf);
+ f = va_arg(fmt->args, Fcall*);
+ type = f->type;
+ fid = f->fid;
+ tag = f->tag;
+ switch(type){
+ case Tversion: /* 100 */
+ seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Rversion:
+ seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Tauth: /* 102 */
+ seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag,
+ f->afid, f->uname, f->aname);
+ break;
+ case Rauth:
+ seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag,
+ f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
+ break;
+ case Tattach: /* 104 */
+ seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+ fid, f->afid, f->uname, f->aname);
+ break;
+ case Rattach:
+ seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
+ break;
+ case Rerror: /* 107; 106 (Terror) illegal */
+ seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename);
+ break;
+ case Tflush: /* 108 */
+ seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+ break;
+ case Rflush:
+ seprint(buf, e, "Rflush tag %ud", tag);
+ break;
+ case Twalk: /* 110 */
+ p = seprint(buf, e, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname);
+ if(f->nwname <= MAXWELEM)
+ for(i=0; i<f->nwname; i++)
+ p = seprint(p, e, "%d:%s ", i, f->wname[i]);
+ break;
+ case Rwalk:
+ p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+ if(f->nwqid <= MAXWELEM)
+ for(i=0; i<f->nwqid; i++){
+ q = &f->wqid[i];
+ p = seprint(p, e, "%d:" QIDFMT " ", i,
+ q->path, q->vers, qidtype(tmp, q->type));
+ }
+ break;
+ case Topen: /* 112 */
+ seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
+ break;
+ case Ropen:
+ seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tcreate: /* 114 */
+ seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
+ break;
+ case Rcreate:
+ seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tread: /* 116 */
+ seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud",
+ tag, fid, f->offset, f->count);
+ break;
+ case Rread:
+ p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Twrite: /* 118 */
+ p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ",
+ tag, fid, f->offset, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Rwrite:
+ seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count);
+ break;
+ case Tclunk: /* 120 */
+ seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid);
+ break;
+ case Rclunk:
+ seprint(buf, e, "Rclunk tag %ud", tag);
+ break;
+ case Tremove: /* 122 */
+ seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid);
+ break;
+ case Rremove:
+ seprint(buf, e, "Rremove tag %ud", tag);
+ break;
+ case Tstat: /* 124 */
+ seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid);
+ break;
+ case Rstat:
+ p = seprint(buf, e, "Rstat tag %ud ", tag);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Twstat: /* 126 */
+ p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Rwstat:
+ seprint(buf, e, "Rwstat tag %ud", tag);
+ break;
+ default:
+ seprint(buf, e, "unknown type %d", type);
+ }
+ return fmtstrcpy(fmt, buf);
+}
+
+static char*
+qidtype(char *s, uchar t)
+{
+ char *p;
+
+ p = s;
+ if(t & QTDIR)
+ *p++ = 'd';
+ if(t & QTAPPEND)
+ *p++ = 'a';
+ if(t & QTEXCL)
+ *p++ = 'l';
+ if(t & QTAUTH)
+ *p++ = 'A';
+ *p = '\0';
+ return s;
+}
+
+int
+dirfmt(Fmt *fmt)
+{
+ char buf[160];
+
+ fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*));
+ return fmtstrcpy(fmt, buf);
+}
+
+static void
+fdirconv(char *buf, char *e, Dir *d)
+{
+ char tmp[16];
+
+ seprint(buf, e, "'%s' '%s' '%s' '%s' "
+ "q " QIDFMT " m %#luo "
+ "at %ld mt %ld l %lld "
+ "t %d d %d",
+ d->name, d->uid, d->gid, d->muid,
+ d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
+ d->atime, d->mtime, d->length,
+ d->type, d->dev);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static uint
+dumpsome(char *ans, char *e, char *buf, long count)
+{
+ int i, printable;
+ char *p;
+
+ if(buf == nil){
+ seprint(ans, e, "<no data>");
+ return strlen(ans);
+ }
+ printable = 1;
+ if(count > DUMPL)
+ count = DUMPL;
+ for(i=0; i<count && printable; i++)
+ if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
+ printable = 0;
+ p = ans;
+ *p++ = '\'';
+ if(printable){
+ if(count > e-p-2)
+ count = e-p-2;
+ memmove(p, buf, count);
+ p += count;
+ }else{
+ if(2*count > e-p-2)
+ count = (e-p-2)/2;
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *p++ = ' ';
+ sprint(p, "%2.2ux", (uchar)buf[i]);
+ p += 2;
+ }
+ }
+ *p++ = '\'';
+ *p = 0;
+ return p - ans;
+}
diff --git a/sys/src/libc/9sys/fork.c b/sys/src/libc/9sys/fork.c
new file mode 100755
index 000000000..46296ed5d
--- /dev/null
+++ b/sys/src/libc/9sys/fork.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+int
+fork(void)
+{
+ return rfork(RFPROC|RFFDG|RFREND);
+}
diff --git a/sys/src/libc/9sys/getenv.c b/sys/src/libc/9sys/getenv.c
new file mode 100755
index 000000000..146b35b62
--- /dev/null
+++ b/sys/src/libc/9sys/getenv.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+getenv(char *name)
+{
+ int r, f;
+ long s;
+ char *ans;
+ char *p, *ep, ename[100];
+
+ if(strchr(name, '/') != nil)
+ return nil;
+ snprint(ename, sizeof ename, "/env/%s", name);
+ if(strcmp(ename+5, name) != 0)
+ return nil;
+ f = open(ename, OREAD);
+ if(f < 0)
+ return 0;
+ s = seek(f, 0, 2);
+ ans = malloc(s+1);
+ if(ans) {
+ setmalloctag(ans, getcallerpc(&name));
+ seek(f, 0, 0);
+ r = read(f, ans, s);
+ if(r >= 0) {
+ ep = ans + s - 1;
+ for(p = ans; p < ep; p++)
+ if(*p == '\0')
+ *p = ' ';
+ ans[s] = '\0';
+ }
+ }
+ close(f);
+ return ans;
+}
diff --git a/sys/src/libc/9sys/getnetconninfo.c b/sys/src/libc/9sys/getnetconninfo.c
new file mode 100755
index 000000000..8dbb95f89
--- /dev/null
+++ b/sys/src/libc/9sys/getnetconninfo.c
@@ -0,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+
+static char *unknown = "???";
+
+static void
+getendpoint(char *dir, char *file, char **sysp, char **servp)
+{
+ int fd, n;
+ char buf[128];
+ char *sys, *serv;
+
+ sys = serv = 0;
+
+ snprint(buf, sizeof buf, "%s/%s", dir, file);
+ fd = open(buf, OREAD);
+ if(fd >= 0){
+ n = read(fd, buf, sizeof(buf)-1);
+ if(n>0){
+ buf[n-1] = 0;
+ serv = strchr(buf, '!');
+ if(serv){
+ *serv++ = 0;
+ serv = strdup(serv);
+ }
+ sys = strdup(buf);
+ }
+ close(fd);
+ }
+ if(serv == 0)
+ serv = unknown;
+ if(sys == 0)
+ sys = unknown;
+ *servp = serv;
+ *sysp = sys;
+}
+
+NetConnInfo*
+getnetconninfo(char *dir, int fd)
+{
+ NetConnInfo *nci;
+ char *cp;
+ Dir *d;
+ char spec[10];
+ char path[128];
+ char netname[128], *p;
+
+ /* get a directory address via fd */
+ if(dir == nil || *dir == 0){
+ if(fd2path(fd, path, sizeof(path)) < 0)
+ return nil;
+ cp = strrchr(path, '/');
+ if(cp == nil)
+ return nil;
+ *cp = 0;
+ dir = path;
+ }
+
+ nci = mallocz(sizeof *nci, 1);
+ if(nci == nil)
+ return nil;
+
+ /* copy connection directory */
+ nci->dir = strdup(dir);
+ if(nci->dir == nil)
+ goto err;
+
+ /* get netroot */
+ nci->root = strdup(dir);
+ if(nci->root == nil)
+ goto err;
+ cp = strchr(nci->root+1, '/');
+ if(cp == nil)
+ goto err;
+ *cp = 0;
+
+ /* figure out bind spec */
+ d = dirstat(nci->dir);
+ if(d != nil){
+ sprint(spec, "#%C%d", d->type, d->dev);
+ nci->spec = strdup(spec);
+ }
+ if(nci->spec == nil)
+ nci->spec = unknown;
+ free(d);
+
+ /* get the two end points */
+ getendpoint(nci->dir, "local", &nci->lsys, &nci->lserv);
+ if(nci->lsys == nil || nci->lserv == nil)
+ goto err;
+ getendpoint(nci->dir, "remote", &nci->rsys, &nci->rserv);
+ if(nci->rsys == nil || nci->rserv == nil)
+ goto err;
+
+ strecpy(netname, netname+sizeof netname, nci->dir);
+ if((p = strrchr(netname, '/')) != nil)
+ *p = 0;
+ if(strncmp(netname, "/net/", 5) == 0)
+ memmove(netname, netname+5, strlen(netname+5)+1);
+ nci->laddr = smprint("%s!%s!%s", netname, nci->lsys, nci->lserv);
+ nci->raddr = smprint("%s!%s!%s", netname, nci->rsys, nci->rserv);
+ if(nci->laddr == nil || nci->raddr == nil)
+ goto err;
+ return nci;
+err:
+ freenetconninfo(nci);
+ return nil;
+}
+
+static void
+xfree(char *x)
+{
+ if(x == nil || x == unknown)
+ return;
+ free(x);
+}
+
+void
+freenetconninfo(NetConnInfo *nci)
+{
+ if(nci == nil)
+ return;
+ xfree(nci->root);
+ xfree(nci->dir);
+ xfree(nci->spec);
+ xfree(nci->lsys);
+ xfree(nci->lserv);
+ xfree(nci->rsys);
+ xfree(nci->rserv);
+ xfree(nci->laddr);
+ xfree(nci->raddr);
+ free(nci);
+}
diff --git a/sys/src/libc/9sys/getpid.c b/sys/src/libc/9sys/getpid.c
new file mode 100755
index 000000000..9a9c86c13
--- /dev/null
+++ b/sys/src/libc/9sys/getpid.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+int
+getpid(void)
+{
+ char b[20];
+ int f;
+
+ memset(b, 0, sizeof(b));
+ f = open("#c/pid", 0);
+ if(f >= 0) {
+ read(f, b, sizeof(b));
+ close(f);
+ }
+ return atol(b);
+}
diff --git a/sys/src/libc/9sys/getppid.c b/sys/src/libc/9sys/getppid.c
new file mode 100755
index 000000000..b90b57ee5
--- /dev/null
+++ b/sys/src/libc/9sys/getppid.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+int
+getppid(void)
+{
+ char b[20];
+ int f;
+
+ memset(b, 0, sizeof(b));
+ f = open("/dev/ppid", 0);
+ if(f >= 0) {
+ read(f, b, sizeof(b));
+ close(f);
+ }
+ return atol(b);
+}
diff --git a/sys/src/libc/9sys/getwd.c b/sys/src/libc/9sys/getwd.c
new file mode 100755
index 000000000..ed73cb775
--- /dev/null
+++ b/sys/src/libc/9sys/getwd.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+
+static char *nsgetwd(char*, int);
+
+char*
+getwd(char *buf, int nbuf)
+{
+ int n, fd;
+
+ fd = open(".", OREAD);
+ if(fd < 0)
+ return nil;
+ n = fd2path(fd, buf, nbuf);
+ close(fd);
+ if(n < 0)
+ return nil;
+ return buf;
+}
diff --git a/sys/src/libc/9sys/iounit.c b/sys/src/libc/9sys/iounit.c
new file mode 100755
index 000000000..194b17173
--- /dev/null
+++ b/sys/src/libc/9sys/iounit.c
@@ -0,0 +1,27 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Format:
+ 3 r M 4 (0000000000457def 11 00) 8192 512 /rc/lib/rcmain
+ */
+
+int
+iounit(int fd)
+{
+ int i, cfd;
+ char buf[128], *args[10];
+
+ snprint(buf, sizeof buf, "#d/%dctl", fd);
+ cfd = open(buf, OREAD);
+ if(cfd < 0)
+ return 0;
+ i = read(cfd, buf, sizeof buf-1);
+ close(cfd);
+ if(i <= 0)
+ return 0;
+ buf[i] = '\0';
+ if(tokenize(buf, args, nelem(args)) != nelem(args))
+ return 0;
+ return atoi(args[7]);
+}
diff --git a/sys/src/libc/9sys/mkfile b/sys/src/libc/9sys/mkfile
new file mode 100755
index 000000000..3b7d95e8e
--- /dev/null
+++ b/sys/src/libc/9sys/mkfile
@@ -0,0 +1,64 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+OFILES=\
+ abort.$O\
+ access.$O\
+ announce.$O\
+ convD2M.$O\
+ convM2D.$O\
+ convM2S.$O\
+ convS2M.$O\
+ cputime.$O\
+ ctime.$O\
+ dial.$O\
+ dirfstat.$O\
+ dirfwstat.$O\
+ dirmodefmt.$O\
+ dirread.$O\
+ dirstat.$O\
+ dirwstat.$O\
+ fcallfmt.$O\
+ fork.$O\
+ getnetconninfo.$O\
+ getenv.$O\
+ getpid.$O\
+ getppid.$O\
+ getwd.$O\
+ iounit.$O\
+ nsec.$O\
+ nulldir.$O\
+ postnote.$O\
+ privalloc.$O\
+ pushssl.$O\
+ pushtls.$O\
+ putenv.$O\
+ qlock.$O\
+ read.$O\
+ read9pmsg.$O\
+ readv.$O\
+ rerrstr.$O\
+ sbrk.$O\
+ setnetmtpt.$O\
+ sysfatal.$O\
+ syslog.$O\
+ sysname.$O\
+ time.$O\
+ times.$O\
+ tm2sec.$O\
+ truerand.$O\
+ wait.$O\
+ waitpid.$O\
+ werrstr.$O\
+ write.$O\
+ writev.$O\
+
+HFILES=/sys/include/libc.h
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mksyslib
+
diff --git a/sys/src/libc/9sys/nsec.c b/sys/src/libc/9sys/nsec.c
new file mode 100755
index 000000000..f0e981d04
--- /dev/null
+++ b/sys/src/libc/9sys/nsec.c
@@ -0,0 +1,75 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+static uvlong order = 0x0001020304050607ULL;
+
+static void
+be2vlong(vlong *to, uchar *f)
+{
+ uchar *t, *o;
+ int i;
+
+ t = (uchar*)to;
+ o = (uchar*)&order;
+ for(i = 0; i < sizeof order; i++)
+ t[o[i]] = f[i];
+}
+
+static int fd = -1;
+static struct {
+ int pid;
+ int fd;
+} fds[64];
+
+vlong
+nsec(void)
+{
+ uchar b[8];
+ vlong t;
+ int pid, i, f, tries;
+
+ /*
+ * Threaded programs may have multiple procs
+ * with different fd tables, so we may need to open
+ * /dev/bintime on a per-pid basis
+ */
+
+ /* First, look if we've opened it for this particular pid */
+ pid = _tos->pid;
+ do{
+ f = -1;
+ for(i = 0; i < nelem(fds); i++)
+ if(fds[i].pid == pid){
+ f = fds[i].fd;
+ break;
+ }
+ tries = 0;
+ if(f < 0){
+ /* If it's not open for this pid, try the global pid */
+ if(fd >= 0)
+ f = fd;
+ else{
+ /* must open */
+ if((f = open("/dev/bintime", OREAD|OCEXEC)) < 0)
+ return 0;
+ fd = f;
+ for(i = 0; i < nelem(fds); i++)
+ if(fds[i].pid == pid || fds[i].pid == 0){
+ fds[i].pid = pid;
+ fds[i].fd = f;
+ break;
+ }
+ }
+ }
+ if(pread(f, b, sizeof b, 0) == sizeof b){
+ be2vlong(&t, b);
+ return t;
+ }
+ close(f);
+ if(i < nelem(fds))
+ fds[i].fd = -1;
+ }while(tries++ == 0); /* retry once */
+ USED(tries);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/nulldir.c b/sys/src/libc/9sys/nulldir.c
new file mode 100755
index 000000000..612725d81
--- /dev/null
+++ b/sys/src/libc/9sys/nulldir.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+void
+nulldir(Dir *d)
+{
+ memset(d, ~0, sizeof(Dir));
+ d->name = d->uid = d->gid = d->muid = "";
+}
diff --git a/sys/src/libc/9sys/postnote.c b/sys/src/libc/9sys/postnote.c
new file mode 100755
index 000000000..46564e9ea
--- /dev/null
+++ b/sys/src/libc/9sys/postnote.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+
+int
+postnote(int group, int pid, char *note)
+{
+ char file[128];
+ int f, r;
+
+ switch(group) {
+ case PNPROC:
+ sprint(file, "/proc/%d/note", pid);
+ break;
+ case PNGROUP:
+ sprint(file, "/proc/%d/notepg", pid);
+ break;
+ default:
+ return -1;
+ }
+
+ f = open(file, OWRITE);
+ if(f < 0)
+ return -1;
+
+ r = strlen(note);
+ if(write(f, note, r) != r) {
+ close(f);
+ return -1;
+ }
+ close(f);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/privalloc.c b/sys/src/libc/9sys/privalloc.c
new file mode 100755
index 000000000..907485e2f
--- /dev/null
+++ b/sys/src/libc/9sys/privalloc.c
@@ -0,0 +1,45 @@
+#include <u.h>
+#include <libc.h>
+
+static Lock privlock;
+static int privinit;
+static void **privs;
+
+extern void **_privates;
+extern int _nprivates;
+
+void **
+privalloc(void)
+{
+ void **p;
+ int i;
+
+ lock(&privlock);
+ if(!privinit){
+ privinit = 1;
+ if(_nprivates){
+ _privates[0] = 0;
+ for(i = 1; i < _nprivates; i++)
+ _privates[i] = &_privates[i - 1];
+ privs = &_privates[i - 1];
+ }
+ }
+ p = privs;
+ if(p != nil){
+ privs = *p;
+ *p = nil;
+ }
+ unlock(&privlock);
+ return p;
+}
+
+void
+privfree(void **p)
+{
+ lock(&privlock);
+ if(p != nil && privinit){
+ *p = privs;
+ privs = p;
+ }
+ unlock(&privlock);
+}
diff --git a/sys/src/libc/9sys/pushssl.c b/sys/src/libc/9sys/pushssl.c
new file mode 100755
index 000000000..8817dd1c3
--- /dev/null
+++ b/sys/src/libc/9sys/pushssl.c
@@ -0,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Since the SSL device uses decimal file descriptors to name channels,
+ * it is impossible for a user-level file server to stand in for the kernel device.
+ * Thus we hard-code #D rather than use /net/ssl.
+ */
+
+int
+pushssl(int fd, char *alg, char *secin, char *secout, int *cfd)
+{
+ char buf[8];
+ char dname[64];
+ int n, data, ctl;
+
+ ctl = open("#D/ssl/clone", ORDWR);
+ if(ctl < 0)
+ return -1;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0)
+ goto error;
+ buf[n] = 0;
+ sprint(dname, "#D/ssl/%s/data", buf);
+ data = open(dname, ORDWR);
+ if(data < 0)
+ goto error;
+ if(fprint(ctl, "fd %d", fd) < 0 ||
+ fprint(ctl, "secretin %s", secin) < 0 ||
+ fprint(ctl, "secretout %s", secout) < 0 ||
+ fprint(ctl, "alg %s", alg) < 0){
+ close(data);
+ goto error;
+ }
+ close(fd);
+ if(cfd != 0)
+ *cfd = ctl;
+ else
+ close(ctl);
+ return data;
+error:
+ close(ctl);
+ return -1;
+}
diff --git a/sys/src/libc/9sys/pushtls.c b/sys/src/libc/9sys/pushtls.c
new file mode 100755
index 000000000..038aad748
--- /dev/null
+++ b/sys/src/libc/9sys/pushtls.c
@@ -0,0 +1,99 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+enum {
+ TLSFinishedLen = 12,
+ HFinished = 20,
+};
+
+static int
+finished(int hand, int isclient)
+{
+ int i, n;
+ uchar buf[500], buf2[500];
+
+ buf[0] = HFinished;
+ buf[1] = TLSFinishedLen>>16;
+ buf[2] = TLSFinishedLen>>8;
+ buf[3] = TLSFinishedLen;
+ n = TLSFinishedLen+4;
+
+ for(i=0; i<2; i++){
+ if(i==0)
+ memmove(buf+4, "client finished", TLSFinishedLen);
+ else
+ memmove(buf+4, "server finished", TLSFinishedLen);
+ if(isclient == 1-i){
+ if(write(hand, buf, n) != n)
+ return -1;
+ }else{
+ if(readn(hand, buf2, n) != n || memcmp(buf,buf2,n) != 0)
+ return -1;
+ }
+ }
+ return 1;
+}
+
+
+// given a plain fd and secrets established beforehand, return encrypted connection
+int
+pushtls(int fd, char *hashalg, char *encalg, int isclient, char *secret, char *dir)
+{
+ char buf[8];
+ char dname[64];
+ int n, data, ctl, hand;
+
+ // open a new filter; get ctl fd
+ data = hand = -1;
+ // /net/tls uses decimal file descriptors to name channels, hence a
+ // user-level file server can't stand in for #a; may as well hard-code it.
+ ctl = open("#a/tls/clone", ORDWR);
+ if(ctl < 0)
+ goto error;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0)
+ goto error;
+ buf[n] = 0;
+ if(dir)
+ sprint(dir, "#a/tls/%s", buf);
+
+ // get application fd
+ sprint(dname, "#a/tls/%s/data", buf);
+ data = open(dname, ORDWR);
+ if(data < 0)
+ goto error;
+
+ // get handshake fd
+ sprint(dname, "#a/tls/%s/hand", buf);
+ hand = open(dname, ORDWR);
+ if(hand < 0)
+ goto error;
+
+ // speak a minimal handshake
+ if(fprint(ctl, "fd %d 0x301", fd) < 0 ||
+ fprint(ctl, "version 0x301") < 0 ||
+ fprint(ctl, "secret %s %s %d %s", hashalg, encalg, isclient, secret) < 0 ||
+ fprint(ctl, "changecipher") < 0 ||
+ finished(hand, isclient) < 0 ||
+ fprint(ctl, "opened") < 0){
+ close(hand);
+ hand = -1;
+ goto error;
+ }
+ close(ctl);
+ close(hand);
+ close(fd);
+ return data;
+
+error:
+ if(data>=0)
+ close(data);
+ if(ctl>=0)
+ close(ctl);
+ if(hand>=0)
+ close(hand);
+ return -1;
+}
diff --git a/sys/src/libc/9sys/putenv.c b/sys/src/libc/9sys/putenv.c
new file mode 100755
index 000000000..de2389482
--- /dev/null
+++ b/sys/src/libc/9sys/putenv.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+int
+putenv(char *name, char *val)
+{
+ int f;
+ char ename[100];
+ long s;
+
+ if(strchr(name, '/') != nil)
+ return -1;
+ snprint(ename, sizeof ename, "/env/%s", name);
+ if(strcmp(ename+5, name) != 0)
+ return -1;
+ f = create(ename, OWRITE, 0664);
+ if(f < 0)
+ return -1;
+ s = strlen(val);
+ if(write(f, val, s) != s){
+ close(f);
+ return -1;
+ }
+ close(f);
+ return 0;
+}
diff --git a/sys/src/libc/9sys/qlock.c b/sys/src/libc/9sys/qlock.c
new file mode 100755
index 000000000..fb2d80238
--- /dev/null
+++ b/sys/src/libc/9sys/qlock.c
@@ -0,0 +1,363 @@
+#include <u.h>
+#include <libc.h>
+
+static struct {
+ QLp *p;
+ QLp x[1024];
+} ql = {
+ ql.x
+};
+
+enum
+{
+ Queuing,
+ QueuingR,
+ QueuingW,
+ Sleeping,
+};
+
+static void* (*_rendezvousp)(void*, void*) = rendezvous;
+
+/* this gets called by the thread library ONLY to get us to use its rendezvous */
+void
+_qlockinit(void* (*r)(void*, void*))
+{
+ _rendezvousp = r;
+}
+
+/* find a free shared memory location to queue ourselves in */
+static QLp*
+getqlp(void)
+{
+ QLp *p, *op;
+
+ op = ql.p;
+ for(p = op+1; ; p++){
+ if(p == &ql.x[nelem(ql.x)])
+ p = ql.x;
+ if(p == op)
+ abort();
+ if(_tas(&(p->inuse)) == 0){
+ ql.p = p;
+ p->next = nil;
+ break;
+ }
+ }
+ return p;
+}
+
+void
+qlock(QLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+
+ /* chain into waiting list */
+ mp = getqlp();
+ p = q->tail;
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->state = Queuing;
+ unlock(&q->lock);
+
+ /* wait */
+ while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+ ;
+ mp->inuse = 0;
+}
+
+void
+qunlock(QLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if (q->locked == 0)
+ fprint(2, "qunlock called with qlock not held, from %#p\n",
+ getcallerpc(&q));
+ p = q->head;
+ if(p != nil){
+ /* wakeup head waiting process */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)(p, (void*)0x12345) == (void*)~0)
+ ;
+ return;
+ }
+ q->locked = 0;
+ unlock(&q->lock);
+}
+
+int
+canqlock(QLock *q)
+{
+ if(!canlock(&q->lock))
+ return 0;
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+rlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return;
+ }
+
+ mp = getqlp();
+ p = q->tail;
+ if(p == 0)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingR;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->writer == 0 && q->head == nil) {
+ /* no writer; go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->readers <= 0)
+ abort();
+ p = q->head;
+ if(--(q->readers) > 0 || p == nil){
+ unlock(&q->lock);
+ return;
+ }
+
+ /* start waiting writer */
+ if(p->state != QueuingW)
+ abort();
+ q->head = p->next;
+ if(q->head == 0)
+ q->tail = 0;
+ q->writer = 1;
+ unlock(&q->lock);
+
+ /* wakeup waiter */
+ while((*_rendezvousp)(p, 0) == (void*)~0)
+ ;
+}
+
+void
+wlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->readers == 0 && q->writer == 0){
+ /* noone waiting, go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+ /* wait */
+ p = q->tail;
+ mp = getqlp();
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingW;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)(mp, (void*)1) == (void*)~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->readers == 0 && q->writer == 0) {
+ /* no one waiting; go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->writer == 0)
+ abort();
+ p = q->head;
+ if(p == nil){
+ q->writer = 0;
+ unlock(&q->lock);
+ return;
+ }
+ if(p->state == QueuingW){
+ /* start waiting writer */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)(p, 0) == (void*)~0)
+ ;
+ return;
+ }
+
+ if(p->state != QueuingR)
+ abort();
+
+ /* wake waiting readers */
+ while(q->head != nil && q->head->state == QueuingR){
+ p = q->head;
+ q->head = p->next;
+ q->readers++;
+ while((*_rendezvousp)(p, 0) == (void*)~0)
+ ;
+ }
+ if(q->head == nil)
+ q->tail = nil;
+ q->writer = 0;
+ unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+ QLp *t, *me;
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ /* we should hold the qlock */
+ if(!r->l->locked)
+ abort();
+
+ /* add ourselves to the wait list */
+ me = getqlp();
+ me->state = Sleeping;
+ if(r->head == nil)
+ r->head = me;
+ else
+ r->tail->next = me;
+ me->next = nil;
+ r->tail = me;
+
+ /* pass the qlock to the next guy */
+ t = r->l->head;
+ if(t){
+ r->l->head = t->next;
+ if(r->l->head == nil)
+ r->l->tail = nil;
+ unlock(&r->l->lock);
+ while((*_rendezvousp)(t, (void*)0x12345) == (void*)~0)
+ ;
+ }else{
+ r->l->locked = 0;
+ unlock(&r->l->lock);
+ }
+
+ /* wait for a wakeup */
+ while((*_rendezvousp)(me, (void*)1) == (void*)~0)
+ ;
+ me->inuse = 0;
+}
+
+int
+rwakeup(Rendez *r)
+{
+ QLp *t;
+
+ /*
+ * take off wait and put on front of queue
+ * put on front so guys that have been waiting will not get starved
+ */
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ if(!r->l->locked)
+ abort();
+
+ t = r->head;
+ if(t == nil){
+ unlock(&r->l->lock);
+ return 0;
+ }
+
+ r->head = t->next;
+ if(r->head == nil)
+ r->tail = nil;
+
+ t->next = r->l->head;
+ r->l->head = t;
+ if(r->l->tail == nil)
+ r->l->tail = t;
+
+ t->state = Queuing;
+ unlock(&r->l->lock);
+ return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+ int i;
+
+ for(i=0; rwakeup(r); i++)
+ ;
+ return i;
+}
+
diff --git a/sys/src/libc/9sys/read.c b/sys/src/libc/9sys/read.c
new file mode 100755
index 000000000..2119ab2ac
--- /dev/null
+++ b/sys/src/libc/9sys/read.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+long
+read(int fd, void *buf, long n)
+{
+ return pread(fd, buf, n, -1LL);
+}
diff --git a/sys/src/libc/9sys/read9pmsg.c b/sys/src/libc/9sys/read9pmsg.c
new file mode 100755
index 000000000..9e90ec5d6
--- /dev/null
+++ b/sys/src/libc/9sys/read9pmsg.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+read9pmsg(int fd, void *abuf, uint n)
+{
+ int m, len;
+ uchar *buf;
+
+ buf = abuf;
+
+ /* read count */
+ m = readn(fd, buf, BIT32SZ);
+ if(m != BIT32SZ){
+ if(m < 0)
+ return -1;
+ return 0;
+ }
+
+ len = GBIT32(buf);
+ if(len <= BIT32SZ || len > n){
+ werrstr("bad length in 9P2000 message header");
+ return -1;
+ }
+ len -= BIT32SZ;
+ m = readn(fd, buf+BIT32SZ, len);
+ if(m < len)
+ return 0;
+ return BIT32SZ+m;
+}
diff --git a/sys/src/libc/9sys/readv.c b/sys/src/libc/9sys/readv.c
new file mode 100755
index 000000000..a0dc10aaf
--- /dev/null
+++ b/sys/src/libc/9sys/readv.c
@@ -0,0 +1,49 @@
+#include <u.h>
+#include <libc.h>
+
+static
+long
+ioreadv(int fd, IOchunk *io, int nio, vlong offset)
+{
+ int i;
+ long m, n, tot;
+ char *buf, *p;
+
+ tot = 0;
+ for(i=0; i<nio; i++)
+ tot += io[i].len;
+ buf = malloc(tot);
+ if(buf == nil)
+ return -1;
+
+ tot = pread(fd, buf, tot, offset);
+
+ p = buf;
+ n = tot;
+ for(i=0; i<nio; i++){
+ if(n <= 0)
+ break;
+ m = io->len;
+ if(m > n)
+ m = n;
+ memmove(io->addr, p, m);
+ n -= m;
+ p += m;
+ io++;
+ }
+
+ free(buf);
+ return tot;
+}
+
+long
+readv(int fd, IOchunk *io, int nio)
+{
+ return ioreadv(fd, io, nio, -1LL);
+}
+
+long
+preadv(int fd, IOchunk *io, int nio, vlong off)
+{
+ return ioreadv(fd, io, nio, off);
+}
diff --git a/sys/src/libc/9sys/rerrstr.c b/sys/src/libc/9sys/rerrstr.c
new file mode 100755
index 000000000..c64cd4940
--- /dev/null
+++ b/sys/src/libc/9sys/rerrstr.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+void
+rerrstr(char *buf, uint nbuf)
+{
+ char tmp[ERRMAX];
+
+ tmp[0] = 0;
+ errstr(tmp, sizeof tmp);
+ utfecpy(buf, buf+nbuf, tmp);
+ errstr(tmp, sizeof tmp);
+}
diff --git a/sys/src/libc/9sys/sbrk.c b/sys/src/libc/9sys/sbrk.c
new file mode 100755
index 000000000..88b593e11
--- /dev/null
+++ b/sys/src/libc/9sys/sbrk.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+
+extern char end[];
+static char *bloc = { end };
+extern int brk_(void*);
+
+enum
+{
+ Round = 7
+};
+
+int
+brk(void *p)
+{
+ uintptr bl;
+
+ bl = ((uintptr)p + Round) & ~Round;
+ if(brk_((void*)bl) < 0)
+ return -1;
+ bloc = (char*)bl;
+ return 0;
+}
+
+void*
+sbrk(ulong n)
+{
+ uintptr bl;
+
+ bl = ((uintptr)bloc + Round) & ~Round;
+ if(brk_((void*)(bl+n)) < 0)
+ return (void*)-1;
+ bloc = (char*)bl + n;
+ return (void*)bl;
+}
diff --git a/sys/src/libc/9sys/setnetmtpt.c b/sys/src/libc/9sys/setnetmtpt.c
new file mode 100755
index 000000000..3d984f08f
--- /dev/null
+++ b/sys/src/libc/9sys/setnetmtpt.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+
+void
+setnetmtpt(char *net, int n, char *x)
+{
+ if(x == nil)
+ x = "/net";
+
+ if(*x == '/'){
+ strncpy(net, x, n);
+ net[n-1] = 0;
+ } else {
+ snprint(net, n, "/net%s", x);
+ }
+}
diff --git a/sys/src/libc/9sys/sysfatal.c b/sys/src/libc/9sys/sysfatal.c
new file mode 100755
index 000000000..e2cef6c48
--- /dev/null
+++ b/sys/src/libc/9sys/sysfatal.c
@@ -0,0 +1,28 @@
+#include <u.h>
+#include <libc.h>
+
+
+static void
+_sysfatalimpl(char *fmt, va_list arg)
+{
+ char buf[1024];
+
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ if(argv0)
+ fprint(2, "%s: %s\n", argv0, buf);
+ else
+ fprint(2, "%s\n", buf);
+ exits(buf);
+}
+
+void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl;
+
+void
+sysfatal(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ (*_sysfatal)(fmt, arg);
+ va_end(arg);
+}
diff --git a/sys/src/libc/9sys/syslog.c b/sys/src/libc/9sys/syslog.c
new file mode 100755
index 000000000..828ad04a7
--- /dev/null
+++ b/sys/src/libc/9sys/syslog.c
@@ -0,0 +1,117 @@
+#include <u.h>
+#include <libc.h>
+
+static struct
+{
+ int fd;
+ int consfd;
+ char *name;
+ Dir *d;
+ Dir *consd;
+ Lock;
+} sl =
+{
+ -1, -1,
+};
+
+static void
+_syslogopen(void)
+{
+ char buf[1024];
+
+ if(sl.fd >= 0)
+ close(sl.fd);
+ snprint(buf, sizeof(buf), "/sys/log/%s", sl.name);
+ sl.fd = open(buf, OWRITE|OCEXEC);
+}
+
+/*
+ * Print
+ * sysname: time: mesg
+ * on /sys/log/logname.
+ * If cons or log file can't be opened, print on the system console, too.
+ */
+void
+syslog(int cons, char *logname, char *fmt, ...)
+{
+ char buf[1024];
+ char *ctim, *p;
+ va_list arg;
+ int n;
+ Dir *d;
+ char err[ERRMAX];
+
+ err[0] = '\0';
+ errstr(err, sizeof err);
+ lock(&sl);
+
+ /*
+ * paranoia makes us stat to make sure a fork+close
+ * hasn't broken our fd's
+ */
+ d = dirfstat(sl.fd);
+ if(sl.fd < 0
+ || sl.name == nil
+ || strcmp(sl.name, logname)!=0
+ || sl.d == nil
+ || d == nil
+ || d->dev != sl.d->dev
+ || d->type != sl.d->type
+ || d->qid.path != sl.d->qid.path){
+ free(sl.name);
+ sl.name = strdup(logname);
+ if(sl.name == nil)
+ cons = 1;
+ else{
+ _syslogopen();
+ if(sl.fd < 0)
+ cons = 1;
+ free(sl.d);
+ sl.d = d;
+ d = nil; /* don't free it */
+ }
+ }
+ free(d);
+ if(cons){
+ d = dirfstat(sl.consfd);
+ if(sl.consfd < 0
+ || d == nil
+ || sl.consd == nil
+ || d->dev != sl.consd->dev
+ || d->type != sl.consd->type
+ || d->qid.path != sl.consd->qid.path){
+ sl.consfd = open("#c/cons", OWRITE|OCEXEC);
+ free(sl.consd);
+ sl.consd = d;
+ d = nil; /* don't free it */
+ }
+ free(d);
+ }
+
+ if(fmt == nil){
+ unlock(&sl);
+ return;
+ }
+
+ ctim = ctime(time(0));
+ werrstr(err);
+ p = buf + snprint(buf, sizeof(buf)-1, "%s ", sysname());
+ strncpy(p, ctim+4, 15);
+ p += 15;
+ *p++ = ' ';
+ va_start(arg, fmt);
+ p = vseprint(p, buf+sizeof(buf)-1, fmt, arg);
+ va_end(arg);
+ *p++ = '\n';
+ n = p - buf;
+
+ if(sl.fd >= 0){
+ seek(sl.fd, 0, 2);
+ write(sl.fd, buf, n);
+ }
+
+ if(cons && sl.consfd >=0)
+ write(sl.consfd, buf, n);
+
+ unlock(&sl);
+}
diff --git a/sys/src/libc/9sys/sysname.c b/sys/src/libc/9sys/sysname.c
new file mode 100755
index 000000000..1854ddceb
--- /dev/null
+++ b/sys/src/libc/9sys/sysname.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+sysname(void)
+{
+ int f, n;
+ static char b[128];
+
+ if(b[0])
+ return b;
+
+ f = open("#c/sysname", 0);
+ if(f >= 0) {
+ n = read(f, b, sizeof(b)-1);
+ if(n > 0)
+ b[n] = 0;
+ close(f);
+ }
+ return b;
+}
diff --git a/sys/src/libc/9sys/time.c b/sys/src/libc/9sys/time.c
new file mode 100755
index 000000000..3e5f83b04
--- /dev/null
+++ b/sys/src/libc/9sys/time.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+
+
+/*
+ * After a fork with fd's copied, both fd's are pointing to
+ * the same Chan structure. Since the offset is kept in the Chan
+ * structure, the seek's and read's in the two processes can
+ * compete at moving the offset around. Hence the unusual loop
+ * in the middle of this routine.
+ */
+static long
+oldtime(long *tp)
+{
+ char b[20];
+ static int f = -1;
+ int i, retries;
+ long t;
+
+ memset(b, 0, sizeof(b));
+ for(retries = 0; retries < 100; retries++){
+ if(f < 0)
+ f = open("/dev/time", OREAD|OCEXEC);
+ if(f < 0)
+ break;
+ if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){
+ close(f);
+ f = -1;
+ } else {
+ if(i != 0)
+ break;
+ }
+ }
+ t = atol(b);
+ if(tp)
+ *tp = t;
+ return t;
+}
+
+long
+time(long *tp)
+{
+ vlong t;
+
+ t = nsec()/1000000000LL;
+ if(t == 0)
+ t = oldtime(0);
+ if(tp != nil)
+ *tp = t;
+ return t;
+}
diff --git a/sys/src/libc/9sys/times.c b/sys/src/libc/9sys/times.c
new file mode 100755
index 000000000..b8303474f
--- /dev/null
+++ b/sys/src/libc/9sys/times.c
@@ -0,0 +1,60 @@
+#include <u.h>
+#include <libc.h>
+
+static
+char*
+skip(char *p)
+{
+
+ while(*p == ' ')
+ p++;
+ while(*p != ' ' && *p != 0)
+ p++;
+ return p;
+}
+
+/*
+ * after a fork with fd's copied, both fd's are pointing to
+ * the same Chan structure. Since the offset is kept in the Chan
+ * structure, the seek's and read's in the two processes can be
+ * are competing moving the offset around. Hence the unusual loop
+ * in the middle of this routine.
+ */
+long
+times(long *t)
+{
+ char b[200], *p;
+ static int f = -1;
+ int i, retries;
+ ulong r;
+
+ memset(b, 0, sizeof(b));
+ for(retries = 0; retries < 100; retries++){
+ if(f < 0)
+ f = open("/dev/cputime", OREAD|OCEXEC);
+ if(f < 0)
+ break;
+ if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){
+ close(f);
+ f = -1;
+ } else {
+ if(i != 0)
+ break;
+ }
+ }
+ p = b;
+ if(t)
+ t[0] = atol(p);
+ p = skip(p);
+ if(t)
+ t[1] = atol(p);
+ p = skip(p);
+ r = atol(p);
+ if(t){
+ p = skip(p);
+ t[2] = atol(p);
+ p = skip(p);
+ t[3] = atol(p);
+ }
+ return r;
+}
diff --git a/sys/src/libc/9sys/tm2sec.c b/sys/src/libc/9sys/tm2sec.c
new file mode 100755
index 000000000..223d3f1d9
--- /dev/null
+++ b/sys/src/libc/9sys/tm2sec.c
@@ -0,0 +1,194 @@
+#include <u.h>
+#include <libc.h>
+
+#define TZSIZE 150
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int y)
+{
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970 GMT
+ * and convert to our timezone.
+ */
+long
+tm2sec(Tm *tm)
+{
+ long secs;
+ int i, yday, year, *d2m;
+
+ if(strcmp(tm->zone, "GMT") != 0 && timezone.stname[0] == 0)
+ readtimezone();
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ year = tm->year + 1900;
+ for(i = 1970; i < year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * if mday is set, use mon and mday to compute yday
+ */
+ if(tm->mday){
+ yday = 0;
+ d2m = yrsize(year);
+ for(i=0; i<tm->mon; i++)
+ yday += d2m[i+1];
+ yday += tm->mday-1;
+ }else{
+ yday = tm->yday;
+ }
+ secs += yday * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->hour * SEC2HOUR;
+ secs += tm->min * SEC2MIN;
+ secs += tm->sec;
+
+ /*
+ * Only handles zones mentioned in /env/timezone,
+ * but things get too ambiguous otherwise.
+ */
+ if(strcmp(tm->zone, timezone.stname) == 0)
+ secs -= timezone.stdiff;
+ else if(strcmp(tm->zone, timezone.dlname) == 0)
+ secs -= timezone.dldiff;
+ if(secs < 0)
+ secs = 0;
+ return secs;
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf))
+ goto error;
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static int
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static int
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
+}
diff --git a/sys/src/libc/9sys/truerand.c b/sys/src/libc/9sys/truerand.c
new file mode 100755
index 000000000..10a0f14de
--- /dev/null
+++ b/sys/src/libc/9sys/truerand.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+ulong
+truerand(void)
+{
+ ulong x;
+ static int randfd = -1;
+
+ if(randfd < 0)
+ randfd = open("/dev/random", OREAD|OCEXEC);
+ if(randfd < 0)
+ sysfatal("can't open /dev/random");
+ if(read(randfd, &x, sizeof(x)) != sizeof(x))
+ sysfatal("can't read /dev/random");
+ return x;
+}
diff --git a/sys/src/libc/9sys/wait.c b/sys/src/libc/9sys/wait.c
new file mode 100755
index 000000000..e2ac817ea
--- /dev/null
+++ b/sys/src/libc/9sys/wait.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+Waitmsg*
+wait(void)
+{
+ int n, l;
+ char buf[512], *fld[5];
+ Waitmsg *w;
+
+ n = await(buf, sizeof buf-1);
+ if(n < 0)
+ return nil;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return nil;
+ }
+ l = strlen(fld[4])+1;
+ w = malloc(sizeof(Waitmsg)+l);
+ if(w == nil)
+ return nil;
+ w->pid = atoi(fld[0]);
+ w->time[0] = atoi(fld[1]);
+ w->time[1] = atoi(fld[2]);
+ w->time[2] = atoi(fld[3]);
+ w->msg = (char*)&w[1];
+ memmove(w->msg, fld[4], l);
+ return w;
+}
+
diff --git a/sys/src/libc/9sys/waitpid.c b/sys/src/libc/9sys/waitpid.c
new file mode 100755
index 000000000..098c428cf
--- /dev/null
+++ b/sys/src/libc/9sys/waitpid.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+waitpid(void)
+{
+ int n;
+ char buf[512], *fld[5];
+
+ n = await(buf, sizeof buf-1);
+ if(n <= 0)
+ return -1;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return -1;
+ }
+ return atoi(fld[0]);
+}
+
diff --git a/sys/src/libc/9sys/werrstr.c b/sys/src/libc/9sys/werrstr.c
new file mode 100755
index 000000000..da542aa17
--- /dev/null
+++ b/sys/src/libc/9sys/werrstr.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+void
+werrstr(char *fmt, ...)
+{
+ va_list arg;
+ char buf[ERRMAX];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+ERRMAX, fmt, arg);
+ va_end(arg);
+ errstr(buf, ERRMAX);
+}
diff --git a/sys/src/libc/9sys/write.c b/sys/src/libc/9sys/write.c
new file mode 100755
index 000000000..5bbd3064e
--- /dev/null
+++ b/sys/src/libc/9sys/write.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+long
+write(int fd, void *buf, long n)
+{
+ return pwrite(fd, buf, n, -1LL);
+}
diff --git a/sys/src/libc/9sys/writev.c b/sys/src/libc/9sys/writev.c
new file mode 100755
index 000000000..e3692db27
--- /dev/null
+++ b/sys/src/libc/9sys/writev.c
@@ -0,0 +1,42 @@
+#include <u.h>
+#include <libc.h>
+
+static
+long
+iowritev(int fd, IOchunk *io, int nio, vlong offset)
+{
+ int i;
+ long tot;
+ char *buf, *p;
+
+ tot = 0;
+ for(i=0; i<nio; i++)
+ tot += io[i].len;
+ buf = malloc(tot);
+ if(buf == nil)
+ return -1;
+
+ p = buf;
+ for(i=0; i<nio; i++){
+ memmove(p, io->addr, io->len);
+ p += io->len;
+ io++;
+ }
+
+ tot = pwrite(fd, buf, tot, offset);
+
+ free(buf);
+ return tot;
+}
+
+long
+writev(int fd, IOchunk *io, int nio)
+{
+ return iowritev(fd, io, nio, -1LL);
+}
+
+long
+pwritev(int fd, IOchunk *io, int nio, vlong off)
+{
+ return iowritev(fd, io, nio, off);
+}
diff --git a/sys/src/libc/9syscall/mkfile b/sys/src/libc/9syscall/mkfile
new file mode 100755
index 000000000..081310e34
--- /dev/null
+++ b/sys/src/libc/9syscall/mkfile
@@ -0,0 +1,145 @@
+NPROC=1
+</$objtype/mkfile
+
+install:V:
+ SYS=`{sed '/^#define._X[123]/d; s/#define.([A-Z0-9_]*).*/\1/' sys.h}
+ for(I in $SYS) {
+ i=`{echo $I|tr A-Z a-z}
+ n=`{sed -n '/[ ]'$I'[ ]/s/.* //p' sys.h}
+ if(~ $i exits) i=_exits
+ {switch($objtype){
+ case 68000 68020
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVL '$'$n, R0
+ echo TRAP '$0'
+ echo RTS
+ case mips
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R1, '0(FP)'
+ echo MOVW '$'$n, R1
+ echo SYSCALL
+ if(~ $i seek) {
+ echo 'MOVW $-1,R5
+ BNE R1,R5,4(PC)
+ MOVW a+0(FP),R5
+ MOVW R1,0(R5)
+ MOVW R1,4(R5)'
+ }
+ echo RET
+ case mips2
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R1, '0(FP)'
+ echo MOVW '$'$n, R1
+ echo ADD '$4',R29
+ echo SYSCALL
+ echo ADD '$-4',R29
+ echo RET
+ case spim
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R1, '0(FP)'
+ echo MOVW '$'$n, R1
+ echo ADD '$4',R29
+ echo SYSCALL
+ echo ADD '$-4',R29
+ if(~ $i seek) { # untested so far - geoff
+ echo 'MOVW $-1,R5
+ BNE R1,R5,4(PC)
+ MOVW a+0(FP),R5
+ MOVW R1,0(R5)
+ MOVW R1,4(R5)'
+ }
+ echo RET
+ case 386
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVL '$'$n, AX
+ echo INT '$'64
+ if(~ $i seek) {
+ echo 'CMPL AX,$-1
+ JNE 4(PC)
+ MOVL a+0(FP),CX
+ MOVL AX,0(CX)
+ MOVL AX,4(CX)'
+ }
+ echo RET
+ case amd64
+ if(~ $i seek)
+ echo TEXT _seek'(SB)', 1, '$0'
+ if not
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVQ RARG, 'a0+0(FP)'
+ echo MOVQ '$'$n, RARG
+ echo SYSCALL
+ echo RET
+ case sparc sparc64
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R7, '0(FP)'
+ echo MOVW '$'$n, R7
+ echo TA R0
+ if(~ $i seek) {
+ echo 'CMP R7,$-1
+ BNE 4(PC)
+ MOVW a+0(FP),R8
+ MOVW R7,0(R8)
+ MOVW R7,4(R8)'
+ }
+ echo RETURN
+ case 3210
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R3, '0(FP)'
+ echo MOVW '$'$n, R3
+ echo WORD '$0x06000000'
+ echo RETURN
+ case 29000
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVL R69, '0(FP)'
+ echo MOVL '$'$n, R69
+ echo EMULATE 0
+ echo RET
+ case arm
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R0, '0(FP)'
+ echo MOVW '$'$n, R0
+ echo SWI 0
+ if(~ $i seek) {
+ echo 'CMP $-1,R0
+ BNE 4(PC)
+ MOVW a+0(FP),R1
+ MOVW R0,0(R1)
+ MOVW R0,4(R1)'
+ }
+ echo RET
+ case power
+ echo TEXT $i'(SB)', 1, '$0'
+ echo MOVW R3, '0(FP)'
+ echo MOVW '$'$n, R3
+ echo SYSCALL
+ if(~ $i seek) {
+ echo 'CMP R3,$-1
+ BNE 4(PC)
+ MOVW a+0(FP),R8
+ MOVW R3,0(R8)
+ MOVW R3,4(R8)'
+ }
+ echo RETURN
+ case alpha
+ j=$i
+ if(~ $i seek) j=_seek
+ echo TEXT $j'(SB)', 1, '$0'
+ echo MOVL R0, '0(FP)'
+ echo MOVQ '$'$n, R0
+ echo CALL_PAL '$'0x83
+ echo RET
+ }} > $i.s
+ $AS $i.s
+ }
+ ar vu /$objtype/lib/libc.a *.$O
+ rm -f *.$O *.s
+
+nuke clean:V:
+ rm -f *.[$OS]
+
+installall:V:
+ for(objtype in $CPUS) mk install
+
+update:V:
+ update $UPDATEFLAGS mkfile sys.h
diff --git a/sys/src/libc/9syscall/sys.h b/sys/src/libc/9syscall/sys.h
new file mode 100755
index 000000000..105baf89c
--- /dev/null
+++ b/sys/src/libc/9syscall/sys.h
@@ -0,0 +1,50 @@
+#define SYSR1 0
+#define _ERRSTR 1
+#define BIND 2
+#define CHDIR 3
+#define CLOSE 4
+#define DUP 5
+#define ALARM 6
+#define EXEC 7
+#define EXITS 8
+#define _FSESSION 9
+#define FAUTH 10
+#define _FSTAT 11
+#define SEGBRK 12
+#define _MOUNT 13
+#define OPEN 14
+#define _READ 15
+#define OSEEK 16
+#define SLEEP 17
+#define _STAT 18
+#define RFORK 19
+#define _WRITE 20
+#define PIPE 21
+#define CREATE 22
+#define FD2PATH 23
+#define BRK_ 24
+#define REMOVE 25
+#define _WSTAT 26
+#define _FWSTAT 27
+#define NOTIFY 28
+#define NOTED 29
+#define SEGATTACH 30
+#define SEGDETACH 31
+#define SEGFREE 32
+#define SEGFLUSH 33
+#define RENDEZVOUS 34
+#define UNMOUNT 35
+#define _WAIT 36
+#define SEMACQUIRE 37
+#define SEMRELEASE 38
+#define SEEK 39
+#define FVERSION 40
+#define ERRSTR 41
+#define STAT 42
+#define FSTAT 43
+#define WSTAT 44
+#define FWSTAT 45
+#define MOUNT 46
+#define AWAIT 47
+#define PREAD 50
+#define PWRITE 51
diff --git a/sys/src/libc/alpha/_seek.c b/sys/src/libc/alpha/_seek.c
new file mode 100755
index 000000000..0aa741a66
--- /dev/null
+++ b/sys/src/libc/alpha/_seek.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+extern int _seek(vlong*, int, vlong, int);
+
+vlong
+seek(int fd, vlong o, int p)
+{
+ vlong l;
+
+ if(_seek(&l, fd, o, p) < 0)
+ l = -1LL;
+ return l;
+}
diff --git a/sys/src/libc/alpha/argv0.s b/sys/src/libc/alpha/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/alpha/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/alpha/atom.s b/sys/src/libc/alpha/atom.s
new file mode 100755
index 000000000..e9dbd14aa
--- /dev/null
+++ b/sys/src/libc/alpha/atom.s
@@ -0,0 +1,67 @@
+TEXT ainc(SB),$-8 /* long ainc(long *); */
+ MOVQ R0, R1 /* p */
+inc1:
+ MOVLL (R1), R2 /* *p */
+ ADDL $1, R2
+ MOVQ R2, R0 /* copy to return */
+ MOVLC R2, (R1) /* (*p)++ */
+ BEQ R2, inc1 /* write failed, retry */
+ RET
+
+TEXT adec(SB),$-8 /* long ainc(long *); */
+ MOVQ R0, R1 /* p */
+dec1:
+ MOVLL (R1), R2 /* *p */
+ SUBL $1, R2
+ MOVQ R2, R0 /* copy to return */
+ MOVLC R2, (R1) /* (*p)++ */
+ BEQ R2, dec1 /* write failed, retry */
+ RET
+
+TEXT _xinc(SB), $-8
+ MOVQ R0, R1 /* p */
+xinc1:
+ MOVLL (R1), R0 /* *p */
+ ADDL $1, R0
+ MOVLC R0, (R1) /* (*p)++ */
+ BEQ R0, xinc1 /* write failed, retry */
+ RET
+
+TEXT _xdec(SB), $-8
+ MOVQ R0, R1 /* p */
+xdec1:
+ MOVLL (R1), R0 /* *p */
+ SUBL $1, R0
+ MOVQ R0, R2
+ MOVLC R2, (R1) /* --(*p) */
+ BEQ R2, xdec1 /* write failed, retry */
+ RET
+
+TEXT cas(SB), $-8
+TEXT casp(SB), $-8
+ MOVQ R0, R1 /* p */
+ MOVL old+4(FP), R2
+ MOVL new+8(FP), R3
+ MOVLL (R1), R0
+ CMPEQ R0, R2, R4
+ BEQ R4, fail /* if R0 != [sic] R2, goto fail */
+ MOVQ R3, R0
+ MOVLC R0, (R1)
+ RET
+fail:
+ MOVL $0, R0
+ RET
+
+TEXT loadlink(SB), $-8
+ MOVLL (R0), R0
+ RET
+
+TEXT storecond(SB), $-8
+ MOVW val+4(FP), R1
+ MOVLC R1, (R0)
+ BEQ R1, storecondfail /* write failed */
+ MOVW $1, R0
+ RET
+storecondfail:
+ MOVW $0, R0
+ RET
diff --git a/sys/src/libc/alpha/cycles.c b/sys/src/libc/alpha/cycles.c
new file mode 100755
index 000000000..9bad3a989
--- /dev/null
+++ b/sys/src/libc/alpha/cycles.c
@@ -0,0 +1,7 @@
+#include <u.h>
+#include <libc.h>
+
+void cycles(uvlong*u)
+{
+ *u = 0LL;
+}
diff --git a/sys/src/libc/alpha/divl.s b/sys/src/libc/alpha/divl.s
new file mode 100755
index 000000000..bce568a8e
--- /dev/null
+++ b/sys/src/libc/alpha/divl.s
@@ -0,0 +1,189 @@
+/*
+ * ulong
+ * _udiv(ulong num, ulong den)
+ * {
+ * int i;
+ * ulong quo;
+ *
+ * if(den == 0)
+ * *(ulong*)-1 = 0;
+ * quo = num;
+ * if(quo > 1<<(32-1))
+ * quo = 1<<(32-1);
+ * for(i=0; den<quo; i++)
+ * den <<= 1;
+ * quo = 0;
+ * for(; i>=0; i--) {
+ * quo <<= 1;
+ * if(num >= den) {
+ * num -= den;
+ * quo |= 1;
+ * }
+ * den >>= 1;
+ * }
+ * return quo::num;
+ * }
+ */
+
+#define NOPROF 1
+
+/*
+ * calling sequence:
+ * num: 8(R30)
+ * den: 12(R30)
+ * returns
+ * quo: 8(R30)
+ * rem: 12(R30)
+ */
+TEXT _udivmodl(SB), NOPROF, $-8
+
+ MOVQ $-1, R11
+ SLLQ $31, R11 /* (1<<31) in canonical form */
+ MOVL 8(R30), R23 /* numerator */
+ MOVL 12(R30), R10 /* denominator */
+ BNE R10, udm20
+ MOVQ R31, -1(R31) /* fault -- divide by zero; todo: use gentrap? */
+udm20:
+ MOVQ R23, R12
+ BGE R12, udm34
+ MOVQ R11, R12
+udm34:
+ MOVQ R31, R11
+udm38:
+ CMPUGE R10, R12, R24
+ BNE R24, udm54
+ SLLL $1, R10
+ ADDQ $1, R11
+ JMP udm38
+udm54:
+ MOVQ R31, R12
+udm58:
+ BLT R11, udm8c
+ SLLL $1, R12
+ CMPUGE R23, R10, R24
+ BEQ R24, udm7c
+ SUBL R10, R23
+ OR $1, R12
+udm7c:
+ SRLL $1, R10
+ SUBQ $1, R11
+ JMP udm58
+udm8c:
+ MOVL R12, 8(R30) /* quotient */
+ MOVL R23, 12(R30) /* remainder */
+ RET
+
+/*
+ * save working registers
+ * and bring in num/den parameters
+ */
+TEXT _unsargl(SB), NOPROF, $-8
+ MOVQ R10, 24(R30)
+ MOVQ R11, 32(R30)
+ MOVQ R12, 40(R30)
+ MOVQ R23, 48(R30)
+ MOVQ R24, 56(R30)
+
+ MOVL R27, 8(R30)
+ MOVL 72(R30), R27
+ MOVL R27, 12(R30)
+
+ RET
+
+/*
+ * save working registers
+ * and bring in absolute value
+ * of num/den parameters
+ */
+TEXT _absargl(SB), NOPROF, $-8
+ MOVQ R10, 24(R30)
+ MOVQ R11, 32(R30)
+ MOVQ R12, 40(R30)
+ MOVQ R23, 48(R30)
+ MOVQ R24, 56(R30)
+
+ MOVL R27, 64(R30)
+ BGE R27, ab1
+ SUBL R27, R31, R27
+ab1:
+ MOVL R27, 8(R30) /* numerator */
+
+ MOVL 72(R30), R27
+ BGE R27, ab2
+ SUBL R27, R31, R27
+ab2:
+ MOVL R27, 12(R30) /* denominator */
+ RET
+
+/*
+ * restore registers and
+ * return to original caller
+ * answer is in R27
+ */
+TEXT _retargl(SB), NOPROF, $-8
+ MOVQ 24(R30), R10
+ MOVQ 32(R30), R11
+ MOVQ 40(R30), R12
+ MOVQ 48(R30), R23
+ MOVQ 56(R30), R24
+ MOVL 0(R30), R26
+
+ ADDQ $64, R30
+ RET /* back to main sequence */
+
+TEXT _divl(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVL R26, 0(R30)
+
+ JSR _absargl(SB)
+ JSR _udivmodl(SB)
+ MOVL 8(R30), R27
+
+ MOVL 64(R30), R10 /* clean up the sign */
+ MOVL 72(R30), R11
+ XOR R11, R10
+ BGE R10, div1
+ SUBL R27, R31, R27
+div1:
+
+ JSR _retargl(SB)
+ RET /* not executed */
+
+TEXT _divlu(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVL R26, 0(R30)
+
+ JSR _unsargl(SB)
+ JSR _udivmodl(SB)
+ MOVL 8(R30), R27
+
+ JSR _retargl(SB)
+ RET /* not executed */
+
+TEXT _modl(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVL R26, 0(R30)
+
+ JSR _absargl(SB)
+ JSR _udivmodl(SB)
+ MOVL 12(R30), R27
+
+ MOVL 64(R30), R10 /* clean up the sign */
+ BGE R10, div2
+ SUBL R27, R31, R27
+div2:
+
+ JSR _retargl(SB)
+ RET /* not executed */
+
+TEXT _modlu(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVL R26, 0(R30)
+
+ JSR _unsargl(SB)
+ JSR _udivmodl(SB)
+ MOVL 12(R30), R27
+
+ JSR _retargl(SB)
+ RET /* not executed */
+
diff --git a/sys/src/libc/alpha/divq.s b/sys/src/libc/alpha/divq.s
new file mode 100755
index 000000000..98a58e4a8
--- /dev/null
+++ b/sys/src/libc/alpha/divq.s
@@ -0,0 +1,191 @@
+/*
+ * uvlong
+ * _udiv(uvlong num, uvlong den)
+ * {
+ * int i;
+ * uvlong quo;
+ *
+ * if(den == 0)
+ * *(ulong*)-1 = 0;
+ * quo = num;
+ * if(quo > 1<<(64-1))
+ * quo = 1<<(64-1);
+ * for(i=0; den<quo; i++)
+ * den <<= 1;
+ * quo = 0;
+ * for(; i>=0; i--) {
+ * quo <<= 1;
+ * if(num >= den) {
+ * num -= den;
+ * quo |= 1;
+ * }
+ * den >>= 1;
+ * }
+ * return quo::num;
+ * }
+ */
+
+#define NOPROF 1
+
+/*
+ * calling sequence:
+ * num: 8(R30)
+ * den: 16(R30)
+ * returns
+ * quo: 8(R30)
+ * rem: 16(R30)
+ */
+TEXT _udivmodq(SB), NOPROF, $-8
+
+ MOVQ $1, R11
+ SLLQ $63, R11
+ MOVQ 8(R30), R23 /* numerator */
+ MOVQ 16(R30), R10 /* denominator */
+ BNE R10, udm20
+ MOVQ R31, -1(R31) /* fault -- divide by zero; todo: use gentrap? */
+udm20:
+ MOVQ R23, R12
+ BGE R12, udm34
+ MOVQ R11, R12
+udm34:
+ MOVQ R31, R11
+udm38:
+ CMPUGE R10, R12, R24
+ BNE R24, udm54
+ SLLQ $1, R10
+ ADDQ $1, R11
+ JMP udm38
+udm54:
+ MOVQ R31, R12
+udm58:
+ BLT R11, udm8c
+ SLLQ $1, R12
+ CMPUGE R23, R10, R24
+ BEQ R24, udm7c
+ SUBQ R10, R23
+ OR $1, R12
+udm7c:
+ SRLQ $1, R10
+ SUBQ $1, R11
+ JMP udm58
+udm8c:
+ MOVQ R12, 8(R30) /* quotient */
+ MOVQ R23, 16(R30) /* remainder */
+ RET
+
+/*
+ * save working registers
+ * and bring in num/den parameters
+ */
+TEXT _unsargq(SB), NOPROF, $-8
+ MOVQ R10, 24(R30)
+ MOVQ R11, 32(R30)
+ MOVQ R12, 40(R30)
+ MOVQ R23, 48(R30)
+ MOVQ R24, 56(R30)
+
+ MOVQ R27, 8(R30)
+ MOVQ 72(R30), R27
+ MOVQ R27, 16(R30)
+
+MOVQ (R30), R10 /* debug */
+ RET
+
+/*
+ * save working registers
+ * and bring in absolute value
+ * of num/den parameters
+ */
+TEXT _absargq(SB), NOPROF, $-8
+ MOVQ R10, 24(R30)
+ MOVQ R11, 32(R30)
+ MOVQ R12, 40(R30)
+ MOVQ R23, 48(R30)
+ MOVQ R24, 56(R30)
+
+ MOVQ R27, 64(R30)
+ BGE R27, ab1
+ SUBQ R27, R31, R27
+ab1:
+ MOVQ R27, 8(R30) /* numerator */
+
+ MOVQ 72(R30), R27
+ BGE R27, ab2
+ SUBQ R27, R31, R27
+ab2:
+ MOVQ R27, 16(R30) /* denominator */
+MOVQ (R30), R10 /* debug */
+ RET
+
+/*
+ * restore registers and
+ * return to original caller
+ * answer is in R27
+ */
+TEXT _retargq(SB), NOPROF, $-8
+ MOVQ 24(R30), R10
+ MOVQ 32(R30), R11
+ MOVQ 40(R30), R12
+ MOVQ 48(R30), R23
+ MOVQ 56(R30), R24
+ MOVL 0(R30), R26
+
+ ADDQ $64, R30
+ RET /* back to main sequence */
+
+TEXT _divq(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVQ R26, 0(R30)
+
+ JSR _absargq(SB)
+ JSR _udivmodq(SB)
+ MOVQ 8(R30), R27
+
+ MOVQ 64(R30), R10 /* clean up the sign */
+ MOVQ 72(R30), R11
+ XOR R11, R10
+ BGE R10, div1
+ SUBQ R27, R31, R27
+div1:
+
+ JSR _retargq(SB)
+ RET /* not executed */
+
+TEXT _divqu(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVQ R26, 0(R30)
+
+ JSR _unsargq(SB)
+ JSR _udivmodq(SB)
+ MOVQ 8(R30), R27
+
+ JSR _retargq(SB)
+ RET /* not executed */
+
+TEXT _modq(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVQ R26, 0(R30)
+
+ JSR _absargq(SB)
+ JSR _udivmodq(SB)
+ MOVQ 16(R30), R27
+
+ MOVQ 64(R30), R10 /* clean up the sign */
+ BGE R10, div2
+ SUBQ R27, R31, R27
+div2:
+
+ JSR _retargq(SB)
+ RET /* not executed */
+
+TEXT _modqu(SB), NOPROF, $-8
+ SUBQ $64, R30 /* 5 reg save, 2 parameters, link */
+ MOVQ R26, 0(R30)
+
+ JSR _unsargq(SB)
+ JSR _udivmodq(SB)
+ MOVQ 16(R30), R27
+
+ JSR _retargq(SB)
+ RET /* not executed */
+
diff --git a/sys/src/libc/alpha/getcallerpc.s b/sys/src/libc/alpha/getcallerpc.s
new file mode 100755
index 000000000..2214561be
--- /dev/null
+++ b/sys/src/libc/alpha/getcallerpc.s
@@ -0,0 +1,4 @@
+TEXT getcallerpc(SB), $-8
+ MOVL 0(SP), R0
+ RET
+
diff --git a/sys/src/libc/alpha/getfcr.s b/sys/src/libc/alpha/getfcr.s
new file mode 100755
index 000000000..06df5de98
--- /dev/null
+++ b/sys/src/libc/alpha/getfcr.s
@@ -0,0 +1,57 @@
+#define EXCB WORD $0x60000400 /* until 7a/7l catch up */
+
+TEXT getfsr(SB), $8
+ EXCB
+ MOVT FPCR, F0
+ EXCB
+ MOVT F0, tmp-8(SP)
+ MOVL tmp-4(SP), R1
+ MOVQ $0x01e00000, R2
+ AND R2, R1, R0
+ RET
+
+TEXT setfsr(SB), $8
+ MOVQ $0x01e00000, R2
+ EXCB
+ MOVT FPCR, F0
+ EXCB
+ MOVT F0, tmp-8(SP)
+ MOVL tmp-4(SP), R1
+ ANDNOT R2, R1, R3
+ AND R2, R0, R4
+ OR R3, R4, R5
+ MOVL R5, tmp-4(SP)
+ MOVT tmp-8(SP), F0
+ EXCB
+ MOVT F0, FPCR
+ EXCB
+ RET
+
+TEXT getfcr(SB), $8
+ EXCB
+ MOVT FPCR, F0
+ EXCB
+ MOVT F0, tmp-8(SP)
+ MOVL tmp-4(SP), R1
+ MOVQ $0x700c0000, R2
+ AND R2, R1, R0
+ XOR R2, R0
+ RET
+
+TEXT setfcr(SB), $8
+ MOVQ $0x700c0000, R2
+ XOR R2, R0
+ EXCB
+ MOVT FPCR, F0
+ EXCB
+ MOVT F0, tmp-8(SP)
+ MOVL tmp-4(SP), R1
+ ANDNOT R2, R1, R3
+ AND R2, R0, R4
+ OR R3, R4, R5
+ MOVL R5, tmp-4(SP)
+ MOVT tmp-8(SP), F0
+ EXCB
+ MOVT F0, FPCR
+ EXCB
+ RET
diff --git a/sys/src/libc/alpha/main9.s b/sys/src/libc/alpha/main9.s
new file mode 100755
index 000000000..5e51d5d28
--- /dev/null
+++ b/sys/src/libc/alpha/main9.s
@@ -0,0 +1,27 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVQ $setSB(SB), R29
+ MOVL R0, _tos(SB)
+
+ MOVQ $p-64(SP),R1
+ MOVL R1,_privates+0(SB)
+ MOVQ $16,R1
+ MOVL R1,_nprivates+0(SB)
+
+ MOVL inargc-8(FP), R0
+ MOVL $inargv-4(FP), R1
+ MOVL R0, 8(R30)
+ MOVL R1, 12(R30)
+ JSR main(SB)
+loop:
+ MOVL $_exitstr<>(SB), R0
+ MOVL R0, 8(R30)
+ JSR exits(SB)
+ MOVQ $_divq(SB), R31 /* force loading of divq */
+ MOVQ $_divl(SB), R31 /* force loading of divl */
+ JMP loop
+
+DATA _exitstr<>+0(SB)/4, $"main"
+GLOBL _exitstr<>+0(SB), $5
diff --git a/sys/src/libc/alpha/main9p.s b/sys/src/libc/alpha/main9p.s
new file mode 100755
index 000000000..defdb4842
--- /dev/null
+++ b/sys/src/libc/alpha/main9p.s
@@ -0,0 +1,37 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $16
+
+ MOVQ $setSB(SB), R29
+ MOVL R0, _tos(SB)
+
+ MOVQ $p-64(SP),R1
+ MOVL R1,_privates+0(SB)
+ MOVQ $16,R1
+ MOVL R1,_nprivates+0(SB)
+ JSR _profmain(SB)
+ MOVL __prof+4(SB), R0
+ MOVL R0, __prof+0(SB)
+ MOVL inargc-4(FP), R0
+ MOVL $inargv+0(FP), R1
+ MOVL R0, 8(R30)
+ MOVL R1, 12(R30)
+ JSR main(SB)
+loop:
+ MOVQ $exits<>(SB), R0
+ MOVL R0, 8(R30)
+ JSR exits(SB)
+ MOVQ $_divq(SB), R31 /* force loading of divq */
+ MOVQ $_divl(SB), R31 /* force loading of divl */
+ MOVQ $_profin(SB), R31 /* force loading of profile */
+ JMP loop
+
+TEXT _savearg(SB), 1, $0
+ RET
+
+TEXT _callpc(SB), 1, $0
+ MOVL argp-8(FP), R0
+ RET
+
+DATA exits<>+0(SB)/4, $"main"
+GLOBL exits<>+0(SB), $5
diff --git a/sys/src/libc/alpha/memmove.s b/sys/src/libc/alpha/memmove.s
new file mode 100755
index 000000000..8a77f68d7
--- /dev/null
+++ b/sys/src/libc/alpha/memmove.s
@@ -0,0 +1,201 @@
+#define QUAD 8
+#define ALIGN 64
+#define BLOCK 64
+
+TEXT memmove(SB), $0
+_memmove:
+ MOVL from+4(FP), R7
+ MOVL n+8(FP), R10
+ MOVQ R0, R6
+
+ CMPUGE R7, R0, R5
+ BNE R5, _forward
+
+ MOVQ R6, R8 /* end to address */
+ ADDL R10, R6, R6 /* to+n */
+ ADDL R10, R7, R7 /* from+n */
+
+ CMPUGE $ALIGN, R10, R1 /* need at least ALIGN bytes */
+ BNE R1, _b1tail
+
+_balign:
+ AND $(ALIGN-1), R6, R1
+ BEQ R1, _baligned
+
+ MOVBU -1(R7), R2
+ ADDL $-1, R6, R6
+ MOVB R2, (R6)
+ ADDL $-1, R7, R7
+ JMP _balign
+
+_baligned:
+ AND $(QUAD-1), R7, R1 /* is the source quad-aligned */
+ BNE R1, _bunaligned
+
+ ADDL $(BLOCK-1), R8, R9
+_bblock:
+ CMPUGE R9, R6, R1
+ BNE R1, _b8tail
+
+ MOVQ -64(R7), R22
+ MOVQ -56(R7), R23
+ MOVQ -48(R7), R24
+ MOVQ -40(R7), R25
+ MOVQ -32(R7), R2
+ MOVQ -24(R7), R3
+ MOVQ -16(R7), R4
+ MOVQ -8(R7), R5
+
+ SUBL $64, R6, R6
+ SUBL $64, R7, R7
+
+ MOVQ R22, (R6)
+ MOVQ R23, 8(R6)
+ MOVQ R24, 16(R6)
+ MOVQ R25, 24(R6)
+ MOVQ R2, 32(R6)
+ MOVQ R3, 40(R6)
+ MOVQ R4, 48(R6)
+ MOVQ R5, 56(R6)
+ JMP _bblock
+
+_b8tail:
+ ADDL $(QUAD-1), R8, R9
+_b8block:
+ CMPUGE R9, R6, R1
+ BNE R1, _b1tail
+
+ MOVQ -8(R7), R2
+ SUBL $8, R6
+ MOVQ R2, (R6)
+ SUBL $8, R7
+ JMP _b8block
+
+_b1tail:
+ CMPUGE R8, R6, R1
+ BNE R1, _ret
+
+ MOVBU -1(R7), R2
+ SUBL $1, R6, R6
+ MOVB R2, (R6)
+ SUBL $1, R7, R7
+ JMP _b1tail
+_ret:
+ RET
+
+_bunaligned:
+ ADDL $(16-1), R8, R9
+
+_bu8block:
+ CMPUGE R9, R6, R1
+ BNE R1, _b1tail
+
+ MOVQU -16(R7), R4
+ MOVQU -8(R7), R3
+ MOVQU (R7), R2
+ SUBL $16, R6
+ EXTQH R7, R2, R2
+ EXTQL R7, R3, R5
+ OR R5, R2, R11
+ EXTQH R7, R3, R3
+ EXTQL R7, R4, R4
+ OR R3, R4, R13
+ MOVQ R11, 8(R6)
+ MOVQ R13, (R6)
+ SUBL $16, R7
+ JMP _bu8block
+
+_forward:
+ ADDL R10, R6, R8 /* end to address */
+
+ CMPUGE $ALIGN, R10, R1 /* need at least ALIGN bytes */
+ BNE R1, _f1tail
+
+_falign:
+ AND $(ALIGN-1), R6, R1
+ BEQ R1, _faligned
+
+ MOVBU (R7), R2
+ ADDL $1, R6, R6
+ ADDL $1, R7, R7
+ MOVB R2, -1(R6)
+ JMP _falign
+
+_faligned:
+ AND $(QUAD-1), R7, R1 /* is the source quad-aligned */
+ BNE R1, _funaligned
+
+ SUBL $(BLOCK-1), R8, R9
+_fblock:
+ CMPUGT R9, R6, R1
+ BEQ R1, _f8tail
+
+ MOVQ (R7), R2
+ MOVQ 8(R7), R3
+ MOVQ 16(R7), R4
+ MOVQ 24(R7), R5
+ MOVQ 32(R7), R22
+ MOVQ 40(R7), R23
+ MOVQ 48(R7), R24
+ MOVQ 56(R7), R25
+
+ ADDL $64, R6, R6
+ ADDL $64, R7, R7
+
+ MOVQ R2, -64(R6)
+ MOVQ R3, -56(R6)
+ MOVQ R4, -48(R6)
+ MOVQ R5, -40(R6)
+ MOVQ R22, -32(R6)
+ MOVQ R23, -24(R6)
+ MOVQ R24, -16(R6)
+ MOVQ R25, -8(R6)
+ JMP _fblock
+
+_f8tail:
+ SUBL $(QUAD-1), R8, R9
+_f8block:
+ CMPUGT R9, R6, R1
+ BEQ R1, _f1tail
+
+ MOVQ (R7), R2
+ ADDL $8, R6
+ ADDL $8, R7
+ MOVQ R2, -8(R6)
+ JMP _f8block
+
+_f1tail:
+ CMPUGT R8, R6, R1
+ BEQ R1, _fret
+ MOVBU (R7), R2
+ ADDL $1, R6, R6
+ ADDL $1, R7, R7
+ MOVB R2, -1(R6)
+ JMP _f1tail
+
+_fret:
+ RET
+
+_funaligned:
+ SUBL $(16-1), R8, R9
+_fu8block:
+ CMPUGT R9, R6, R1
+ BEQ R1, _f1tail
+
+ MOVQU (R7), R2
+ MOVQU 8(R7), R3
+ MOVQU 16(R7), R4
+ EXTQL R7, R2, R2
+ EXTQH R7, R3, R5
+ OR R5, R2, R11
+ EXTQL R7, R3, R3
+ MOVQ R11, (R6)
+ EXTQH R7, R4, R4
+ OR R3, R4, R11
+ MOVQ R11, 8(R6)
+ ADDL $16, R6
+ ADDL $16, R7
+ JMP _fu8block
+
+TEXT memcpy(SB), $0
+ JMP _memmove
diff --git a/sys/src/libc/alpha/memset.s b/sys/src/libc/alpha/memset.s
new file mode 100755
index 000000000..e3cfd468b
--- /dev/null
+++ b/sys/src/libc/alpha/memset.s
@@ -0,0 +1,61 @@
+TEXT memset(SB), $0
+ MOVL R0, R6
+ MOVBU data+4(FP), R2
+ MOVL n+8(FP), R10
+
+ ADDL R10, R0, R8
+
+ CMPUGE $8, R10, R1 /* need at least 8 bytes */
+ BNE R1, _1loop
+
+ SLLQ $8, R2, R1 /* replicate the byte */
+ OR R1, R2
+ SLLQ $16, R2, R1
+ OR R1, R2
+ SLLQ $32, R2, R1
+ OR R1, R2
+
+_align:
+ AND $(8-1), R6, R1
+ BEQ R1, _aligned
+
+ MOVB R2, (R6)
+ ADDL $1, R6, R6
+ JMP _align
+
+_aligned:
+ SUBL $(64-1), R8, R9 /* end pointer minus slop */
+_64loop:
+ CMPUGT R9, R6, R1
+ BEQ R1, _8tail
+
+ MOVQ R2, (R6)
+ MOVQ R2, 8(R6)
+ MOVQ R2, 16(R6)
+ MOVQ R2, 24(R6)
+ MOVQ R2, 32(R6)
+ MOVQ R2, 40(R6)
+ MOVQ R2, 48(R6)
+ MOVQ R2, 56(R6)
+ ADDL $64, R6, R6
+ JMP _64loop
+
+_8tail:
+ SUBL $(8-1), R8, R9
+_8loop:
+ CMPUGT R9, R6, R1
+ BEQ R1, _1loop
+
+ MOVQ R2, (R6)
+ ADDL $8, R6
+ JMP _8loop
+
+_1loop:
+ CMPUGT R8, R6, R1
+ BEQ R1, _ret
+ MOVB R2, (R6)
+ ADDL $1, R6
+ JMP _1loop
+
+_ret:
+ RET
diff --git a/sys/src/libc/alpha/mkfile b/sys/src/libc/alpha/mkfile
new file mode 100755
index 000000000..ef66ac423
--- /dev/null
+++ b/sys/src/libc/alpha/mkfile
@@ -0,0 +1,33 @@
+objtype=alpha
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ atom.s\
+ divl.s\
+ divq.s\
+ getcallerpc.s\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memmove.s\
+ memset.s\
+ setjmp.s\
+ tas.s
+
+CFILES=\
+ _seek.c\
+ cycles.c\
+ notejmp.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/alpha/notejmp.c b/sys/src/libc/alpha/notejmp.c
new file mode 100755
index 000000000..dff612bce
--- /dev/null
+++ b/sys/src/libc/alpha/notejmp.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ r->r0 = ret;
+ if(ret == 0)
+ r->r0 = 1;
+ r->pc = j[JMPBUFPC];
+ r->sp = j[JMPBUFSP];
+ noted(NCONT);
+}
diff --git a/sys/src/libc/alpha/setjmp.s b/sys/src/libc/alpha/setjmp.s
new file mode 100755
index 000000000..dd423df02
--- /dev/null
+++ b/sys/src/libc/alpha/setjmp.s
@@ -0,0 +1,14 @@
+TEXT setjmp(SB), 1, $-8
+ MOVL R30, (R0)
+ MOVL R26, 4(R0)
+ MOVQ $0, R0
+ RET
+
+TEXT longjmp(SB), 1, $-8
+ MOVL r+4(FP), R3
+ BNE R3, ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVQ $1, R3 /* bless their pointed heads */
+ok: MOVL (R0), R30
+ MOVL 4(R0), R26
+ MOVL R3, R0
+ RET
diff --git a/sys/src/libc/alpha/tas.s b/sys/src/libc/alpha/tas.s
new file mode 100755
index 000000000..613eb7b91
--- /dev/null
+++ b/sys/src/libc/alpha/tas.s
@@ -0,0 +1,10 @@
+TEXT _tas(SB), $-8
+ MOVQ R0, R1 /* l */
+tas1:
+ MOVLL (R1), R0 /* l->key */
+ BNE R0, tas2
+ MOVQ $1, R2
+ MOVLC R2, (R1) /* l->key = 1 */
+ BEQ R2, tas1 /* write failed, try again? */
+tas2:
+ RET
diff --git a/sys/src/libc/arm/argv0.s b/sys/src/libc/arm/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/arm/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/arm/atom.s b/sys/src/libc/arm/atom.s
new file mode 100755
index 000000000..674021391
--- /dev/null
+++ b/sys/src/libc/arm/atom.s
@@ -0,0 +1,55 @@
+
+/*
+ * int cas(ulong *p, ulong ov, ulong nv);
+ */
+
+#define LDREX(a,r) WORD $(0xe<<28|0x01900f9f | (a)<<16 | (r)<<12)
+#define STREX(a,v,r) WORD $(0xe<<28|0x01800f90 | (a)<<16 | (r)<<12 | (v)<<0)
+
+TEXT cas+0(SB),0,$12 /* r0 holds p */
+TEXT casp+0(SB),0,$12 /* r0 holds p */
+TEXT casl+0(SB),0,$12 /* r0 holds p */
+ MOVW ov+4(FP), R1
+ MOVW nv+8(FP), R2
+spincas:
+ LDREX(0,3) /* LDREX 0(R0),R3 */
+ CMP.S R3, R1
+ BNE fail
+ STREX(0,2,4) /* STREX 0(R0),R2,R4 */
+ CMP.S $0, R4
+ BNE spincas
+ MOVW $1, R0
+ RET
+fail:
+ MOVW $0, R0
+ RET
+
+TEXT ainc(SB), $0 /* long ainc(long *); */
+spinainc:
+ LDREX(0,3) /* LDREX 0(R0),R3 */
+ ADD $1,R3
+ STREX(0,3,4) /* STREX 0(R0),R2,R4 */
+ CMP.S $0, R4
+ BNE spinainc
+ MOVW R3, R0
+ RET
+
+TEXT adec(SB), $0 /* long ainc(long *); */
+spinadec:
+ LDREX(0,3) /* LDREX 0(R0),R3 */
+ SUB $1,R3
+ STREX(0,3,4) /* STREX 0(R0),R3,R4 */
+ CMP.S $0, R4
+ BNE spinadec
+ MOVW R3, R0
+ RET
+
+TEXT loadlinked(SB), $0 /* long loadlinked(long *); */
+ LDREX(0,0) /* LDREX 0(R0),R0 */
+ RET
+
+TEXT storecond(SB), $0 /* int storecond(long *, long); */
+ MOVW ov+4(FP), R3
+ STREX(0,3,4) /* STREX 0(R0),R3,R0 */
+ RSB $1, R0
+ RET
diff --git a/sys/src/libc/arm/cas.s b/sys/src/libc/arm/cas.s
new file mode 100755
index 000000000..7bb3a05f7
--- /dev/null
+++ b/sys/src/libc/arm/cas.s
@@ -0,0 +1,28 @@
+
+/*
+ * int swp(int r, int *p);
+ * uchar swpb(uchar r, uchar *p);
+ *
+ * int cas(uintptr *p, uintptr ov, uintptr nv);
+ */
+
+#define LDREX(a,r) WORD $(0xe<<28|0x01900f9f | (a)<<16 | (r)<<12)
+#define STREX(a,v,r) WORD $(0xe<<28|0x01800f90 | (a)<<16 | (r)<<12 | (v)<<0)
+
+TEXT cas+0(SB),0,$12 /* r0 holds p */
+ MOVW ov+4(FP), R1
+ MOVW nv+8(FP), R2
+spin:
+/* LDREX 0(R0),R3 */
+ LDREX(0,3)
+ CMP.S R3, R1
+ BNE fail
+/* STREX 0(R0),R2,R4 */
+ STREX(0,2,4)
+ CMP.S $0, R4
+ BNE spin
+ MOVW $1, R0
+ RET
+fail:
+ MOVW $0, R0
+ RET
diff --git a/sys/src/libc/arm/cycles.c b/sys/src/libc/arm/cycles.c
new file mode 100755
index 000000000..9bad3a989
--- /dev/null
+++ b/sys/src/libc/arm/cycles.c
@@ -0,0 +1,7 @@
+#include <u.h>
+#include <libc.h>
+
+void cycles(uvlong*u)
+{
+ *u = 0LL;
+}
diff --git a/sys/src/libc/arm/div.s b/sys/src/libc/arm/div.s
new file mode 100755
index 000000000..2f7699c50
--- /dev/null
+++ b/sys/src/libc/arm/div.s
@@ -0,0 +1,118 @@
+Q = 0
+N = 1
+D = 2
+CC = 3
+TMP = 11
+
+TEXT save<>(SB), 1, $0
+ MOVW R(Q), 0(FP)
+ MOVW R(N), 4(FP)
+ MOVW R(D), 8(FP)
+ MOVW R(CC), 12(FP)
+
+ MOVW R(TMP), R(Q) /* numerator */
+ MOVW 20(FP), R(D) /* denominator */
+ CMP $0, R(D)
+ BNE s1
+ MOVW -1(R(D)), R(TMP) /* divide by zero fault */
+s1: RET
+
+TEXT rest<>(SB), 1, $0
+ MOVW 0(FP), R(Q)
+ MOVW 4(FP), R(N)
+ MOVW 8(FP), R(D)
+ MOVW 12(FP), R(CC)
+/*
+ * return to caller
+ * of rest<>
+ */
+ MOVW 0(R13), R14
+ ADD $20, R13
+ B (R14)
+
+TEXT div<>(SB), 1, $0
+ MOVW $32, R(CC)
+/*
+ * skip zeros 8-at-a-time
+ */
+e1:
+ AND.S $(0xff<<24),R(Q), R(N)
+ BNE e2
+ SLL $8, R(Q)
+ SUB.S $8, R(CC)
+ BNE e1
+ RET
+e2:
+ MOVW $0, R(N)
+
+loop:
+/*
+ * shift R(N||Q) left one
+ */
+ SLL $1, R(N)
+ CMP $0, R(Q)
+ ORR.LT $1, R(N)
+ SLL $1, R(Q)
+
+/*
+ * compare numerator to denominator
+ * if less, subtract and set quotent bit
+ */
+ CMP R(D), R(N)
+ ORR.HS $1, R(Q)
+ SUB.HS R(D), R(N)
+ SUB.S $1, R(CC)
+ BNE loop
+ RET
+
+TEXT _div(SB), 1, $16
+ BL save<>(SB)
+ CMP $0, R(Q)
+ BGE d1
+ RSB $0, R(Q), R(Q)
+ CMP $0, R(D)
+ BGE d2
+ RSB $0, R(D), R(D)
+d0:
+ BL div<>(SB) /* none/both neg */
+ MOVW R(Q), R(TMP)
+ B out
+d1:
+ CMP $0, R(D)
+ BGE d0
+ RSB $0, R(D), R(D)
+d2:
+ BL div<>(SB) /* one neg */
+ RSB $0, R(Q), R(TMP)
+ B out
+
+TEXT _mod(SB), 1, $16
+ BL save<>(SB)
+ CMP $0, R(D)
+ RSB.LT $0, R(D), R(D)
+ CMP $0, R(Q)
+ BGE m1
+ RSB $0, R(Q), R(Q)
+ BL div<>(SB) /* neg numerator */
+ RSB $0, R(N), R(TMP)
+ B out
+m1:
+ BL div<>(SB) /* pos numerator */
+ MOVW R(N), R(TMP)
+ B out
+
+TEXT _divu(SB), 1, $16
+ BL save<>(SB)
+ BL div<>(SB)
+ MOVW R(Q), R(TMP)
+ B out
+
+TEXT _modu(SB), 1, $16
+ BL save<>(SB)
+ BL div<>(SB)
+ MOVW R(N), R(TMP)
+ B out
+
+out:
+ BL rest<>(SB)
+ B out
diff --git a/sys/src/libc/arm/doprint.xc b/sys/src/libc/arm/doprint.xc
new file mode 100755
index 000000000..e3846a8d8
--- /dev/null
+++ b/sys/src/libc/arm/doprint.xc
@@ -0,0 +1,617 @@
+#include <u.h>
+#include <libc.h>
+
+enum
+{
+ SIZE = 1024,
+ IDIGIT = 40,
+ MAXCONV = 40,
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NONE = -1000,
+ MAXFMT = 512,
+
+ FPLUS = 1<<0,
+ FMINUS = 1<<1,
+ FSHARP = 1<<2,
+ FLONG = 1<<3,
+ FSHORT = 1<<4,
+ FUNSIGN = 1<<5,
+ FVLONG = 1<<6,
+};
+
+int printcol;
+
+static int convcount;
+static char fmtindex[MAXFMT];
+
+static int noconv(va_list*, Fconv*);
+static int flags(va_list*, Fconv*);
+
+static int cconv(va_list*, Fconv*);
+static int rconv(va_list*, Fconv*);
+static int sconv(va_list*, Fconv*);
+static int percent(va_list*, Fconv*);
+static int column(va_list*, Fconv*);
+
+int numbconv(va_list*, Fconv*);
+
+static
+int (*fmtconv[MAXCONV])(va_list*, Fconv*) =
+{
+ noconv
+};
+
+static
+void
+initfmt(void)
+{
+ int cc;
+
+ cc = 0;
+ fmtconv[cc] = noconv;
+ cc++;
+
+ fmtconv[cc] = flags;
+ fmtindex['+'] = cc;
+ fmtindex['-'] = cc;
+ fmtindex['#'] = cc;
+ fmtindex['h'] = cc;
+ fmtindex['l'] = cc;
+ fmtindex['u'] = cc;
+ cc++;
+
+ fmtconv[cc] = numbconv;
+ fmtindex['d'] = cc;
+ fmtindex['o'] = cc;
+ fmtindex['x'] = cc;
+ fmtindex['X'] = cc;
+ cc++;
+
+ fmtconv[cc] = cconv;
+ fmtindex['c'] = cc;
+ fmtindex['C'] = cc;
+ cc++;
+
+ fmtconv[cc] = rconv;
+ fmtindex['r'] = cc;
+ cc++;
+
+ fmtconv[cc] = sconv;
+ fmtindex['s'] = cc;
+ fmtindex['S'] = cc;
+ cc++;
+
+ fmtconv[cc] = percent;
+ fmtindex['%'] = cc;
+ cc++;
+
+ fmtconv[cc] = column;
+ fmtindex['|'] = cc;
+ cc++;
+
+ convcount = cc;
+}
+
+int
+fmtinstall(int c, int (*f)(va_list*, Fconv*))
+{
+
+ if(convcount == 0)
+ initfmt();
+ if(c < 0 || c >= MAXFMT)
+ return -1;
+ if(convcount >= MAXCONV)
+ return -1;
+ fmtconv[convcount] = f;
+ fmtindex[c] = convcount;
+ convcount++;
+ return 0;
+}
+
+char*
+doprint(char *s, char *es, char *fmt, va_list argp)
+{
+ int n, c;
+ Rune rune;
+ Fconv local;
+
+ if(s >= es)
+ return s;
+ local.out = s;
+ local.eout = es-UTFmax-1;
+
+loop:
+ c = *fmt & 0xff;
+ if(c >= Runeself) {
+ n = chartorune(&rune, fmt);
+ fmt += n;
+ c = rune;
+ } else
+ fmt++;
+ switch(c) {
+ case 0:
+ *local.out = 0;
+ return local.out;
+
+ default:
+ printcol++;
+ goto common;
+
+ case '\n':
+ printcol = 0;
+ goto common;
+
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ goto common;
+
+ common:
+ if(local.out < local.eout)
+ if(c >= Runeself) {
+ rune = c;
+ n = runetochar(local.out, &rune);
+ local.out += n;
+ } else
+ *local.out++ = c;
+ goto loop;
+
+ case '%':
+ break;
+ }
+ local.f1 = NONE;
+ local.f2 = NONE;
+ local.f3 = 0;
+
+ /*
+ * read one of the following
+ * 1. number, => f1, f2 in order.
+ * 2. '*' same as number (from args)
+ * 3. '.' ignored (separates numbers)
+ * 4. flag => f3
+ * 5. verb and terminate
+ */
+l0:
+ c = *fmt & 0xff;
+ if(c >= Runeself) {
+ n = chartorune(&rune, fmt);
+ fmt += n;
+ c = rune;
+ } else
+ fmt++;
+
+l1:
+ if(c == 0) {
+ fmt--;
+ goto loop;
+ }
+ if(c == '.') {
+ if(local.f1 == NONE)
+ local.f1 = 0;
+ local.f2 = 0;
+ goto l0;
+ }
+ if((c >= '1' && c <= '9') ||
+ (c == '0' && local.f1 != NONE)) { /* '0' is a digit for f2 */
+ n = 0;
+ while(c >= '0' && c <= '9') {
+ n = n*10 + c-'0';
+ c = *fmt++;
+ }
+ if(local.f1 == NONE)
+ local.f1 = n;
+ else
+ local.f2 = n;
+ goto l1;
+ }
+ if(c == '*') {
+ n = va_arg(argp, int);
+ if(local.f1 == NONE)
+ local.f1 = n;
+ else
+ local.f2 = n;
+ goto l0;
+ }
+ n = 0;
+ if(c >= 0 && c < MAXFMT)
+ n = fmtindex[c];
+ local.chr = c;
+ n = (*fmtconv[n])(&argp, &local);
+ if(n < 0) {
+ local.f3 |= -n;
+ goto l0;
+ }
+ goto loop;
+}
+
+int
+numbconv(va_list *arg, Fconv *fp)
+{
+ char s[IDIGIT];
+ int i, f, n, b, ucase;
+ short h;
+ long v;
+ vlong vl;
+
+ SET(v);
+ SET(vl);
+
+ ucase = 0;
+ b = fp->chr;
+ switch(fp->chr) {
+ case 'u':
+ fp->f3 |= FUNSIGN;
+ case 'd':
+ b = 10;
+ break;
+
+ case 'o':
+ b = 8;
+ break;
+
+ case 'X':
+ ucase = 1;
+ case 'x':
+ b = 16;
+ break;
+ }
+
+ f = 0;
+ switch(fp->f3 & (FVLONG|FLONG|FSHORT|FUNSIGN)) {
+ case FVLONG|FLONG:
+ vl = va_arg(*arg, vlong);
+ break;
+
+ case FUNSIGN|FVLONG|FLONG:
+ vl = va_arg(*arg, uvlong);
+ break;
+
+ case FLONG:
+ v = va_arg(*arg, long);
+ break;
+
+ case FUNSIGN|FLONG:
+ v = va_arg(*arg, ulong);
+ break;
+
+ case FSHORT:
+ h = va_arg(*arg, int);
+ v = h;
+ break;
+
+ case FUNSIGN|FSHORT:
+ h = va_arg(*arg, int);
+ v = (ushort)h;
+ break;
+
+ default:
+ v = va_arg(*arg, int);
+ break;
+
+ case FUNSIGN:
+ v = va_arg(*arg, unsigned);
+ break;
+ }
+ if(fp->f3 & FVLONG) {
+ if(!(fp->f3 & FUNSIGN) && vl < 0) {
+ vl = -vl;
+ f = 1;
+ }
+ } else {
+ if(!(fp->f3 & FUNSIGN) && v < 0) {
+ v = -v;
+ f = 1;
+ }
+ }
+ s[IDIGIT-1] = 0;
+ for(i = IDIGIT-2;; i--) {
+ if(fp->f3 & FVLONG)
+ n = (uvlong)vl % b;
+ else
+ n = (ulong)v % b;
+ n += '0';
+ if(n > '9') {
+ n += 'a' - ('9'+1);
+ if(ucase)
+ n += 'A'-'a';
+ }
+ s[i] = n;
+ if(i < 2)
+ break;
+ if(fp->f3 & FVLONG)
+ vl = (uvlong)vl / b;
+ else
+ v = (ulong)v / b;
+ if(fp->f2 != NONE && i >= IDIGIT-fp->f2)
+ continue;
+ if(fp->f3 & FVLONG) {
+ if(vl <= 0)
+ break;
+ continue;
+ }
+ if(v <= 0)
+ break;
+ }
+
+ if(fp->f3 & FSHARP) {
+ if(b == 8 && s[i] != '0')
+ s[--i] = '0';
+ if(b == 16) {
+ if(ucase)
+ s[--i] = 'X';
+ else
+ s[--i] = 'x';
+ s[--i] = '0';
+ }
+ }
+ if(f)
+ s[--i] = '-';
+ fp->f2 = NONE;
+ strconv(s+i, fp);
+ return 0;
+}
+
+void
+Strconv(Rune *s, Fconv *fp)
+{
+ int n, c, i;
+ Rune rune;
+
+ if(fp->f3 & FMINUS)
+ fp->f1 = -fp->f1;
+ n = 0;
+ if(fp->f1 != NONE && fp->f1 >= 0) {
+ for(; s[n]; n++)
+ ;
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+ for(;;) {
+ c = *s++;
+ if(c == 0)
+ break;
+ n++;
+ if(fp->f2 == NONE || fp->f2 > 0) {
+ if(fp->out < fp->eout)
+ if(c >= Runeself) {
+ rune = c;
+ i = runetochar(fp->out, &rune);
+ fp->out += i;
+ } else
+ *fp->out++ = c;
+ if(fp->f2 != NONE)
+ fp->f2--;
+ switch(c) {
+ default:
+ printcol++;
+ break;
+ case '\n':
+ printcol = 0;
+ break;
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ break;
+ }
+ }
+ }
+ if(fp->f1 != NONE && fp->f1 < 0) {
+ fp->f1 = -fp->f1;
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+}
+
+void
+strconv(char *s, Fconv *fp)
+{
+ int n, c, i;
+ Rune rune;
+
+ if(fp->f3 & FMINUS)
+ fp->f1 = -fp->f1;
+ n = 0;
+ if(fp->f1 != NONE && fp->f1 >= 0) {
+ n = utflen(s);
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+ for(;;) {
+ c = *s & 0xff;
+ if(c >= Runeself) {
+ i = chartorune(&rune, s);
+ s += i;
+ c = rune;
+ } else
+ s++;
+ if(c == 0)
+ break;
+ n++;
+ if(fp->f2 == NONE || fp->f2 > 0) {
+ if(fp->out < fp->eout)
+ if(c >= Runeself) {
+ rune = c;
+ i = runetochar(fp->out, &rune);
+ fp->out += i;
+ } else
+ *fp->out++ = c;
+ if(fp->f2 != NONE)
+ fp->f2--;
+ switch(c) {
+ default:
+ printcol++;
+ break;
+ case '\n':
+ printcol = 0;
+ break;
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ break;
+ }
+ }
+ }
+ if(fp->f1 != NONE && fp->f1 < 0) {
+ fp->f1 = -fp->f1;
+ while(n < fp->f1) {
+ if(fp->out < fp->eout)
+ *fp->out++ = ' ';
+ printcol++;
+ n++;
+ }
+ }
+}
+
+static
+int
+noconv(va_list *arg, Fconv *fp)
+{
+ int n;
+ char s[10];
+
+ if(convcount == 0) {
+ initfmt();
+ n = 0;
+ if(fp->chr >= 0 && fp->chr < MAXFMT)
+ n = fmtindex[fp->chr];
+ return (*fmtconv[n])(arg, fp);
+ }
+ s[0] = '*';
+ s[1] = fp->chr;
+ s[2] = '*';
+ s[3] = 0;
+ fp->f1 = 0;
+ fp->f2 = NONE;
+ fp->f3 = 0;
+ strconv(s, fp);
+ return 0;
+}
+
+static
+int
+rconv(va_list*, Fconv *fp)
+{
+ char s[ERRLEN];
+
+ s[0] = 0;
+ errstr(s);
+ fp->f2 = NONE;
+ strconv(s, fp);
+ return 0;
+}
+
+static
+int
+cconv(va_list *arg, Fconv *fp)
+{
+ char s[10];
+ Rune rune;
+
+ rune = va_arg(*arg, int);
+ if(fp->chr == 'c')
+ rune &= 0xff;
+ s[runetochar(s, &rune)] = 0;
+
+ fp->f2 = NONE;
+ strconv(s, fp);
+ return 0;
+}
+
+static
+int
+sconv(va_list *arg, Fconv *fp)
+{
+ char *s;
+ Rune *r;
+
+ if(fp->chr == 's') {
+ s = va_arg(*arg, char*);
+ if(s == 0)
+ s = "<null>";
+ strconv(s, fp);
+ } else {
+ r = va_arg(*arg, Rune*);
+ if(r == 0)
+ r = L"<null>";
+ Strconv(r, fp);
+ }
+ return 0;
+}
+
+static
+int
+percent(va_list*, Fconv *fp)
+{
+
+ if(fp->out < fp->eout)
+ *fp->out++ = '%';
+ printcol++;
+ return 0;
+}
+
+static
+int
+column(va_list *arg, Fconv *fp)
+{
+ int col, pc;
+
+ col = va_arg(*arg, int);
+ while(fp->out < fp->eout && printcol < col) {
+ pc = (printcol+8) & ~7;
+ if(pc <= col) {
+ *fp->out++ = '\t';
+ printcol = pc;
+ } else {
+ *fp->out++ = ' ';
+ printcol++;
+ }
+ }
+ return 0;
+}
+
+static
+int
+flags(va_list*, Fconv *fp)
+{
+ int f;
+
+ f = 0;
+ switch(fp->chr) {
+ case '+':
+ f = FPLUS;
+ break;
+
+ case '-':
+ f = FMINUS;
+ break;
+
+ case '#':
+ f = FSHARP;
+ break;
+
+ case 'h':
+ f = FSHORT;
+ break;
+
+ case 'l':
+ f = FLONG;
+ if(fp->f3 & FLONG)
+ f = FVLONG;
+ break;
+
+ case 'u':
+ f = FUNSIGN;
+ break;
+ }
+ return -f;
+}
diff --git a/sys/src/libc/arm/getcallerpc.s b/sys/src/libc/arm/getcallerpc.s
new file mode 100755
index 000000000..ac4575913
--- /dev/null
+++ b/sys/src/libc/arm/getcallerpc.s
@@ -0,0 +1,3 @@
+TEXT getcallerpc(SB), $-4
+ MOVW 0(R13), R0
+ RET
diff --git a/sys/src/libc/arm/getfcr.s b/sys/src/libc/arm/getfcr.s
new file mode 100755
index 000000000..dc9a207bc
--- /dev/null
+++ b/sys/src/libc/arm/getfcr.s
@@ -0,0 +1,12 @@
+TEXT setfcr(SB), $0
+ RET
+
+TEXT getfcr(SB), $0
+ RET
+
+TEXT getfsr(SB), $0
+ RET
+
+TEXT setfsr(SB), $0
+ RET
+
diff --git a/sys/src/libc/arm/main9.s b/sys/src/libc/arm/main9.s
new file mode 100755
index 000000000..14cd31f2d
--- /dev/null
+++ b/sys/src/libc/arm/main9.s
@@ -0,0 +1,29 @@
+#define NPRIVATES 16
+
+arg=0
+sp=13
+sb=12
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+ MOVW $setR12(SB), R(sb)
+ MOVW R(arg), _tos(SB)
+
+ MOVW $p-64(SP), R1
+ MOVW R1, _privates(SB)
+ MOVW $NPRIVATES, R1
+ MOVW R1, _nprivates(SB)
+
+ MOVW $inargv+0(FP), R(arg)
+ MOVW R(arg), 8(R(sp))
+ MOVW inargc-4(FP), R(arg)
+ MOVW R(arg), 4(R(sp))
+ BL main(SB)
+loop:
+ MOVW $_exitstr<>(SB), R(arg)
+ MOVW R(arg), 4(R(sp))
+ BL exits(SB)
+ BL _div(SB)
+ B loop
+
+DATA _exitstr<>+0(SB)/4, $"main"
+GLOBL _exitstr<>+0(SB), $5
diff --git a/sys/src/libc/arm/main9p.s b/sys/src/libc/arm/main9p.s
new file mode 100755
index 000000000..e77df7162
--- /dev/null
+++ b/sys/src/libc/arm/main9p.s
@@ -0,0 +1,42 @@
+#define NPRIVATES 16
+
+arg=0
+sp=13
+sb=12
+
+TEXT _mainp(SB), 1, $(16 + NPRIVATES*4)
+ MOVW $setR12(SB), R(sb)
+ MOVW R(arg), _tos(SB)
+
+ MOVW $p-64(SP), R1
+ MOVW R1, _privates(SB)
+ MOVW $NPRIVATES, R1
+ MOVW R1, _nprivates(SB)
+
+ BL _profmain(SB)
+ MOVW _tos(SB), R1
+ MOVW 4(R1), R0
+ MOVW R0, 0(R1)
+
+ MOVW $inargv+0(FP), R(arg)
+ MOVW R(arg), 8(R(sp))
+ MOVW inargc-4(FP), R(arg)
+ MOVW R(arg), 4(R(sp))
+ BL main(SB)
+loop:
+ MOVW $_exitstr<>(SB), R(arg)
+ MOVW R(arg), 4(R(sp))
+ BL exits(SB)
+ MOVW $_div(SB), R(arg) /* force loading of div */
+ MOVW $_profin(SB), R(arg) /* force loading of profile */
+ B loop
+
+TEXT _savearg(SB), 1, $0
+ RET
+
+TEXT _callpc(SB), 1, $0
+ MOVW argp-4(FP), R(arg)
+ RET
+
+DATA _exitstr<>+0(SB)/4, $"main"
+GLOBL _exitstr<>+0(SB), $5
diff --git a/sys/src/libc/arm/memmove.s b/sys/src/libc/arm/memmove.s
new file mode 100755
index 000000000..346a23d72
--- /dev/null
+++ b/sys/src/libc/arm/memmove.s
@@ -0,0 +1,212 @@
+TS = 0
+TE = 1
+FROM = 2
+N = 3
+TMP = 3 /* N and TMP don't overlap */
+TMP1 = 4
+
+TEXT memcpy(SB), $-4
+ B _memmove
+TEXT memmove(SB), $-4
+_memmove:
+ MOVW R(TS), to+0(FP) /* need to save for return value */
+ MOVW from+4(FP), R(FROM)
+ MOVW n+8(FP), R(N)
+
+ ADD R(N), R(TS), R(TE) /* to end pointer */
+
+ CMP R(FROM), R(TS)
+ BLS _forward
+
+_back:
+ ADD R(N), R(FROM) /* from end pointer */
+ CMP $4, R(N) /* need at least 4 bytes to copy */
+ BLT _b1tail
+
+_b4align: /* align destination on 4 */
+ AND.S $3, R(TE), R(TMP)
+ BEQ _b4aligned
+
+ MOVBU.W -1(R(FROM)), R(TMP) /* pre-indexed */
+ MOVBU.W R(TMP), -1(R(TE)) /* pre-indexed */
+ B _b4align
+
+_b4aligned: /* is source now aligned? */
+ AND.S $3, R(FROM), R(TMP)
+ BNE _bunaligned
+
+ ADD $31, R(TS), R(TMP) /* do 32-byte chunks if possible */
+_b32loop:
+ CMP R(TMP), R(TE)
+ BLS _b4tail
+
+ MOVM.DB.W (R(FROM)), [R4-R7]
+ MOVM.DB.W [R4-R7], (R(TE))
+ MOVM.DB.W (R(FROM)), [R4-R7]
+ MOVM.DB.W [R4-R7], (R(TE))
+ B _b32loop
+
+_b4tail: /* do remaining words if possible */
+ ADD $3, R(TS), R(TMP)
+_b4loop:
+ CMP R(TMP), R(TE)
+ BLS _b1tail
+
+ MOVW.W -4(R(FROM)), R(TMP1) /* pre-indexed */
+ MOVW.W R(TMP1), -4(R(TE)) /* pre-indexed */
+ B _b4loop
+
+_b1tail: /* remaining bytes */
+ CMP R(TE), R(TS)
+ BEQ _return
+
+ MOVBU.W -1(R(FROM)), R(TMP) /* pre-indexed */
+ MOVBU.W R(TMP), -1(R(TE)) /* pre-indexed */
+ B _b1tail
+
+_forward:
+ CMP $4, R(N) /* need at least 4 bytes to copy */
+ BLT _f1tail
+
+_f4align: /* align destination on 4 */
+ AND.S $3, R(TS), R(TMP)
+ BEQ _f4aligned
+
+ MOVBU.P 1(R(FROM)), R(TMP) /* implicit write back */
+ MOVBU.P R(TMP), 1(R(TS)) /* implicit write back */
+ B _f4align
+
+_f4aligned: /* is source now aligned? */
+ AND.S $3, R(FROM), R(TMP)
+ BNE _funaligned
+
+ SUB $31, R(TE), R(TMP) /* do 32-byte chunks if possible */
+_f32loop:
+ CMP R(TMP), R(TS)
+ BHS _f4tail
+
+ MOVM.IA.W (R(FROM)), [R4-R7]
+ MOVM.IA.W [R4-R7], (R(TS))
+ MOVM.IA.W (R(FROM)), [R4-R7]
+ MOVM.IA.W [R4-R7], (R(TS))
+ B _f32loop
+
+_f4tail:
+ SUB $3, R(TE), R(TMP) /* do remaining words if possible */
+_f4loop:
+ CMP R(TMP), R(TS)
+ BHS _f1tail
+
+ MOVW.P 4(R(FROM)), R(TMP1) /* implicit write back */
+ MOVW.P R4, 4(R(TS)) /* implicit write back */
+ B _f4loop
+
+_f1tail:
+ CMP R(TS), R(TE)
+ BEQ _return
+
+ MOVBU.P 1(R(FROM)), R(TMP) /* implicit write back */
+ MOVBU.P R(TMP), 1(R(TS)) /* implicit write back */
+ B _f1tail
+
+_return:
+ MOVW to+0(FP), R0
+ RET
+
+RSHIFT = 4
+LSHIFT = 5
+OFFSET = 11
+
+BR0 = 6
+BW0 = 7
+BR1 = 7
+BW1 = 8
+
+_bunaligned:
+ CMP $2, R(TMP) /* is R(TMP) < 2 ? */
+
+ MOVW.LT $8, R(RSHIFT) /* (R(n)<<24)|(R(n-1)>>8) */
+ MOVW.LT $24, R(LSHIFT)
+ MOVW.LT $1, R(OFFSET)
+
+ MOVW.EQ $16, R(RSHIFT) /* (R(n)<<16)|(R(n-1)>>16) */
+ MOVW.EQ $16, R(LSHIFT)
+ MOVW.EQ $2, R(OFFSET)
+
+ MOVW.GT $24, R(RSHIFT) /* (R(n)<<8)|(R(n-1)>>24) */
+ MOVW.GT $8, R(LSHIFT)
+ MOVW.GT $3, R(OFFSET)
+
+ ADD $8, R(TS), R(TMP) /* do 8-byte chunks if possible */
+ CMP R(TMP), R(TE)
+ BLS _b1tail
+
+ BIC $3, R(FROM) /* align source */
+ MOVW (R(FROM)), R(BR0) /* prime first block register */
+
+_bu8loop:
+ CMP R(TMP), R(TE)
+ BLS _bu1tail
+
+ MOVW R(BR0)<<R(LSHIFT), R(BW1)
+ MOVM.DB.W (R(FROM)), [R(BR0)-R(BR1)]
+ ORR R(BR1)>>R(RSHIFT), R(BW1)
+
+ MOVW R(BR1)<<R(LSHIFT), R(BW0)
+ ORR R(BR0)>>R(RSHIFT), R(BW0)
+
+ MOVM.DB.W [R(BW0)-R(BW1)], (R(TE))
+ B _bu8loop
+
+_bu1tail:
+ ADD R(OFFSET), R(FROM)
+ B _b1tail
+
+RSHIFT = 4
+LSHIFT = 5
+OFFSET = 11
+
+FW0 = 6
+FR0 = 7
+FW1 = 7
+FR1 = 8
+
+_funaligned:
+ CMP $2, R(TMP)
+
+ MOVW.LT $8, R(RSHIFT) /* (R(n+1)<<24)|(R(n)>>8) */
+ MOVW.LT $24, R(LSHIFT)
+ MOVW.LT $3, R(OFFSET)
+
+ MOVW.EQ $16, R(RSHIFT) /* (R(n+1)<<16)|(R(n)>>16) */
+ MOVW.EQ $16, R(LSHIFT)
+ MOVW.EQ $2, R(OFFSET)
+
+ MOVW.GT $24, R(RSHIFT) /* (R(n+1)<<8)|(R(n)>>24) */
+ MOVW.GT $8, R(LSHIFT)
+ MOVW.GT $1, R(OFFSET)
+
+ SUB $8, R(TE), R(TMP) /* do 8-byte chunks if possible */
+ CMP R(TMP), R(TS)
+ BHS _f1tail
+
+ BIC $3, R(FROM) /* align source */
+ MOVW.P 4(R(FROM)), R(FR1) /* prime last block register, implicit write back */
+
+_fu8loop:
+ CMP R(TMP), R(TS)
+ BHS _fu1tail
+
+ MOVW R(FR1)>>R(RSHIFT), R(FW0)
+ MOVM.IA.W (R(FROM)), [R(FR0)-R(FR1)]
+ ORR R(FR0)<<R(LSHIFT), R(FW0)
+
+ MOVW R(FR0)>>R(RSHIFT), R(FW1)
+ ORR R(FR1)<<R(LSHIFT), R(FW1)
+
+ MOVM.IA.W [R(FW0)-R(FW1)], (R(TS))
+ B _fu8loop
+
+_fu1tail:
+ SUB R(OFFSET), R(FROM)
+ B _f1tail
diff --git a/sys/src/libc/arm/memset.s b/sys/src/libc/arm/memset.s
new file mode 100755
index 000000000..7ebbb44c3
--- /dev/null
+++ b/sys/src/libc/arm/memset.s
@@ -0,0 +1,60 @@
+TO = 1
+TOE = 2
+N = 3
+TMP = 3 /* N and TMP don't overlap */
+
+TEXT memset(SB), $0
+ MOVW R0, R(TO)
+ MOVW data+4(FP), R(4)
+ MOVW n+8(FP), R(N)
+
+ ADD R(N), R(TO), R(TOE) /* to end pointer */
+
+ CMP $4, R(N) /* need at least 4 bytes to copy */
+ BLT _1tail
+
+ AND $0xFF, R(4)
+ ORR R(4)<<8, R(4)
+ ORR R(4)<<16, R(4) /* replicate to word */
+
+_4align: /* align on 4 */
+ AND.S $3, R(TO), R(TMP)
+ BEQ _4aligned
+
+ MOVBU.P R(4), 1(R(TO)) /* implicit write back */
+ B _4align
+
+_4aligned:
+ SUB $15, R(TOE), R(TMP) /* do 16-byte chunks if possible */
+ CMP R(TMP), R(TO)
+ BHS _4tail
+
+ MOVW R4, R5 /* replicate */
+ MOVW R4, R6
+ MOVW R4, R7
+
+_f16loop:
+ CMP R(TMP), R(TO)
+ BHS _4tail
+
+ MOVM.IA.W [R4-R7], (R(TO))
+ B _f16loop
+
+_4tail:
+ SUB $3, R(TOE), R(TMP) /* do remaining words if possible */
+_4loop:
+ CMP R(TMP), R(TO)
+ BHS _1tail
+
+ MOVW.P R(4), 4(R(TO)) /* implicit write back */
+ B _4loop
+
+_1tail:
+ CMP R(TO), R(TOE)
+ BEQ _return
+
+ MOVBU.P R(4), 1(R(TO)) /* implicit write back */
+ B _1tail
+
+_return:
+ RET
diff --git a/sys/src/libc/arm/mkfile b/sys/src/libc/arm/mkfile
new file mode 100755
index 000000000..14f81e1b8
--- /dev/null
+++ b/sys/src/libc/arm/mkfile
@@ -0,0 +1,36 @@
+objtype=arm
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ atom.s\
+ div.s\
+ getcallerpc.s\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memmove.s\
+ memset.s\
+ setjmp.s\
+ strchr.s\
+ strcmp.s\
+ strcpy.s\
+ tas.s\
+ vlop.s\
+
+CFILES=\
+ cycles.c\
+ notejmp.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/arm/notejmp.c b/sys/src/libc/arm/notejmp.c
new file mode 100755
index 000000000..a8a555022
--- /dev/null
+++ b/sys/src/libc/arm/notejmp.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ r->r0 = ret;
+ if(ret == 0)
+ r->r0 = 1;
+ r->pc = j[JMPBUFPC];
+ r->r13 = j[JMPBUFSP];
+ noted(NCONT);
+}
diff --git a/sys/src/libc/arm/setjmp.s b/sys/src/libc/arm/setjmp.s
new file mode 100755
index 000000000..64bdc2196
--- /dev/null
+++ b/sys/src/libc/arm/setjmp.s
@@ -0,0 +1,19 @@
+arg=0
+link=14
+sp=13
+
+TEXT setjmp(SB), 1, $-4
+ MOVW R(sp), (R(arg+0))
+ MOVW R(link), 4(R(arg+0))
+ MOVW $0, R0
+ RET
+
+TEXT longjmp(SB), 1, $-4
+ MOVW r+4(FP), R(arg+2)
+ CMP $0, R(arg+2)
+ BNE ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVW $1, R(arg+2) /* bless their pointed heads */
+ok: MOVW (R(arg+0)), R(sp)
+ MOVW 4(R(arg+0)), R(link)
+ MOVW R(arg+2), R(arg+0)
+ RET
diff --git a/sys/src/libc/arm/strchr.s b/sys/src/libc/arm/strchr.s
new file mode 100755
index 000000000..349b5a49f
--- /dev/null
+++ b/sys/src/libc/arm/strchr.s
@@ -0,0 +1,56 @@
+TEXT strchr(SB), $-4
+ MOVBU c+4(FP), R1
+ CMP $0, R1
+ BEQ _null
+
+_strchr: /* not looking for a null, byte at a time */
+ MOVBU.P 1(R0), R2
+ CMP R1, R2
+ BEQ _sub1
+
+ CMP $0, R2
+ BNE _strchr
+
+_return0: /* character not found in string, return 0 */
+ MOVW $0, R0
+ RET
+
+_null: /* looking for null, align */
+ AND.S $3, R0, R2
+ BEQ _aligned
+
+ MOVBU.P 1(R0), R4
+ CMP $0, R4
+ BEQ _sub1
+ B _null
+
+_aligned:
+ MOVW $0xFF, R3 /* mask */
+
+_loop:
+ MOVW.P 4(R0), R4 /* 4 at a time */
+ TST R4, R3 /* AND.S R2, R3, Rx */
+ TST.NE R4>>8, R3
+ TST.NE R4>>16, R3
+ TST.NE R4>>24, R3
+ BNE _loop
+
+ TST R4, R3 /* its somewhere, find it and correct */
+ BEQ _sub4
+ TST R4>>8, R3
+ BEQ _sub3
+ TST R4>>16, R3
+ BEQ _sub2
+
+_sub1: /* compensate for pointer increment */
+ SUB $1, R0
+ RET
+_sub2:
+ SUB $2, R0
+ RET
+_sub3:
+ SUB $3, R0
+ RET
+_sub4:
+ SUB $4, R0
+ RET
diff --git a/sys/src/libc/arm/strcmp.s b/sys/src/libc/arm/strcmp.s
new file mode 100755
index 000000000..015e51596
--- /dev/null
+++ b/sys/src/libc/arm/strcmp.s
@@ -0,0 +1,67 @@
+TEXT strcmp(SB), $-4
+ MOVW R0, R1
+ MOVW s2+4(FP), R2
+
+ MOVW $0xFF, R3 /* mask */
+
+_align: /* align s1 on 4 */
+ TST $3, R1
+ BEQ _aligned
+
+ MOVBU.P 1(R1), R4 /* implicit write back */
+ MOVBU.P 1(R2), R8 /* implicit write back */
+ SUB.S R8, R4, R0
+ BNE _return
+ CMP $0, R4
+ BEQ _return
+ B _align
+
+_aligned: /* is s2 now aligned? */
+ TST $3, R2
+ BNE _unaligned
+
+_aloop:
+ MOVW.P 4(R1), R5 /* 4 at a time */
+ MOVW.P 4(R2), R7
+
+ AND R5, R3, R4
+ AND R7, R3, R8
+ SUB.S R8, R4, R0
+ BNE _return
+ CMP $0, R4
+ BEQ _return
+
+ AND R5>>8, R3, R4
+ AND R7>>8, R3, R8
+ SUB.S R8, R4, R0
+ BNE _return
+ CMP $0, R4
+ BEQ _return
+
+ AND R5>>16, R3, R4
+ AND R7>>16, R3, R8
+ SUB.S R8, R4, R0
+ BNE _return
+ CMP $0, R4
+ BEQ _return
+
+ AND R5>>24, R3, R4
+ AND R7>>24, R3, R8
+ SUB.S R8, R4, R0
+ BNE _return
+ CMP $0, R4
+ BEQ _return
+
+ B _aloop
+
+_return:
+ RET
+
+_unaligned:
+ MOVBU.P 1(R1), R4 /* implicit write back */
+ MOVBU.P 1(R2), R8 /* implicit write back */
+ SUB.S R8, R4, R0
+ BNE _return
+ CMP $0, R4
+ BEQ _return
+ B _unaligned
diff --git a/sys/src/libc/arm/strcpy.s b/sys/src/libc/arm/strcpy.s
new file mode 100755
index 000000000..3e69fdc7d
--- /dev/null
+++ b/sys/src/libc/arm/strcpy.s
@@ -0,0 +1,46 @@
+TEXT strcpy(SB), $-4
+ MOVW R0, to+0(FP) /* need to save for return value */
+ MOVW from+4(FP), R1
+ MOVW $0xFF, R2 /* mask */
+
+salign: /* align source on 4 */
+ AND.S $3, R1, R3
+ BEQ dalign
+ MOVBU.P 1(R1), R3 /* implicit write back */
+ TST R3, R2
+ MOVBU.P R3, 1(R0) /* implicit write back */
+ BNE salign
+ B return
+
+dalign: /* is destination now aligned? */
+ AND.S $3, R0, R3
+ BNE uloop
+
+aloop:
+ MOVW.P 4(R1), R4 /* read 4, write 4 */
+ TST R4, R2 /* AND.S R3, R2, Rx */
+ TST.NE R4>>8, R2
+ TST.NE R4>>16, R2
+ TST.NE R4>>24, R2
+ BEQ tail
+ MOVW.P R4, 4(R0)
+ B aloop
+
+uloop:
+ MOVW.P 4(R1), R4 /* read 4, write 1,1,1,1 */
+
+tail:
+ AND.S R4, R2, R3
+ MOVBU.NE.P R3, 1(R0)
+ AND.NE.S R4>>8, R2, R3
+ MOVBU.NE.P R3, 1(R0)
+ AND.NE.S R4>>16, R2, R3
+ MOVBU.NE.P R3, 1(R0)
+ AND.NE.S R4>>24, R2, R3
+ MOVBU.P R3, 1(R0)
+ BNE uloop
+ B return
+
+return:
+ MOVW to+0(FP), R0
+ RET
diff --git a/sys/src/libc/arm/tas.s b/sys/src/libc/arm/tas.s
new file mode 100755
index 000000000..15febbd2c
--- /dev/null
+++ b/sys/src/libc/arm/tas.s
@@ -0,0 +1,5 @@
+TEXT _tas(SB), $-4
+ MOVW R0,R1
+ MOVW $1,R0
+ SWPW R0,(R1) /* fix: deprecated in armv7 */
+ RET
diff --git a/sys/src/libc/arm/vlop.s b/sys/src/libc/arm/vlop.s
new file mode 100755
index 000000000..3a5375541
--- /dev/null
+++ b/sys/src/libc/arm/vlop.s
@@ -0,0 +1,13 @@
+TEXT _mulv(SB), $0
+ MOVW 4(FP),R8 /* l0 */
+ MOVW 8(FP),R11 /* h0 */
+ MOVW 12(FP),R4 /* l1 */
+ MOVW 16(FP),R5 /* h1 */
+ MULLU R8,R4,(R6, R7) /* l0*l1 */
+ MUL R8,R5,R5 /* l0*h1 */
+ MUL R11,R4,R4 /* h0*l1 */
+ ADD R4,R6
+ ADD R5,R6
+ MOVW R6,4(R0)
+ MOVW R7,0(R0)
+ RET
diff --git a/sys/src/libc/arm/vlrt.c b/sys/src/libc/arm/vlrt.c
new file mode 100755
index 000000000..5e9524d34
--- /dev/null
+++ b/sys/src/libc/arm/vlrt.c
@@ -0,0 +1,708 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ ulong lo;
+ ulong hi;
+};
+
+void abort(void);
+
+/* needed by profiler; can't be profiled */
+#pragma profile off
+
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+#pragma profile on
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u = *ret;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
diff --git a/sys/src/libc/fmt/dofmt.c b/sys/src/libc/fmt/dofmt.c
new file mode 100755
index 000000000..2a4b42959
--- /dev/null
+++ b/sys/src/libc/fmt/dofmt.c
@@ -0,0 +1,523 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted */
+int
+dofmt(Fmt *f, char *fmt)
+{
+ Rune rune, *rt, *rs;
+ int r;
+ char *t, *s;
+ int n, nfmt;
+
+ nfmt = f->nfmt;
+ for(;;){
+ if(f->runes){
+ rt = f->to;
+ rs = f->stop;
+ while((r = *(uchar*)fmt) && r != '%'){
+ if(r < Runeself)
+ fmt++;
+ else{
+ fmt += chartorune(&rune, fmt);
+ r = rune;
+ }
+ FMTRCHAR(f, rt, rs, r);
+ }
+ fmt++;
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ }else{
+ t = f->to;
+ s = f->stop;
+ while((r = *(uchar*)fmt) && r != '%'){
+ if(r < Runeself){
+ FMTCHAR(f, t, s, r);
+ fmt++;
+ }else{
+ n = chartorune(&rune, fmt);
+ if(t + n > s){
+ t = _fmtflush(f, t, n);
+ if(t != nil)
+ s = f->stop;
+ else
+ return -1;
+ }
+ while(n--)
+ *t++ = *fmt++;
+ }
+ }
+ fmt++;
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = _fmtdispatch(f, fmt, 0);
+ if(fmt == nil)
+ return -1;
+ }
+}
+
+void *
+_fmtflush(Fmt *f, void *t, int len)
+{
+ if(f->runes)
+ f->nfmt += (Rune*)t - (Rune*)f->to;
+ else
+ f->nfmt += (char*)t - (char *)f->to;
+ f->to = t;
+ if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
+ f->stop = f->to;
+ return nil;
+ }
+ return f->to;
+}
+
+/*
+ * put a formatted block of memory sz bytes long of n runes into the output buffer,
+ * left/right justified in a field of at least f->width charactes
+ */
+int
+_fmtpad(Fmt *f, int n)
+{
+ char *t, *s;
+ int i;
+
+ t = f->to;
+ s = f->stop;
+ for(i = 0; i < n; i++)
+ FMTCHAR(f, t, s, ' ');
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ return 0;
+}
+
+int
+_rfmtpad(Fmt *f, int n)
+{
+ Rune *t, *s;
+ int i;
+
+ t = f->to;
+ s = f->stop;
+ for(i = 0; i < n; i++)
+ FMTRCHAR(f, t, s, ' ');
+ f->nfmt += t - (Rune *)f->to;
+ f->to = t;
+ return 0;
+}
+
+int
+_fmtcpy(Fmt *f, void *vm, int n, int sz)
+{
+ Rune *rt, *rs, r;
+ char *t, *s, *m, *me;
+ ulong fl;
+ int nc, w;
+
+ m = vm;
+ me = m + sz;
+ w = f->width;
+ fl = f->flags;
+ if((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if(f->runes){
+ if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = f->to;
+ rs = f->stop;
+ for(nc = n; nc > 0; nc--){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
+ return -1;
+ t = f->to;
+ s = f->stop;
+ for(nc = n; nc > 0; nc--){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+_fmtrcpy(Fmt *f, void *vm, int n)
+{
+ Rune r, *m, *me, *rt, *rs;
+ char *t, *s;
+ ulong fl;
+ int w;
+
+ m = vm;
+ w = f->width;
+ fl = f->flags;
+ if((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if(f->runes){
+ if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = f->to;
+ rs = f->stop;
+ for(me = m + n; m < me; m++)
+ FMTRCHAR(f, rt, rs, *m);
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
+ return -1;
+ t = f->to;
+ s = f->stop;
+ for(me = m + n; m < me; m++){
+ r = *m;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/* fmt out one character */
+int
+_charfmt(Fmt *f)
+{
+ char x[1];
+
+ x[0] = va_arg(f->args, int);
+ f->prec = 1;
+ return _fmtcpy(f, x, 1, 1);
+}
+
+/* fmt out one rune */
+int
+_runefmt(Fmt *f)
+{
+ Rune x[1];
+
+ x[0] = va_arg(f->args, int);
+ return _fmtrcpy(f, x, 1);
+}
+
+/* public helper routine: fmt out a null terminated string already in hand */
+int
+fmtstrcpy(Fmt *f, char *s)
+{
+ int i, j;
+ Rune r;
+
+ if(!s)
+ return _fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if(f->flags & FmtPrec){
+ i = 0;
+ for(j=0; j<f->prec && s[i]; j++)
+ i += chartorune(&r, s+i);
+ return _fmtcpy(f, s, j, i);
+ }
+ return _fmtcpy(f, s, utflen(s), strlen(s));
+}
+
+/* fmt out a null terminated utf string */
+int
+_strfmt(Fmt *f)
+{
+ char *s;
+
+ s = va_arg(f->args, char *);
+ return fmtstrcpy(f, s);
+}
+
+/* public helper routine: fmt out a null terminated rune string already in hand */
+int
+fmtrunestrcpy(Fmt *f, Rune *s)
+{
+ Rune *e;
+ int n, p;
+
+ if(!s)
+ return _fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if(f->flags & FmtPrec){
+ p = f->prec;
+ for(n = 0; n < p; n++)
+ if(s[n] == 0)
+ break;
+ }else{
+ for(e = s; *e; e++)
+ ;
+ n = e - s;
+ }
+ return _fmtrcpy(f, s, n);
+}
+
+/* fmt out a null terminated rune string */
+int
+_runesfmt(Fmt *f)
+{
+ Rune *s;
+
+ s = va_arg(f->args, Rune *);
+ return fmtrunestrcpy(f, s);
+}
+
+/* fmt a % */
+int
+_percentfmt(Fmt *f)
+{
+ Rune x[1];
+
+ x[0] = f->r;
+ f->prec = 1;
+ return _fmtrcpy(f, x, 1);
+}
+
+/* fmt an integer */
+int
+_ifmt(Fmt *f)
+{
+ char buf[70], *p, *conv;
+ uvlong vu;
+ ulong u;
+ uintptr pu;
+ int neg, base, i, n, fl, w, isv;
+
+ neg = 0;
+ fl = f->flags;
+ isv = 0;
+ vu = 0;
+ u = 0;
+ if(f->r == 'p'){
+ pu = va_arg(f->args, uintptr);
+ if(sizeof(uintptr) == sizeof(uvlong)){
+ vu = pu;
+ isv = 1;
+ }else
+ u = pu;
+ f->r = 'x';
+ fl |= FmtUnsigned;
+ }else if(fl & FmtVLong){
+ isv = 1;
+ if(fl & FmtUnsigned)
+ vu = va_arg(f->args, uvlong);
+ else
+ vu = va_arg(f->args, vlong);
+ }else if(fl & FmtLong){
+ if(fl & FmtUnsigned)
+ u = va_arg(f->args, ulong);
+ else
+ u = va_arg(f->args, long);
+ }else if(fl & FmtByte){
+ if(fl & FmtUnsigned)
+ u = (uchar)va_arg(f->args, int);
+ else
+ u = (char)va_arg(f->args, int);
+ }else if(fl & FmtShort){
+ if(fl & FmtUnsigned)
+ u = (ushort)va_arg(f->args, int);
+ else
+ u = (short)va_arg(f->args, int);
+ }else{
+ if(fl & FmtUnsigned)
+ u = va_arg(f->args, uint);
+ else
+ u = va_arg(f->args, int);
+ }
+ conv = "0123456789abcdef";
+ switch(f->r){
+ case 'd':
+ base = 10;
+ break;
+ case 'x':
+ base = 16;
+ break;
+ case 'X':
+ base = 16;
+ conv = "0123456789ABCDEF";
+ break;
+ case 'b':
+ base = 2;
+ break;
+ case 'o':
+ base = 8;
+ break;
+ default:
+ return -1;
+ }
+ if(!(fl & FmtUnsigned)){
+ if(isv && (vlong)vu < 0){
+ vu = -(vlong)vu;
+ neg = 1;
+ }else if(!isv && (long)u < 0){
+ u = -(long)u;
+ neg = 1;
+ }
+ }
+ p = buf + sizeof buf - 1;
+ n = 0;
+ if(isv){
+ while(vu){
+ i = vu % base;
+ vu /= base;
+ if((fl & FmtComma) && n % 4 == 3){
+ *p-- = ',';
+ n++;
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }else{
+ while(u){
+ i = u % base;
+ u /= base;
+ if((fl & FmtComma) && n % 4 == 3){
+ *p-- = ',';
+ n++;
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }
+ if(n == 0){
+ *p-- = '0';
+ n = 1;
+ }
+ for(w = f->prec; n < w && p > buf+3; n++)
+ *p-- = '0';
+ if(neg || (fl & (FmtSign|FmtSpace)))
+ n++;
+ if(fl & FmtSharp){
+ if(base == 16)
+ n += 2;
+ else if(base == 8){
+ if(p[1] == '0')
+ fl &= ~FmtSharp;
+ else
+ n++;
+ }
+ }
+ if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
+ for(w = f->width; n < w && p > buf+3; n++)
+ *p-- = '0';
+ f->width = 0;
+ }
+ if(fl & FmtSharp){
+ if(base == 16)
+ *p-- = f->r;
+ if(base == 16 || base == 8)
+ *p-- = '0';
+ }
+ if(neg)
+ *p-- = '-';
+ else if(fl & FmtSign)
+ *p-- = '+';
+ else if(fl & FmtSpace)
+ *p-- = ' ';
+ f->flags &= ~FmtPrec;
+ return _fmtcpy(f, p + 1, n, n);
+}
+
+int
+_countfmt(Fmt *f)
+{
+ void *p;
+ ulong fl;
+
+ fl = f->flags;
+ p = va_arg(f->args, void*);
+ if(fl & FmtVLong){
+ *(vlong*)p = f->nfmt;
+ }else if(fl & FmtLong){
+ *(long*)p = f->nfmt;
+ }else if(fl & FmtByte){
+ *(char*)p = f->nfmt;
+ }else if(fl & FmtShort){
+ *(short*)p = f->nfmt;
+ }else{
+ *(int*)p = f->nfmt;
+ }
+ return 0;
+}
+
+int
+_flagfmt(Fmt *f)
+{
+ switch(f->r){
+ case ',':
+ f->flags |= FmtComma;
+ break;
+ case '-':
+ f->flags |= FmtLeft;
+ break;
+ case '+':
+ f->flags |= FmtSign;
+ break;
+ case '#':
+ f->flags |= FmtSharp;
+ break;
+ case ' ':
+ f->flags |= FmtSpace;
+ break;
+ case 'u':
+ f->flags |= FmtUnsigned;
+ break;
+ case 'h':
+ if(f->flags & FmtShort)
+ f->flags |= FmtByte;
+ f->flags |= FmtShort;
+ break;
+ case 'l':
+ if(f->flags & FmtLong)
+ f->flags |= FmtVLong;
+ f->flags |= FmtLong;
+ break;
+ }
+ return 1;
+}
+
+/* default error format */
+int
+_badfmt(Fmt *f)
+{
+ char x[3];
+
+ x[0] = '%';
+ x[1] = f->r;
+ x[2] = '%';
+ f->prec = 3;
+ _fmtcpy(f, x, 3, 3);
+ return 0;
+}
diff --git a/sys/src/libc/fmt/dorfmt.c b/sys/src/libc/fmt/dorfmt.c
new file mode 100755
index 000000000..6e60073d9
--- /dev/null
+++ b/sys/src/libc/fmt/dorfmt.c
@@ -0,0 +1,45 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted */
+
+int
+dorfmt(Fmt *f, Rune *fmt)
+{
+ Rune *rt, *rs;
+ int r;
+ char *t, *s;
+ int nfmt;
+
+ nfmt = f->nfmt;
+ for(;;){
+ if(f->runes){
+ rt = f->to;
+ rs = f->stop;
+ while((r = *fmt++) && r != '%'){
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ }else{
+ t = f->to;
+ s = f->stop;
+ while((r = *fmt++) && r != '%'){
+ FMTRUNE(f, t, f->stop, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = _fmtdispatch(f, fmt, 1);
+ if(fmt == nil)
+ return -1;
+ }
+}
diff --git a/sys/src/libc/fmt/errfmt.c b/sys/src/libc/fmt/errfmt.c
new file mode 100755
index 000000000..5b29b1671
--- /dev/null
+++ b/sys/src/libc/fmt/errfmt.c
@@ -0,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+errfmt(Fmt *f)
+{
+ char buf[ERRMAX];
+
+ rerrstr(buf, sizeof buf);
+ return _fmtcpy(f, buf, utflen(buf), strlen(buf));
+}
diff --git a/sys/src/libc/fmt/fltfmt.c b/sys/src/libc/fmt/fltfmt.c
new file mode 100755
index 000000000..ff5462910
--- /dev/null
+++ b/sys/src/libc/fmt/fltfmt.c
@@ -0,0 +1,318 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include "fmtdef.h"
+
+enum
+{
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NSIGNIF = 17,
+ NEXP10 = 308,
+};
+
+static int
+xadd(char *a, int n, int v)
+{
+ char *b;
+ int c;
+
+ if(n < 0 || n >= NSIGNIF)
+ return 0;
+ for(b = a+n; b >= a; b--) {
+ c = *b + v;
+ if(c <= '9') {
+ *b = c;
+ return 0;
+ }
+ *b = '0';
+ v = 1;
+ }
+ *a = '1'; // overflow adding
+ return 1;
+}
+
+static int
+xsub(char *a, int n, int v)
+{
+ char *b;
+ int c;
+
+ for(b = a+n; b >= a; b--) {
+ c = *b - v;
+ if(c >= '0') {
+ *b = c;
+ return 0;
+ }
+ *b = '9';
+ v = 1;
+ }
+ *a = '9'; // underflow subtracting
+ return 1;
+}
+
+static void
+xdtoa(Fmt *fmt, char *s2, double f)
+{
+ char s1[NSIGNIF+10];
+ double g, h;
+ int e, d, i, n;
+ int c1, c2, c3, c4, ucase, sign, chr, prec;
+
+ prec = FDEFLT;
+ if(fmt->flags & FmtPrec)
+ prec = fmt->prec;
+ if(prec > FDIGIT)
+ prec = FDIGIT;
+ if(isNaN(f)) {
+ strcpy(s2, "NaN");
+ return;
+ }
+ if(isInf(f, 1)) {
+ strcpy(s2, "+Inf");
+ return;
+ }
+ if(isInf(f, -1)) {
+ strcpy(s2, "-Inf");
+ return;
+ }
+ sign = 0;
+ if(f < 0) {
+ f = -f;
+ sign++;
+ }
+ ucase = 0;
+ chr = fmt->r;
+ if(isupper(chr)) {
+ ucase = 1;
+ chr = tolower(chr);
+ }
+
+ e = 0;
+ g = f;
+ if(g != 0) {
+ frexp(f, &e);
+ e = e * .301029995664;
+ if(e >= -150 && e <= +150) {
+ d = 0;
+ h = f;
+ } else {
+ d = e/2;
+ h = f * pow10(-d);
+ }
+ g = h * pow10(d-e);
+ while(g < 1) {
+ e--;
+ g = h * pow10(d-e);
+ }
+ while(g >= 10) {
+ e++;
+ g = h * pow10(d-e);
+ }
+ }
+
+ /*
+ * convert NSIGNIF digits and convert
+ * back to get accuracy.
+ */
+ for(i=0; i<NSIGNIF; i++) {
+ d = g;
+ s1[i] = d + '0';
+ g = (g - d) * 10;
+ }
+ s1[i] = 0;
+
+ /*
+ * try decimal rounding to eliminate 9s
+ */
+ c2 = prec + 1;
+ if(chr == 'f')
+ c2 += e;
+ if(c2 >= NSIGNIF-2) {
+ strcpy(s2, s1);
+ d = e;
+ s1[NSIGNIF-2] = '0';
+ s1[NSIGNIF-1] = '0';
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ g = strtod(s1, nil);
+ if(g == f)
+ goto found;
+ if(xadd(s1, NSIGNIF-3, 1)) {
+ e++;
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ }
+ g = strtod(s1, nil);
+ if(g == f)
+ goto found;
+ strcpy(s1, s2);
+ e = d;
+ }
+
+ /*
+ * convert back so s1 gets exact answer
+ */
+ for(;;) {
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ g = strtod(s1, nil);
+ if(f > g) {
+ if(xadd(s1, NSIGNIF-1, 1))
+ e--;
+ continue;
+ }
+ if(f < g) {
+ if(xsub(s1, NSIGNIF-1, 1))
+ e++;
+ continue;
+ }
+ break;
+ }
+
+found:
+ /*
+ * sign
+ */
+ d = 0;
+ i = 0;
+ if(sign)
+ s2[d++] = '-';
+ else if(fmt->flags & FmtSign)
+ s2[d++] = '+';
+ else if(fmt->flags & FmtSpace)
+ s2[d++] = ' ';
+
+ /*
+ * copy into final place
+ * c1 digits of leading '0'
+ * c2 digits from conversion
+ * c3 digits of trailing '0'
+ * c4 digits after '.'
+ */
+ c1 = 0;
+ c2 = prec + 1;
+ c3 = 0;
+ c4 = prec;
+ switch(chr) {
+ default:
+ if(xadd(s1, c2, 5))
+ e++;
+ break;
+ case 'g':
+ /*
+ * decide on 'e' of 'f' style convers
+ */
+ if(xadd(s1, c2, 5))
+ e++;
+ if(e >= -5 && e <= prec) {
+ c1 = -e - 1;
+ c4 = prec - e;
+ chr = 'h'; // flag for 'f' style
+ }
+ break;
+ case 'f':
+ if(xadd(s1, c2+e, 5))
+ e++;
+ c1 = -e;
+ if(c1 > prec)
+ c1 = c2;
+ c2 += e;
+ break;
+ }
+
+ /*
+ * clean up c1 c2 and c3
+ */
+ if(c1 < 0)
+ c1 = 0;
+ if(c2 < 0)
+ c2 = 0;
+ if(c2 > NSIGNIF) {
+ c3 = c2-NSIGNIF;
+ c2 = NSIGNIF;
+ }
+
+ /*
+ * copy digits
+ */
+ while(c1 > 0) {
+ if(c1+c2+c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = '0';
+ c1--;
+ }
+ while(c2 > 0) {
+ if(c2+c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = s1[i++];
+ c2--;
+ }
+ while(c3 > 0) {
+ if(c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = '0';
+ c3--;
+ }
+
+ /*
+ * strip trailing '0' on g conv
+ */
+ if(fmt->flags & FmtSharp) {
+ if(0 == c4)
+ s2[d++] = '.';
+ } else
+ if(chr == 'g' || chr == 'h') {
+ for(n=d-1; n>=0; n--)
+ if(s2[n] != '0')
+ break;
+ for(i=n; i>=0; i--)
+ if(s2[i] == '.') {
+ d = n;
+ if(i != n)
+ d++;
+ break;
+ }
+ }
+ if(chr == 'e' || chr == 'g') {
+ if(ucase)
+ s2[d++] = 'E';
+ else
+ s2[d++] = 'e';
+ c1 = e;
+ if(c1 < 0) {
+ s2[d++] = '-';
+ c1 = -c1;
+ } else
+ s2[d++] = '+';
+ if(c1 >= 100) {
+ s2[d++] = c1/100 + '0';
+ c1 = c1%100;
+ }
+ s2[d++] = c1/10 + '0';
+ s2[d++] = c1%10 + '0';
+ }
+ s2[d] = 0;
+}
+
+int
+_floatfmt(Fmt *fmt, double f)
+{
+ char s[1+NEXP10+1+FDIGIT+1];
+
+ /*
+ * The max length of a %f string is
+ * '[+-]'+"max exponent"+'.'+"max precision"+'\0'
+ * which is 341 currently.
+ */
+ xdtoa(fmt, s, f);
+ fmt->flags &= FmtWidth|FmtLeft;
+ _fmtcpy(fmt, s, strlen(s), strlen(s));
+ return 0;
+}
+
+int
+_efgfmt(Fmt *f)
+{
+ double d;
+
+ d = va_arg(f->args, double);
+ return _floatfmt(f, d);
+}
diff --git a/sys/src/libc/fmt/fmt.c b/sys/src/libc/fmt/fmt.c
new file mode 100755
index 000000000..82ae36543
--- /dev/null
+++ b/sys/src/libc/fmt/fmt.c
@@ -0,0 +1,202 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+enum
+{
+ Maxfmt = 64
+};
+
+typedef struct Convfmt Convfmt;
+struct Convfmt
+{
+ int c;
+ volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
+};
+
+struct
+{
+ /* lock by calling _fmtlock, _fmtunlock */
+ int nfmt;
+ Convfmt fmt[Maxfmt];
+} fmtalloc;
+
+static Convfmt knownfmt[] = {
+ ' ', _flagfmt,
+ '#', _flagfmt,
+ '%', _percentfmt,
+ '+', _flagfmt,
+ ',', _flagfmt,
+ '-', _flagfmt,
+ 'C', _runefmt,
+ 'E', _efgfmt,
+ 'G', _efgfmt,
+ 'S', _runesfmt,
+ 'X', _ifmt,
+ 'b', _ifmt,
+ 'c', _charfmt,
+ 'd', _ifmt,
+ 'e', _efgfmt,
+ 'f', _efgfmt,
+ 'g', _efgfmt,
+ 'h', _flagfmt,
+ 'l', _flagfmt,
+ 'n', _countfmt,
+ 'o', _ifmt,
+ 'p', _ifmt,
+ 'r', errfmt,
+ 's', _strfmt,
+ 'u', _flagfmt,
+ 'x', _ifmt,
+ 0, nil,
+};
+
+int (*doquote)(int);
+
+/*
+ * _fmtlock() must be set
+ */
+static int
+_fmtinstall(int c, Fmts f)
+{
+ Convfmt *p, *ep;
+
+ if(c<=0 || c>=65536)
+ return -1;
+ if(!f)
+ f = _badfmt;
+
+ ep = &fmtalloc.fmt[fmtalloc.nfmt];
+ for(p=fmtalloc.fmt; p<ep; p++)
+ if(p->c == c)
+ break;
+
+ if(p == &fmtalloc.fmt[Maxfmt])
+ return -1;
+
+ p->fmt = f;
+ if(p == ep){ /* installing a new format character */
+ fmtalloc.nfmt++;
+ p->c = c;
+ }
+
+ return 0;
+}
+
+int
+fmtinstall(int c, Fmts f)
+{
+ int ret;
+
+ _fmtlock();
+ ret = _fmtinstall(c, f);
+ _fmtunlock();
+ return ret;
+}
+
+static Fmts
+fmtfmt(int c)
+{
+ Convfmt *p, *ep;
+
+ ep = &fmtalloc.fmt[fmtalloc.nfmt];
+ for(p=fmtalloc.fmt; p<ep; p++)
+ if(p->c == c){
+ while(p->fmt == nil) /* loop until value is updated */
+ ;
+ return p->fmt;
+ }
+
+ /* is this a predefined format char? */
+ _fmtlock();
+ for(p=knownfmt; p->c; p++)
+ if(p->c == c){
+ _fmtinstall(p->c, p->fmt);
+ _fmtunlock();
+ return p->fmt;
+ }
+ _fmtunlock();
+
+ return _badfmt;
+}
+
+void*
+_fmtdispatch(Fmt *f, void *fmt, int isrunes)
+{
+ Rune rune, r;
+ int i, n;
+
+ f->flags = 0;
+ f->width = f->prec = 0;
+
+ for(;;){
+ if(isrunes){
+ r = *(Rune*)fmt;
+ fmt = (Rune*)fmt + 1;
+ }else{
+ fmt = (char*)fmt + chartorune(&rune, fmt);
+ r = rune;
+ }
+ f->r = r;
+ switch(r){
+ case '\0':
+ return nil;
+ case '.':
+ f->flags |= FmtWidth|FmtPrec;
+ continue;
+ case '0':
+ if(!(f->flags & FmtWidth)){
+ f->flags |= FmtZero;
+ continue;
+ }
+ /* fall through */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ i = 0;
+ while(r >= '0' && r <= '9'){
+ i = i * 10 + r - '0';
+ if(isrunes){
+ r = *(Rune*)fmt;
+ fmt = (Rune*)fmt + 1;
+ }else{
+ r = *(char*)fmt;
+ fmt = (char*)fmt + 1;
+ }
+ }
+ if(isrunes)
+ fmt = (Rune*)fmt - 1;
+ else
+ fmt = (char*)fmt - 1;
+ numflag:
+ if(f->flags & FmtWidth){
+ f->flags |= FmtPrec;
+ f->prec = i;
+ }else{
+ f->flags |= FmtWidth;
+ f->width = i;
+ }
+ continue;
+ case '*':
+ i = va_arg(f->args, int);
+ if(i < 0){
+ /*
+ * negative precision =>
+ * ignore the precision.
+ */
+ if(f->flags & FmtPrec){
+ f->flags &= ~FmtPrec;
+ f->prec = 0;
+ continue;
+ }
+ i = -i;
+ f->flags |= FmtLeft;
+ }
+ goto numflag;
+ }
+ n = (*fmtfmt(r))(f);
+ if(n < 0)
+ return nil;
+ if(n == 0)
+ return fmt;
+ }
+}
diff --git a/sys/src/libc/fmt/fmtdef.h b/sys/src/libc/fmt/fmtdef.h
new file mode 100755
index 000000000..d8f3680fb
--- /dev/null
+++ b/sys/src/libc/fmt/fmtdef.h
@@ -0,0 +1,85 @@
+/*
+ * dofmt -- format to a buffer
+ * the number of characters formatted is returned,
+ * or -1 if there was an error.
+ * if the buffer is ever filled, flush is called.
+ * it should reset the buffer and return whether formatting should continue.
+ */
+
+typedef int (*Fmts)(Fmt*);
+
+typedef struct Quoteinfo Quoteinfo;
+struct Quoteinfo
+{
+ int quoted; /* if set, string must be quoted */
+ int nrunesin; /* number of input runes that can be accepted */
+ int nbytesin; /* number of input bytes that can be accepted */
+ int nrunesout; /* number of runes that will be generated */
+ int nbytesout; /* number of bytes that will be generated */
+};
+
+void *_fmtflush(Fmt*, void*, int);
+void *_fmtdispatch(Fmt*, void*, int);
+int _floatfmt(Fmt*, double);
+int _fmtpad(Fmt*, int);
+int _rfmtpad(Fmt*, int);
+int _fmtFdFlush(Fmt*);
+
+int _efgfmt(Fmt*);
+int _charfmt(Fmt*);
+int _countfmt(Fmt*);
+int _flagfmt(Fmt*);
+int _percentfmt(Fmt*);
+int _ifmt(Fmt*);
+int _runefmt(Fmt*);
+int _runesfmt(Fmt*);
+int _strfmt(Fmt*);
+int _badfmt(Fmt*);
+int _fmtcpy(Fmt*, void*, int, int);
+int _fmtrcpy(Fmt*, void*, int n);
+
+void _fmtlock(void);
+void _fmtunlock(void);
+
+#define FMTCHAR(f, t, s, c)\
+ do{\
+ if(t + 1 > (char*)s){\
+ t = _fmtflush(f, t, 1);\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ *t++ = c;\
+ }while(0)
+
+#define FMTRCHAR(f, t, s, c)\
+ do{\
+ if(t + 1 > (Rune*)s){\
+ t = _fmtflush(f, t, sizeof(Rune));\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ *t++ = c;\
+ }while(0)
+
+#define FMTRUNE(f, t, s, r)\
+ do{\
+ Rune _rune;\
+ int _runelen;\
+ if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\
+ t = _fmtflush(f, t, _runelen);\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ if(r < Runeself)\
+ *t++ = r;\
+ else{\
+ _rune = r;\
+ t += runetochar(t, &_rune);\
+ }\
+ }while(0)
diff --git a/sys/src/libc/fmt/fmtfd.c b/sys/src/libc/fmt/fmtfd.c
new file mode 100755
index 000000000..db42eba7d
--- /dev/null
+++ b/sys/src/libc/fmt/fmtfd.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * public routine for final flush of a formatting buffer
+ * to a file descriptor; returns total char count.
+ */
+int
+fmtfdflush(Fmt *f)
+{
+ if(_fmtFdFlush(f) <= 0)
+ return -1;
+ return f->nfmt;
+}
+
+/*
+ * initialize an output buffer for buffered printing
+ */
+int
+fmtfdinit(Fmt *f, int fd, char *buf, int size)
+{
+ f->runes = 0;
+ f->start = buf;
+ f->to = buf;
+ f->stop = buf + size;
+ f->flush = _fmtFdFlush;
+ f->farg = (void*)fd;
+ f->nfmt = 0;
+ return 0;
+}
diff --git a/sys/src/libc/fmt/fmtlock.c b/sys/src/libc/fmt/fmtlock.c
new file mode 100755
index 000000000..02ae96312
--- /dev/null
+++ b/sys/src/libc/fmt/fmtlock.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+
+static Lock fmtl;
+
+void
+_fmtlock(void)
+{
+ lock(&fmtl);
+}
+
+void
+_fmtunlock(void)
+{
+ unlock(&fmtl);
+}
diff --git a/sys/src/libc/fmt/fmtprint.c b/sys/src/libc/fmt/fmtprint.c
new file mode 100755
index 000000000..4c858b94c
--- /dev/null
+++ b/sys/src/libc/fmt/fmtprint.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtprint(Fmt *f, char *fmt, ...)
+{
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ va = f->args;
+ va_start(f->args, fmt);
+ n = dofmt(f, fmt);
+ va_end(f->args);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ f->args = va;
+ if(n >= 0)
+ return 0;
+ return n;
+}
+
diff --git a/sys/src/libc/fmt/fmtquote.c b/sys/src/libc/fmt/fmtquote.c
new file mode 100755
index 000000000..bede387bc
--- /dev/null
+++ b/sys/src/libc/fmt/fmtquote.c
@@ -0,0 +1,249 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by _quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ * NUL in input
+ * count exceeded in input
+ * count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+_quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
+{
+ int w;
+ Rune c;
+
+ q->quoted = 0;
+ q->nbytesout = 0;
+ q->nrunesout = 0;
+ q->nbytesin = 0;
+ q->nrunesin = 0;
+ if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
+ if(nout < 2)
+ return;
+ q->quoted = 1;
+ q->nbytesout = 2;
+ q->nrunesout = 2;
+ }
+ for(; nin!=0; nin--){
+ if(s)
+ w = chartorune(&c, s);
+ else{
+ c = *r;
+ w = runelen(c);
+ }
+
+ if(c == '\0')
+ break;
+ if(runesout){
+ if(q->nrunesout+1 > nout)
+ break;
+ }else{
+ if(q->nbytesout+w > nout)
+ break;
+ }
+
+ if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){
+ if(!q->quoted){
+ if(runesout){
+ if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
+ break;
+ }else{
+ if(1+q->nbytesout+w+1 > nout) /* no room for quotes */
+ break;
+ }
+ q->nrunesout += 2; /* include quotes */
+ q->nbytesout += 2; /* include quotes */
+ q->quoted = 1;
+ }
+ if(c == '\'') {
+ if(runesout){
+ if(1+q->nrunesout+1 > nout) /* no room for quotes */
+ break;
+ }else{
+ if(1+q->nbytesout+w > nout) /* no room for quotes */
+ break;
+ }
+ q->nbytesout++;
+ q->nrunesout++; /* quotes reproduce as two characters */
+ }
+ }
+
+ /* advance input */
+ if(s)
+ s += w;
+ else
+ r++;
+ q->nbytesin += w;
+ q->nrunesin++;
+
+ /* advance output */
+ q->nbytesout += w;
+ q->nrunesout++;
+ }
+}
+
+static int
+qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
+{
+ Rune r, *rm, *rme;
+ char *t, *s, *m, *me;
+ Rune *rt, *rs;
+ ulong fl;
+ int nc, w;
+
+ m = sin;
+ me = m + q->nbytesin;
+ rm = rin;
+ rme = rm + q->nrunesin;
+
+ w = f->width;
+ fl = f->flags;
+ if(f->runes){
+ if(!(fl & FmtLeft) && _rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && _fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ t = f->to;
+ s = f->stop;
+ rt = f->to;
+ rs = f->stop;
+ if(f->runes)
+ FMTRCHAR(f, rt, rs, '\'');
+ else
+ FMTRUNE(f, t, s, '\'');
+ for(nc = q->nrunesin; nc > 0; nc--){
+ if(sin){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ }else{
+ if(rm >= rme)
+ break;
+ r = *(uchar*)rm++;
+ }
+ if(f->runes){
+ FMTRCHAR(f, rt, rs, r);
+ if(r == '\'')
+ FMTRCHAR(f, rt, rs, r);
+ }else{
+ FMTRUNE(f, t, s, r);
+ if(r == '\'')
+ FMTRUNE(f, t, s, r);
+ }
+ }
+
+ if(f->runes){
+ FMTRCHAR(f, rt, rs, '\'');
+ USED(rs);
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && _rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ }else{
+ FMTRUNE(f, t, s, '\'');
+ USED(s);
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && _fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+_quotestrfmt(int runesin, Fmt *f)
+{
+ int nin, outlen;
+ Rune *r;
+ char *s;
+ Quoteinfo q;
+
+ nin = -1;
+ if(f->flags&FmtPrec)
+ nin = f->prec;
+ if(runesin){
+ r = va_arg(f->args, Rune *);
+ s = nil;
+ }else{
+ s = va_arg(f->args, char *);
+ r = nil;
+ }
+ if(!s && !r)
+ return _fmtcpy(f, "<nil>", 5, 5);
+
+ if(f->flush)
+ outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
+ else if(f->runes)
+ outlen = (Rune*)f->stop - (Rune*)f->to;
+ else
+ outlen = (char*)f->stop - (char*)f->to;
+
+ _quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
+//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
+
+ if(runesin){
+ if(!q.quoted)
+ return _fmtrcpy(f, r, q.nrunesin);
+ return qstrfmt(nil, r, &q, f);
+ }
+
+ if(!q.quoted)
+ return _fmtcpy(f, s, q.nrunesin, q.nbytesin);
+ return qstrfmt(s, nil, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+ return _quotestrfmt(0, f);
+}
+
+int
+quoterunestrfmt(Fmt *f)
+{
+ return _quotestrfmt(1, f);
+}
+
+void
+quotefmtinstall(void)
+{
+ fmtinstall('q', quotestrfmt);
+ fmtinstall('Q', quoterunestrfmt);
+}
+
+int
+_needsquotes(char *s, int *quotelenp)
+{
+ Quoteinfo q;
+
+ _quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nbytesout;
+
+ return q.quoted;
+}
+
+int
+_runeneedsquotes(Rune *r, int *quotelenp)
+{
+ Quoteinfo q;
+
+ _quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nrunesout;
+
+ return q.quoted;
+}
diff --git a/sys/src/libc/fmt/fmtrune.c b/sys/src/libc/fmt/fmtrune.c
new file mode 100755
index 000000000..e2db137a3
--- /dev/null
+++ b/sys/src/libc/fmt/fmtrune.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+int
+fmtrune(Fmt *f, int r)
+{
+ Rune *rt;
+ char *t;
+ int n;
+
+ if(f->runes){
+ rt = f->to;
+ FMTRCHAR(f, rt, f->stop, r);
+ f->to = rt;
+ n = 1;
+ }else{
+ t = f->to;
+ FMTRUNE(f, t, f->stop, r);
+ n = t - (char*)f->to;
+ f->to = t;
+ }
+ f->nfmt += n;
+ return 0;
+}
diff --git a/sys/src/libc/fmt/fmtstr.c b/sys/src/libc/fmt/fmtstr.c
new file mode 100755
index 000000000..6f5821382
--- /dev/null
+++ b/sys/src/libc/fmt/fmtstr.c
@@ -0,0 +1,11 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+fmtstrflush(Fmt *f)
+{
+ if(f->start == nil)
+ return nil;
+ *(char*)f->to = '\0';
+ return f->start;
+}
diff --git a/sys/src/libc/fmt/fmtvprint.c b/sys/src/libc/fmt/fmtvprint.c
new file mode 100755
index 000000000..1d6493eaa
--- /dev/null
+++ b/sys/src/libc/fmt/fmtvprint.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtvprint(Fmt *f, char *fmt, va_list args)
+{
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ va = f->args;
+ f->args = args;
+ n = dofmt(f, fmt);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ f->args = va;
+ if(n >= 0)
+ return 0;
+ return n;
+}
+
diff --git a/sys/src/libc/fmt/fprint.c b/sys/src/libc/fmt/fprint.c
new file mode 100755
index 000000000..b1376a05a
--- /dev/null
+++ b/sys/src/libc/fmt/fprint.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+int
+fprint(int fd, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(fd, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libc/fmt/mkfile b/sys/src/libc/fmt/mkfile
new file mode 100755
index 000000000..7930c0a47
--- /dev/null
+++ b/sys/src/libc/fmt/mkfile
@@ -0,0 +1,46 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+
+OFILES=\
+ dofmt.$O\
+ dorfmt.$O\
+ errfmt.$O\
+ fltfmt.$O\
+ fmt.$O\
+ fmtfd.$O\
+ fmtlock.$O\
+ fmtprint.$O\
+ fmtquote.$O\
+ fmtrune.$O\
+ fmtstr.$O\
+ fmtvprint.$O\
+ fprint.$O\
+ print.$O\
+ runefmtstr.$O\
+ runeseprint.$O\
+ runesmprint.$O\
+ runesnprint.$O\
+ runesprint.$O\
+ runevseprint.$O\
+ runevsmprint.$O\
+ runevsnprint.$O\
+ seprint.$O\
+ smprint.$O\
+ snprint.$O\
+ sprint.$O\
+ vfprint.$O\
+ vseprint.$O\
+ vsmprint.$O\
+ vsnprint.$O\
+
+HFILES=/sys/include/libc.h\
+ fmtdef.h\
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}
+
+</sys/src/cmd/mksyslib
+
diff --git a/sys/src/libc/fmt/print.c b/sys/src/libc/fmt/print.c
new file mode 100755
index 000000000..fdbb52278
--- /dev/null
+++ b/sys/src/libc/fmt/print.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+int
+print(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(1, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libc/fmt/runefmtstr.c b/sys/src/libc/fmt/runefmtstr.c
new file mode 100755
index 000000000..9ce9c131c
--- /dev/null
+++ b/sys/src/libc/fmt/runefmtstr.c
@@ -0,0 +1,11 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runefmtstrflush(Fmt *f)
+{
+ if(f->start == nil)
+ return nil;
+ *(Rune*)f->to = '\0';
+ return f->start;
+}
diff --git a/sys/src/libc/fmt/runeseprint.c b/sys/src/libc/fmt/runeseprint.c
new file mode 100755
index 000000000..77a761e41
--- /dev/null
+++ b/sys/src/libc/fmt/runeseprint.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runeseprint(Rune *buf, Rune *e, char *fmt, ...)
+{
+ Rune *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = runevseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/sys/src/libc/fmt/runesmprint.c b/sys/src/libc/fmt/runesmprint.c
new file mode 100755
index 000000000..1f8b22025
--- /dev/null
+++ b/sys/src/libc/fmt/runesmprint.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runesmprint(char *fmt, ...)
+{
+ va_list args;
+ Rune *p;
+
+ va_start(args, fmt);
+ p = runevsmprint(fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/sys/src/libc/fmt/runesnprint.c b/sys/src/libc/fmt/runesnprint.c
new file mode 100755
index 000000000..ac15ec031
--- /dev/null
+++ b/sys/src/libc/fmt/runesnprint.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runesnprint(Rune *buf, int len, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = runevsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
+
diff --git a/sys/src/libc/fmt/runesprint.c b/sys/src/libc/fmt/runesprint.c
new file mode 100755
index 000000000..5eca8cd9a
--- /dev/null
+++ b/sys/src/libc/fmt/runesprint.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runesprint(Rune *buf, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = runevsnprint(buf, 256, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libc/fmt/runevseprint.c b/sys/src/libc/fmt/runevseprint.c
new file mode 100755
index 000000000..43c601e5e
--- /dev/null
+++ b/sys/src/libc/fmt/runevseprint.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runevseprint(Rune *buf, Rune *e, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(e <= buf)
+ return nil;
+ f.runes = 1;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ f.args = args;
+ dofmt(&f, fmt);
+ *(Rune*)f.to = '\0';
+ return f.to;
+}
+
diff --git a/sys/src/libc/fmt/runevsmprint.c b/sys/src/libc/fmt/runevsmprint.c
new file mode 100755
index 000000000..4db326297
--- /dev/null
+++ b/sys/src/libc/fmt/runevsmprint.c
@@ -0,0 +1,70 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+static int
+runeFmtStrFlush(Fmt *f)
+{
+ Rune *s;
+ int n;
+
+ if(f->start == nil)
+ return 0;
+ n = (int)(uintptr)f->farg;
+ n *= 2;
+ s = f->start;
+ f->start = realloc(s, sizeof(Rune)*n);
+ if(f->start == nil){
+ f->farg = nil;
+ f->to = nil;
+ f->stop = nil;
+ free(s);
+ return 0;
+ }
+ f->farg = (void*)n;
+ f->to = (Rune*)f->start + ((Rune*)f->to - s);
+ f->stop = (Rune*)f->start + n - 1;
+ return 1;
+}
+
+int
+runefmtstrinit(Fmt *f)
+{
+ int n;
+
+ memset(f, 0, sizeof *f);
+ f->runes = 1;
+ n = 32;
+ f->start = malloc(sizeof(Rune)*n);
+ if(f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (Rune*)f->start + n - 1;
+ f->flush = runeFmtStrFlush;
+ f->farg = (void*)n;
+ f->nfmt = 0;
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+Rune*
+runevsmprint(char *fmt, va_list args)
+{
+ Fmt f;
+ int n;
+
+ if(runefmtstrinit(&f) < 0)
+ return nil;
+ f.args = args;
+ n = dofmt(&f, fmt);
+ if(f.start == nil)
+ return nil;
+ if(n < 0){
+ free(f.start);
+ return nil;
+ }
+ *(Rune*)f.to = '\0';
+ return f.start;
+}
diff --git a/sys/src/libc/fmt/runevsnprint.c b/sys/src/libc/fmt/runevsnprint.c
new file mode 100755
index 000000000..61fe4af81
--- /dev/null
+++ b/sys/src/libc/fmt/runevsnprint.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runevsnprint(Rune *buf, int len, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(len <= 0)
+ return -1;
+ f.runes = 1;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ f.args = args;
+ dofmt(&f, fmt);
+ *(Rune*)f.to = '\0';
+ return (Rune*)f.to - buf;
+}
diff --git a/sys/src/libc/fmt/seprint.c b/sys/src/libc/fmt/seprint.c
new file mode 100755
index 000000000..519c1aa61
--- /dev/null
+++ b/sys/src/libc/fmt/seprint.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+ char *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = vseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/sys/src/libc/fmt/smprint.c b/sys/src/libc/fmt/smprint.c
new file mode 100755
index 000000000..7be117c29
--- /dev/null
+++ b/sys/src/libc/fmt/smprint.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+smprint(char *fmt, ...)
+{
+ va_list args;
+ char *p;
+
+ va_start(args, fmt);
+ p = vsmprint(fmt, args);
+ va_end(args);
+ setmalloctag(p, getcallerpc(&fmt));
+ return p;
+}
diff --git a/sys/src/libc/fmt/snprint.c b/sys/src/libc/fmt/snprint.c
new file mode 100755
index 000000000..8a4681392
--- /dev/null
+++ b/sys/src/libc/fmt/snprint.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
+
diff --git a/sys/src/libc/fmt/sprint.c b/sys/src/libc/fmt/sprint.c
new file mode 100755
index 000000000..2da6d925e
--- /dev/null
+++ b/sys/src/libc/fmt/sprint.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, 65536, fmt, args); /* big number, but sprint is deprecated anyway */
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libc/fmt/vfprint.c b/sys/src/libc/fmt/vfprint.c
new file mode 100755
index 000000000..63c9349f0
--- /dev/null
+++ b/sys/src/libc/fmt/vfprint.c
@@ -0,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+/*
+ * generic routine for flushing a formatting buffer
+ * to a file descriptor
+ */
+int
+_fmtFdFlush(Fmt *f)
+{
+ int n;
+
+ n = (char*)f->to - (char*)f->start;
+ if(n && write((int)(uintptr)f->farg, f->start, n) != n)
+ return 0;
+ f->to = f->start;
+ return 1;
+}
+
+int
+vfprint(int fd, char *fmt, va_list args)
+{
+ Fmt f;
+ char buf[256];
+ int n;
+
+ fmtfdinit(&f, fd, buf, sizeof(buf));
+ f.args = args;
+ n = dofmt(&f, fmt);
+ if(n > 0 && _fmtFdFlush(&f) == 0)
+ return -1;
+ return n;
+}
diff --git a/sys/src/libc/fmt/vseprint.c b/sys/src/libc/fmt/vseprint.c
new file mode 100755
index 000000000..72d02f7fa
--- /dev/null
+++ b/sys/src/libc/fmt/vseprint.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+vseprint(char *buf, char *e, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(e <= buf)
+ return nil;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ f.args = args;
+ dofmt(&f, fmt);
+ *(char*)f.to = '\0';
+ return f.to;
+}
+
diff --git a/sys/src/libc/fmt/vsmprint.c b/sys/src/libc/fmt/vsmprint.c
new file mode 100755
index 000000000..d183a5826
--- /dev/null
+++ b/sys/src/libc/fmt/vsmprint.c
@@ -0,0 +1,70 @@
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+
+static int
+fmtStrFlush(Fmt *f)
+{
+ char *s;
+ int n;
+
+ if(f->start == nil)
+ return 0;
+ n = (int)(uintptr)f->farg;
+ n *= 2;
+ s = f->start;
+ f->start = realloc(s, n);
+ if(f->start == nil){
+ f->farg = nil;
+ f->to = nil;
+ f->stop = nil;
+ free(s);
+ return 0;
+ }
+ f->farg = (void*)n;
+ f->to = (char*)f->start + ((char*)f->to - s);
+ f->stop = (char*)f->start + n - 1;
+ return 1;
+}
+
+int
+fmtstrinit(Fmt *f)
+{
+ int n;
+
+ memset(f, 0, sizeof *f);
+ f->runes = 0;
+ n = 32;
+ f->start = malloc(n);
+ if(f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (char*)f->start + n - 1;
+ f->flush = fmtStrFlush;
+ f->farg = (void*)n;
+ f->nfmt = 0;
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+char*
+vsmprint(char *fmt, va_list args)
+{
+ Fmt f;
+ int n;
+
+ if(fmtstrinit(&f) < 0)
+ return nil;
+ f.args = args;
+ n = dofmt(&f, fmt);
+ if(f.start == nil)
+ return nil;
+ if(n < 0){
+ free(f.start);
+ return nil;
+ }
+ *(char*)f.to = '\0';
+ return f.start;
+}
diff --git a/sys/src/libc/fmt/vsnprint.c b/sys/src/libc/fmt/vsnprint.c
new file mode 100755
index 000000000..bd2049684
--- /dev/null
+++ b/sys/src/libc/fmt/vsnprint.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+int
+vsnprint(char *buf, int len, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(len <= 0)
+ return -1;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ f.args = args;
+ dofmt(&f, fmt);
+ *(char*)f.to = '\0';
+ return (char*)f.to - buf;
+}
diff --git a/sys/src/libc/mips/argv0.s b/sys/src/libc/mips/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/mips/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/mips/atom.s b/sys/src/libc/mips/atom.s
new file mode 100755
index 000000000..8975ac569
--- /dev/null
+++ b/sys/src/libc/mips/atom.s
@@ -0,0 +1,57 @@
+/*
+ * R4000 user-level atomic operations
+ */
+
+#define LL(base, rt) WORD $((060<<26)|((base)<<21)|((rt)<<16))
+#define SC(base, rt) WORD $((070<<26)|((base)<<21)|((rt)<<16))
+#define NOOP WORD $0x27
+
+TEXT ainc(SB), 1, $-4 /* long ainc(long *); */
+TEXT _xinc(SB), 1, $-4 /* void _xinc(long *); */
+ MOVW R1, R2 /* address of counter */
+loop: MOVW $1, R3
+ LL(2, 1)
+ NOOP
+ ADD R1,R3,R3
+ SC(2, 3)
+ NOOP
+ BEQ R3,loop
+ RET
+
+TEXT adec(SB), 1, $-4 /* long adec(long*); */
+TEXT _xdec(SB), 1, $-4 /* long _xdec(long *); */
+ MOVW R1, R2 /* address of counter */
+loop1: MOVW $-1, R3
+ LL(2, 1)
+ NOOP
+ ADD R1,R3,R3
+ MOVW R3, R1
+ SC(2, 3)
+ NOOP
+ BEQ R3,loop1
+ RET
+
+/*
+ * int cas(uint* p, int ov, int nv);
+ */
+TEXT cas(SB), 1, $-4
+ MOVW ov+4(FP), R2
+ MOVW nv+8(FP), R3
+spincas:
+ LL(1, 4) /* R4 = *R1 */
+ NOOP
+ BNE R2, R4, fail
+ SC(1, 3) /* *R1 = R3 */
+ NOOP
+ BEQ R3, spincas /* R3 == 0 means store failed */
+ MOVW $1, R1
+ RET
+fail:
+ MOVW $0, R1
+ RET
+
+/* general-purpose abort */
+_trap:
+ MOVD $0, R0
+ MOVD 0(R0), R0
+ RET
diff --git a/sys/src/libc/mips/cycles.c b/sys/src/libc/mips/cycles.c
new file mode 100755
index 000000000..9bad3a989
--- /dev/null
+++ b/sys/src/libc/mips/cycles.c
@@ -0,0 +1,7 @@
+#include <u.h>
+#include <libc.h>
+
+void cycles(uvlong*u)
+{
+ *u = 0LL;
+}
diff --git a/sys/src/libc/mips/getcallerpc.s b/sys/src/libc/mips/getcallerpc.s
new file mode 100755
index 000000000..7397526e7
--- /dev/null
+++ b/sys/src/libc/mips/getcallerpc.s
@@ -0,0 +1,4 @@
+TEXT getcallerpc(SB), $0
+ MOVW 0(SP), R1
+ RET
+
diff --git a/sys/src/libc/mips/getfcr.s b/sys/src/libc/mips/getfcr.s
new file mode 100755
index 000000000..9e84cbccd
--- /dev/null
+++ b/sys/src/libc/mips/getfcr.s
@@ -0,0 +1,15 @@
+TEXT getfsr(SB), $0
+ MOVW FCR31, R1
+ RET
+
+TEXT setfsr(SB), $0
+ MOVW R1, FCR31
+ RET
+
+TEXT getfcr(SB), $0
+ MOVW FCR31, R1
+ RET
+
+TEXT setfcr(SB), $0
+ MOVW R1, FCR31
+ RET
diff --git a/sys/src/libc/mips/lock.c b/sys/src/libc/mips/lock.c
new file mode 100755
index 000000000..c42208822
--- /dev/null
+++ b/sys/src/libc/mips/lock.c
@@ -0,0 +1,169 @@
+#include <u.h>
+#include <libc.h>
+
+enum
+{
+ Pagesize = 4096,
+ Semperpg = Pagesize/(16*sizeof(uint)),
+ Lockaddr = 0x60000000,
+
+ POWER = 0x320,
+ MAGNUM = 0x330,
+ MAGNUMII = 0x340,
+ R4K = 0x500,
+};
+
+static int arch;
+extern int C_3ktas(int*);
+extern int C_4ktas(int*);
+extern int C_fcr0(void);
+
+static void
+lockinit(void)
+{
+ void *v;
+
+ if(arch != 0)
+ return; /* allow multiple calls */
+ arch = C_fcr0();
+ switch(arch) {
+ case POWER:
+ v = (void*)Lockaddr;
+ if(segattach(SG_CEXEC, "lock", v, Pagesize) == (void*)-1) {
+ arch = MAGNUM;
+ break;
+ }
+ memset(v, 0, Pagesize);
+ break;
+ case MAGNUM:
+ case MAGNUMII:
+ case R4K:
+ break;
+ default:
+ arch = R4K;
+ break;
+ }
+}
+
+void
+lock(Lock *lk)
+{
+ int *hwsem;
+ int hash;
+
+retry:
+ switch(arch) {
+ case 0:
+ lockinit();
+ goto retry;
+ case MAGNUM:
+ case MAGNUMII:
+ while(C_3ktas(&lk->val))
+ sleep(0);
+ return;
+ case R4K:
+ for(;;){
+ while(lk->val)
+ ;
+ if(C_4ktas(&lk->val) == 0)
+ return;
+ }
+ break;
+ case POWER:
+ /* Use low order lock bits to generate hash */
+ hash = ((int)lk/sizeof(int)) & (Semperpg-1);
+ hwsem = (int*)Lockaddr+hash;
+
+ for(;;) {
+ if((*hwsem & 1) == 0) {
+ if(lk->val)
+ *hwsem = 0;
+ else {
+ lk->val = 1;
+ *hwsem = 0;
+ return;
+ }
+ }
+ while(lk->val)
+ ;
+ }
+ }
+}
+
+int
+canlock(Lock *lk)
+{
+ int *hwsem;
+ int hash;
+
+retry:
+ switch(arch) {
+ case 0:
+ lockinit();
+ goto retry;
+ case MAGNUM:
+ case MAGNUMII:
+ if(C_3ktas(&lk->val))
+ return 0;
+ return 1;
+ case R4K:
+ if(C_4ktas(&lk->val))
+ return 0;
+ return 1;
+ case POWER:
+ /* Use low order lock bits to generate hash */
+ hash = ((int)lk/sizeof(int)) & (Semperpg-1);
+ hwsem = (int*)Lockaddr+hash;
+
+ if((*hwsem & 1) == 0) {
+ if(lk->val)
+ *hwsem = 0;
+ else {
+ lk->val = 1;
+ *hwsem = 0;
+ return 1;
+ }
+ }
+ return 0;
+ }
+}
+
+void
+unlock(Lock *lk)
+{
+ lk->val = 0;
+}
+
+int
+_tas(int *p)
+{
+ int *hwsem;
+ int hash;
+
+retry:
+ switch(arch) {
+ case 0:
+ lockinit();
+ goto retry;
+ case MAGNUM:
+ case MAGNUMII:
+ return C_3ktas(p);
+ case R4K:
+ return C_4ktas(p);
+ case POWER:
+ /* Use low order lock bits to generate hash */
+ hash = ((int)p/sizeof(int)) & (Semperpg-1);
+ hwsem = (int*)Lockaddr+hash;
+
+ if((*hwsem & 1) == 0) {
+ if(*p)
+ *hwsem = 0;
+ else {
+ *p = 1;
+ *hwsem = 0;
+ return 0;
+ }
+ }
+ return 1;
+ }
+}
diff --git a/sys/src/libc/mips/main9.s b/sys/src/libc/mips/main9.s
new file mode 100755
index 000000000..60ed1c148
--- /dev/null
+++ b/sys/src/libc/mips/main9.s
@@ -0,0 +1,25 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVW $setR30(SB), R30
+ MOVW R1, _tos(SB)
+
+ MOVW $p-64(SP), R1
+ MOVW R1, _privates(SB)
+ MOVW $NPRIVATES, R1
+ MOVW R1, _nprivates(SB)
+
+ MOVW inargc-4(FP), R1
+ MOVW $inargv+0(FP), R2
+ MOVW R1, 4(R29)
+ MOVW R2, 8(R29)
+ JAL main(SB)
+loop:
+ MOVW $_exitstr<>(SB), R1
+ MOVW R1, 4(R29)
+ JAL exits(SB)
+ JMP loop
+
+DATA _exitstr<>+0(SB)/4, $"main"
+GLOBL _exitstr<>+0(SB), $5
diff --git a/sys/src/libc/mips/main9p.s b/sys/src/libc/mips/main9p.s
new file mode 100755
index 000000000..135d07522
--- /dev/null
+++ b/sys/src/libc/mips/main9p.s
@@ -0,0 +1,36 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVW $setR30(SB), R30
+ MOVW R1, _tos(SB)
+
+ MOVW $p-64(SP), R1
+ MOVW R1, _privates(SB)
+ MOVW $NPRIVATES, R1
+ MOVW R1, _nprivates(SB)
+
+ JAL _profmain(SB)
+ MOVW __prof+4(SB), R1
+ MOVW R1, __prof+0(SB)
+ MOVW inargc-4(FP), R1
+ MOVW $inargv+0(FP), R2
+ MOVW R1, 4(R29)
+ MOVW R2, 8(R29)
+ JAL main(SB)
+loop:
+ MOVW $exits<>(SB), R1
+ MOVW R1, 4(R29)
+ JAL exits(SB)
+ MOVW $_profin(SB), R0 /* force loading of profile */
+ JMP loop
+
+TEXT _savearg(SB), 1, $0
+ RET
+
+TEXT _callpc(SB), 1, $0
+ MOVW argp-4(FP), R1
+ RET
+
+DATA exits<>+0(SB)/4, $"main"
+GLOBL exits<>+0(SB), $5
diff --git a/sys/src/libc/mips/memccpy.s b/sys/src/libc/mips/memccpy.s
new file mode 100755
index 000000000..776dd2768
--- /dev/null
+++ b/sys/src/libc/mips/memccpy.s
@@ -0,0 +1,20 @@
+ TEXT memccpy(SB), $0
+MOVW R1, 0(FP)
+ MOVW n+12(FP), R1
+ BEQ R1, ret
+ MOVW s1+0(FP), R3
+ MOVW s2+4(FP), R2
+ MOVBU c+11(FP), R4
+ ADDU R1, R2, R5
+
+l1: MOVBU (R2), R6
+ ADDU $1, R2
+ MOVBU R6, (R3)
+ ADDU $1, R3
+ BEQ R4, R6, eq
+ BNE R2, R5, l1
+ MOVW $0, R1
+ RET
+
+eq: MOVW R3, R1
+ret: RET
diff --git a/sys/src/libc/mips/memchr.s b/sys/src/libc/mips/memchr.s
new file mode 100755
index 000000000..5d1c3cc85
--- /dev/null
+++ b/sys/src/libc/mips/memchr.s
@@ -0,0 +1,39 @@
+ TEXT memchr(SB), $0
+MOVW R1, 0(FP)
+
+ MOVW n+8(FP), R1
+ MOVW s1+0(FP), R2
+ MOVBU c+7(FP), R3
+ ADDU R1, R2, R6
+
+ AND $(~1), R1, R5
+ ADDU R2, R5
+ BEQ R2, R5, lt2
+
+l1:
+ MOVBU 0(R2), R4
+ MOVBU 1(R2), R7
+ BEQ R3, R4, eq0
+ ADDU $2, R2
+ BEQ R3, R7, eq
+ BNE R2, R5, l1
+
+lt2:
+ BEQ R2, R6, zret
+
+l2:
+ MOVBU (R2), R4
+ ADDU $1, R2
+ BEQ R3, R4, eq
+ BNE R2, R6, l2
+zret:
+ MOVW R0, R1
+ RET
+
+eq0:
+ MOVW R2, R1
+ RET
+
+eq:
+ SUBU $1,R2, R1
+ RET
diff --git a/sys/src/libc/mips/memcmp.s b/sys/src/libc/mips/memcmp.s
new file mode 100755
index 000000000..fb82500d6
--- /dev/null
+++ b/sys/src/libc/mips/memcmp.s
@@ -0,0 +1,114 @@
+ TEXT memcmp(SB), $0
+MOVW R1, 0(FP)
+
+/*
+ * performance:
+ * alligned about 1.0us/call and 17.4mb/sec
+ * unalligned is about 3.1mb/sec
+ */
+
+ MOVW n+8(FP), R3 /* R3 is count */
+ MOVW s1+0(FP), R4 /* R4 is pointer1 */
+ MOVW s2+4(FP), R5 /* R5 is pointer2 */
+ ADDU R3,R4, R6 /* R6 is end pointer1 */
+
+/*
+ * if not at least 4 chars,
+ * dont even mess around.
+ * 3 chars to guarantee any
+ * rounding up to a word
+ * boundary and 4 characters
+ * to get at least maybe one
+ * full word cmp.
+ */
+ SGT $4,R3, R1
+ BNE R1, out
+
+/*
+ * test if both pointers
+ * are similarly word alligned
+ */
+ XOR R4,R5, R1
+ AND $3, R1
+ BNE R1, out
+
+/*
+ * byte at a time to word allign
+ */
+l1:
+ AND $3,R4, R1
+ BEQ R1, l2
+ MOVBU 0(R4), R8
+ MOVBU 0(R5), R9
+ ADDU $1, R4
+ BNE R8,R9, ne
+ ADDU $1, R5
+ JMP l1
+
+/*
+ * turn R3 into end pointer1-15
+ * cmp 16 at a time while theres room
+ */
+l2:
+ ADDU $-15,R6, R3
+l3:
+ SGTU R3,R4, R1
+ BEQ R1, l4
+ MOVW 0(R4), R8
+ MOVW 0(R5), R9
+ MOVW 4(R4), R10
+ BNE R8,R9, ne
+ MOVW 4(R5), R11
+ MOVW 8(R4), R8
+ BNE R10,R11, ne1
+ MOVW 8(R5), R9
+ MOVW 12(R4), R10
+ BNE R8,R9, ne
+ MOVW 12(R5), R11
+ ADDU $16, R4
+ BNE R10,R11, ne1
+ BNE R8,R9, ne
+ ADDU $16, R5
+ JMP l3
+
+/*
+ * turn R3 into end pointer1-3
+ * cmp 4 at a time while theres room
+ */
+l4:
+ ADDU $-3,R6, R3
+l5:
+ SGTU R3,R4, R1
+ BEQ R1, out
+ MOVW 0(R4), R8
+ MOVW 0(R5), R9
+ ADDU $4, R4
+ BNE R8,R9, ne /* only works because big endian */
+ ADDU $4, R5
+ JMP l5
+
+/*
+ * last loop, cmp byte at a time
+ */
+out:
+ SGTU R6,R4, R1
+ BEQ R1, ret
+ MOVBU 0(R4), R8
+ MOVBU 0(R5), R9
+ ADDU $1, R4
+ BNE R8,R9, ne
+ ADDU $1, R5
+ JMP out
+
+ne1:
+ SGTU R10,R11, R1
+ BNE R1, ret
+ MOVW $-1,R1
+ RET
+ne:
+ SGTU R8,R9, R1
+ BNE R1, ret
+ MOVW $-1,R1
+ret:
+ RET
+ END
diff --git a/sys/src/libc/mips/memmove.s b/sys/src/libc/mips/memmove.s
new file mode 100755
index 000000000..c14d2a823
--- /dev/null
+++ b/sys/src/libc/mips/memmove.s
@@ -0,0 +1,237 @@
+ TEXT memmove(SB), $0
+
+ JMP move
+
+ TEXT memcpy(SB), $0
+move:
+ MOVW R1, s1+0(FP)
+
+ MOVW n+8(FP), R3 /* R3 is count */
+ MOVW R1, R4 /* R4 is to-pointer */
+ SGT R0, R3, R5
+ BEQ R5, ok
+ MOVW (R0), R0 /* abort if negative count */
+ok:
+ MOVW s2+4(FP), R5 /* R5 is from-pointer */
+ ADDU R3,R5, R7 /* R7 is end from-pointer */
+ ADDU R3,R4, R6 /* R6 is end to-pointer */
+
+/*
+ * easiest test is copy backwards if
+ * destination string has higher mem address
+ */
+ SGT $4,R3, R2
+ SGTU R4,R5, R1
+ BNE R1, back
+
+/*
+ * if not at least 4 chars,
+ * don't even mess around.
+ * 3 chars to guarantee any
+ * rounding up to a word
+ * boundary and 4 characters
+ * to get at least maybe one
+ * full word store.
+ */
+ BNE R2, fout
+
+
+/*
+ * byte at a time to word align destination
+ */
+f1:
+ AND $3,R4, R1
+ BEQ R1, f2
+ MOVB 0(R5), R8
+ ADDU $1, R5
+ MOVB R8, 0(R4)
+ ADDU $1, R4
+ JMP f1
+
+/*
+ * test if source is now word aligned
+ */
+f2:
+ AND $3, R5, R1
+ BNE R1, fun2
+/*
+ * turn R3 into to-end pointer-15
+ * copy 16 at a time while theres room.
+ * R6 is smaller than R7 --
+ * there are problems if R7 is 0.
+ */
+ ADDU $-15,R6, R3
+f3:
+ SGTU R3,R4, R1
+ BEQ R1, f4
+ MOVW 0(R5), R8
+ MOVW 4(R5), R9
+ MOVW R8, 0(R4)
+ MOVW 8(R5), R8
+ MOVW R9, 4(R4)
+ MOVW 12(R5), R9
+ ADDU $16, R5
+ MOVW R8, 8(R4)
+ MOVW R9, 12(R4)
+ ADDU $16, R4
+ JMP f3
+
+/*
+ * turn R3 into to-end pointer-3
+ * copy 4 at a time while theres room
+ */
+f4:
+ ADDU $-3,R6, R3
+f5:
+ SGTU R3,R4, R1
+ BEQ R1, fout
+ MOVW 0(R5), R8
+ ADDU $4, R5
+ MOVW R8, 0(R4)
+ ADDU $4, R4
+ JMP f5
+
+/*
+ * forward copy, unaligned
+ * turn R3 into to-end pointer-15
+ * copy 16 at a time while theres room.
+ * R6 is smaller than R7 --
+ * there are problems if R7 is 0.
+ */
+fun2:
+ ADDU $-15,R6, R3
+fun3:
+ SGTU R3,R4, R1
+ BEQ R1, fun4
+ MOVWL 0(R5), R8
+ MOVWR 3(R5), R8
+ MOVWL 4(R5), R9
+ MOVWR 7(R5), R9
+ MOVW R8, 0(R4)
+ MOVWL 8(R5), R8
+ MOVWR 11(R5), R8
+ MOVW R9, 4(R4)
+ MOVWL 12(R5), R9
+ MOVWR 15(R5), R9
+ ADDU $16, R5
+ MOVW R8, 8(R4)
+ MOVW R9, 12(R4)
+ ADDU $16, R4
+ JMP fun3
+
+/*
+ * turn R3 into to-end pointer-3
+ * copy 4 at a time while theres room
+ */
+fun4:
+ ADDU $-3,R6, R3
+fun5:
+ SGTU R3,R4, R1
+ BEQ R1, fout
+ MOVWL 0(R5), R8
+ MOVWR 3(R5), R8
+ ADDU $4, R5
+ MOVW R8, 0(R4)
+ ADDU $4, R4
+ JMP fun5
+
+/*
+ * last loop, copy byte at a time
+ */
+fout:
+ BEQ R7,R5, ret
+ MOVB 0(R5), R8
+ ADDU $1, R5
+ MOVB R8, 0(R4)
+ ADDU $1, R4
+ JMP fout
+
+/*
+ * whole thing repeated for backwards
+ */
+back:
+ BNE R2, bout
+b1:
+ AND $3,R6, R1
+ BEQ R1, b2
+ MOVB -1(R7), R8
+ ADDU $-1, R7
+ MOVB R8, -1(R6)
+ ADDU $-1, R6
+ JMP b1
+
+b2:
+ AND $3, R7, R1
+ BNE R1, bun2
+
+ ADDU $15,R5, R3
+b3:
+ SGTU R7,R3, R1
+ BEQ R1, b4
+ MOVW -4(R7), R8
+ MOVW -8(R7), R9
+ MOVW R8, -4(R6)
+ MOVW -12(R7), R8
+ MOVW R9, -8(R6)
+ MOVW -16(R7), R9
+ ADDU $-16, R7
+ MOVW R8, -12(R6)
+ MOVW R9, -16(R6)
+ ADDU $-16, R6
+ JMP b3
+b4:
+ ADDU $3,R5, R3
+b5:
+ SGTU R7,R3, R1
+ BEQ R1, bout
+ MOVW -4(R7), R8
+ ADDU $-4, R7
+ MOVW R8, -4(R6)
+ ADDU $-4, R6
+ JMP b5
+
+bun2:
+ ADDU $15,R5, R3
+bun3:
+ SGTU R7,R3, R1
+ BEQ R1, bun4
+ MOVWL -4(R7), R8
+ MOVWR -1(R7), R8
+ MOVWL -8(R7), R9
+ MOVWR -5(R7), R9
+ MOVW R8, -4(R6)
+ MOVWL -12(R7), R8
+ MOVWR -9(R7), R8
+ MOVW R9, -8(R6)
+ MOVWL -16(R7), R9
+ MOVWR -13(R7), R9
+ ADDU $-16, R7
+ MOVW R8, -12(R6)
+ MOVW R9, -16(R6)
+ ADDU $-16, R6
+ JMP bun3
+
+bun4:
+ ADDU $3,R5, R3
+bun5:
+ SGTU R7,R3, R1
+ BEQ R1, bout
+ MOVWL -4(R7), R8
+ MOVWR -1(R7), R8
+ ADDU $-4, R7
+ MOVW R8, -4(R6)
+ ADDU $-4, R6
+ JMP bun5
+
+bout:
+ BEQ R7,R5, ret
+ MOVB -1(R7), R8
+ ADDU $-1, R7
+ MOVB R8, -1(R6)
+ ADDU $-1, R6
+ JMP bout
+
+ret:
+ MOVW s1+0(FP), R1
+ RET
+ END
diff --git a/sys/src/libc/mips/memset.s b/sys/src/libc/mips/memset.s
new file mode 100755
index 000000000..8c9467f45
--- /dev/null
+++ b/sys/src/libc/mips/memset.s
@@ -0,0 +1,88 @@
+ TEXT memset(SB),$12
+MOVW R1, 0(FP)
+
+/*
+ * performance:
+ * about 1us/call and 28mb/sec
+ */
+
+ MOVW n+8(FP), R3 /* R3 is count */
+ MOVW p+0(FP), R4 /* R4 is pointer */
+ MOVW c+4(FP), R5 /* R5 is char */
+ ADDU R3,R4, R6 /* R6 is end pointer */
+
+/*
+ * if not at least 4 chars,
+ * dont even mess around.
+ * 3 chars to guarantee any
+ * rounding up to a word
+ * boundary and 4 characters
+ * to get at least maybe one
+ * full word store.
+ */
+ SGT $4,R3, R1
+ BNE R1, out
+
+/*
+ * turn R5 into a word of characters
+ */
+ AND $0xff, R5
+ SLL $8,R5, R1
+ OR R1, R5
+ SLL $16,R5, R1
+ OR R1, R5
+
+/*
+ * store one byte at a time until pointer
+ * is alligned on a word boundary
+ */
+l1:
+ AND $3,R4, R1
+ BEQ R1, l2
+ MOVB R5, 0(R4)
+ ADDU $1, R4
+ JMP l1
+
+/*
+ * turn R3 into end pointer-15
+ * store 16 at a time while theres room
+ */
+l2:
+ ADDU $-15,R6, R3
+l3:
+ SGTU R3,R4, R1
+ BEQ R1, l4
+ MOVW R5, 0(R4)
+ MOVW R5, 4(R4)
+ ADDU $16, R4
+ MOVW R5, -8(R4)
+ MOVW R5, -4(R4)
+ JMP l3
+
+/*
+ * turn R3 into end pointer-3
+ * store 4 at a time while theres room
+ */
+l4:
+ ADDU $-3,R6, R3
+l5:
+ SGTU R3,R4, R1
+ BEQ R1, out
+ MOVW R5, 0(R4)
+ ADDU $4, R4
+ JMP l5
+
+/*
+ * last loop, store byte at a time
+ */
+out:
+ SGTU R6,R4 ,R1
+ BEQ R1, ret
+ MOVB R5, 0(R4)
+ ADDU $1, R4
+ JMP out
+
+ret:
+ MOVW s1+0(FP), R1
+ RET
+ END
diff --git a/sys/src/libc/mips/mkfile b/sys/src/libc/mips/mkfile
new file mode 100755
index 000000000..3b902f6e3
--- /dev/null
+++ b/sys/src/libc/mips/mkfile
@@ -0,0 +1,40 @@
+objtype=mips
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ atom.s\
+ getcallerpc.s\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memccpy.s\
+ memchr.s\
+ memcmp.s\
+ memmove.s\
+ memset.s\
+ setjmp.s\
+ strchr.s\
+ strcmp.s\
+ strcpy.s\
+ tas.s\
+ vlop.s\
+
+CFILES=\
+ cycles.c\
+ lock.c\
+ notejmp.c\
+ sqrt.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/mips/notejmp.c b/sys/src/libc/mips/notejmp.c
new file mode 100755
index 000000000..9d3be9389
--- /dev/null
+++ b/sys/src/libc/mips/notejmp.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ r->r1 = ret;
+ if(ret == 0)
+ r->r1 = 1;
+ r->pc = j[JMPBUFPC];
+ r->sp = j[JMPBUFSP];
+ noted(NCONT);
+}
diff --git a/sys/src/libc/mips/setjmp.s b/sys/src/libc/mips/setjmp.s
new file mode 100755
index 000000000..bb971b60f
--- /dev/null
+++ b/sys/src/libc/mips/setjmp.s
@@ -0,0 +1,14 @@
+TEXT setjmp(SB), 1, $-4
+ MOVW R29, (R1)
+ MOVW R31, 4(R1)
+ MOVW $0, R1
+ RET
+
+TEXT longjmp(SB), 1, $-4
+ MOVW r+4(FP), R3
+ BNE R3, ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVW $1, R3 /* bless their pointed heads */
+ok: MOVW (R1), R29
+ MOVW 4(R1), R31
+ MOVW R3, R1
+ RET
diff --git a/sys/src/libc/mips/sqrt.c b/sys/src/libc/mips/sqrt.c
new file mode 100755
index 000000000..fa27c35ef
--- /dev/null
+++ b/sys/src/libc/mips/sqrt.c
@@ -0,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+
+static long sqtab[64] =
+{
+ 0x6cdb2, 0x726d4, 0x77ea3, 0x7d52f, 0x82a85, 0x87eb1, 0x8d1c0, 0x923bd,
+ 0x974b2, 0x9c4a8, 0xa13a9, 0xa61be, 0xaaeee, 0xafb41, 0xb46bf, 0xb916e,
+ 0xbdb55, 0xc247a, 0xc6ce3, 0xcb495, 0xcfb95, 0xd41ea, 0xd8796, 0xdcca0,
+ 0xe110c, 0xe54dd, 0xe9818, 0xedac0, 0xf1cd9, 0xf5e67, 0xf9f6e, 0xfdfef,
+ 0x01fe0, 0x05ee6, 0x09cfd, 0x0da30, 0x11687, 0x1520c, 0x18cc8, 0x1c6c1,
+ 0x20000, 0x2388a, 0x27068, 0x2a79e, 0x2de32, 0x3142b, 0x3498c, 0x37e5b,
+ 0x3b29d, 0x3e655, 0x41989, 0x44c3b, 0x47e70, 0x4b02b, 0x4e16f, 0x51241,
+ 0x542a2, 0x57296, 0x5a220, 0x5d142, 0x60000, 0x62e5a, 0x65c55, 0x689f2,
+};
+
+double
+sqrt(double arg)
+{
+ int e, ms;
+ double a, t;
+ union
+ {
+ double d;
+ struct
+ {
+ long ms;
+ long ls;
+ };
+ } u;
+
+ u.d = arg;
+ ms = u.ms;
+
+ /*
+ * sign extend the mantissa with
+ * exponent. result should be > 0 for
+ * normal case.
+ */
+ e = ms >> 20;
+ if(e <= 0) {
+ if(e == 0)
+ return 0;
+ return NaN();
+ }
+
+ /*
+ * pick up arg/4 by adjusting exponent
+ */
+ u.ms = ms - (2 << 20);
+ a = u.d;
+
+ /*
+ * use 5 bits of mantissa and 1 bit
+ * of exponent to form table index.
+ * insert exponent/2 - 1.
+ */
+ e = (((e - 1023) >> 1) + 1022) << 20;
+ u.ms = *(long*)((char*)sqtab + ((ms >> 13) & 0xfc)) | e;
+ u.ls = 0;
+
+ /*
+ * three laps of newton
+ */
+ e = 1 << 20;
+ t = u.d;
+ u.d = t + a/t;
+ u.ms -= e; /* u.d /= 2; */
+ t = u.d;
+ u.d = t + a/t;
+ u.ms -= e; /* u.d /= 2; */
+ t = u.d;
+
+ return t + a/t;
+}
+
+/*
+ * this is the program that generated the table.
+ * it calls sqrt by some other means.
+ *
+ * void
+ * main(void)
+ * {
+ * int i;
+ * union U
+ * {
+ * double d;
+ * struct
+ * {
+ * long ms;
+ * long ls;
+ * };
+ * } u;
+ *
+ * for(i=0; i<64; i++) {
+ * u.ms = (i<<15) | 0x3fe04000;
+ * u.ls = 0;
+ * u.d = sqrt(u.d);
+ * print(" 0x%.5lux,", u.ms & 0xfffff);
+ * }
+ * print("\n");
+ * exits(0);
+ * }
+ */
diff --git a/sys/src/libc/mips/strchr.s b/sys/src/libc/mips/strchr.s
new file mode 100755
index 000000000..e009ffad2
--- /dev/null
+++ b/sys/src/libc/mips/strchr.s
@@ -0,0 +1,63 @@
+ TEXT strchr(SB), $0
+MOVW R1, 0(FP)
+ MOVB c+7(FP), R4
+ MOVW s+0(FP), R3
+
+ BEQ R4, l2
+
+/*
+ * char is not null
+ */
+l1:
+ MOVB (R3), R1
+ ADDU $1, R3
+ BEQ R1, ret
+ BNE R1,R4, l1
+ JMP rm1
+
+/*
+ * char is null
+ * align to word
+ */
+l2:
+ AND $3,R3, R1
+ BEQ R1, l3
+ MOVB (R3), R1
+ ADDU $1, R3
+ BNE R1, l2
+ JMP rm1
+
+l3:
+ MOVW $0xff000000, R6
+ MOVW $0x00ff0000, R7
+
+l4:
+ MOVW (R3), R5
+ ADDU $4, R3
+ AND R6,R5, R1
+ AND R7,R5, R2
+ BEQ R1, b0
+ AND $0xff00,R5, R1
+ BEQ R2, b1
+ AND $0xff,R5, R2
+ BEQ R1, b2
+ BNE R2, l4
+
+rm1:
+ ADDU $-1,R3, R1
+ JMP ret
+
+b2:
+ ADDU $-2,R3, R1
+ JMP ret
+
+b1:
+ ADDU $-3,R3, R1
+ JMP ret
+
+b0:
+ ADDU $-4,R3, R1
+ JMP ret
+
+ret:
+ RET
diff --git a/sys/src/libc/mips/strcmp.s b/sys/src/libc/mips/strcmp.s
new file mode 100755
index 000000000..da986a2ed
--- /dev/null
+++ b/sys/src/libc/mips/strcmp.s
@@ -0,0 +1,21 @@
+TEXT strcmp(SB), $0
+
+ MOVW s2+4(FP), R2
+
+l1:
+ MOVB (R2), R3
+ MOVB (R1), R4
+ ADDU $1, R1
+ BEQ R3, end
+ ADDU $1, R2
+ BEQ R3, R4, l1
+
+ SGTU R4, R3, R1
+ BNE R1, ret
+ MOVW $-1, R1
+ RET
+
+end:
+ SGTU R4, R3, R1
+ret:
+ RET
diff --git a/sys/src/libc/mips/strcpy.s b/sys/src/libc/mips/strcpy.s
new file mode 100755
index 000000000..77be42669
--- /dev/null
+++ b/sys/src/libc/mips/strcpy.s
@@ -0,0 +1,96 @@
+TEXT strcpy(SB), $0
+
+ MOVW s2+4(FP),R2 /* R2 is from pointer */
+ MOVW R1, R3 /* R3 is to pointer */
+
+/*
+ * align 'from' pointer
+ */
+l1:
+ AND $3, R2, R5
+ ADDU $1, R2
+ BEQ R5, l2
+ MOVB -1(R2), R5
+ ADDU $1, R3
+ MOVB R5, -1(R3)
+ BNE R5, l1
+ RET
+
+/*
+ * test if 'to' is also alligned
+ */
+l2:
+ AND $3,R3, R5
+ BEQ R5, l4
+
+/*
+ * copy 4 at a time, 'to' not aligned
+ */
+l3:
+ MOVW -1(R2), R4
+ ADD $4, R2
+ ADD $4, R3
+ SRL $24,R4, R5
+ MOVB R5, -4(R3)
+ BEQ R5, out
+
+ SRL $16,R4, R5
+ AND $0xff, R5
+ MOVB R5, -3(R3)
+ BEQ R5, out
+
+ SRL $8,R4, R5
+ AND $0xff, R5
+ MOVB R5, -2(R3)
+ BEQ R5, out
+
+ AND $0xff,R4, R5
+ MOVB R5, -1(R3)
+ BNE R5, l3
+
+out:
+ RET
+
+/*
+ * word at a time both aligned
+ */
+l4:
+ MOVW $0xff000000, R7
+ MOVW $0x00ff0000, R8
+
+l5:
+ ADDU $4, R3
+ MOVW -1(R2), R4 /* fetch */
+
+ ADDU $4, R2
+ AND R7,R4, R5 /* is it byte 0 */
+ AND R8,R4, R6 /* is it byte 1 */
+ BEQ R5, b0
+
+ AND $0xff00,R4, R5 /* is it byte 2 */
+ BEQ R6, b1
+
+ AND $0xff,R4, R6 /* is it byte 3 */
+ BEQ R5, b2
+
+ MOVW R4, -4(R3) /* store */
+ BNE R6, l5
+ JMP out
+
+b0:
+ MOVB $0, -4(R3)
+ JMP out
+
+b1:
+ SRL $24, R4
+ MOVB R4, -4(R3)
+ MOVB $0, -3(R3)
+ JMP out
+
+b2:
+ SRL $24,R4, R5
+ MOVB R5, -4(R3)
+ SRL $16, R4
+ MOVB R4, -3(R3)
+ MOVB $0, -2(R3)
+ JMP out
diff --git a/sys/src/libc/mips/tas.s b/sys/src/libc/mips/tas.s
new file mode 100755
index 000000000..d754b21f6
--- /dev/null
+++ b/sys/src/libc/mips/tas.s
@@ -0,0 +1,33 @@
+/*
+ * magnum user level lock code
+ */
+
+#define LL(base, rt) WORD $((060<<26)|((base)<<21)|((rt)<<16))
+#define SC(base, rt) WORD $((070<<26)|((base)<<21)|((rt)<<16))
+#define NOOP WORD $0x27
+#define COP3 WORD $(023<<26)
+
+ TEXT C_3ktas(SB),$0
+ MOVW R1, R21
+btas:
+ MOVW R21, R1
+ MOVB R0, 1(R1)
+ NOOP
+ COP3
+ BLTZ R1, btas
+ RET
+
+ TEXT C_4ktas(SB), $0
+ MOVW R1, R2 /* address of key */
+tas1:
+ MOVW $1, R3
+ LL(2, 1)
+ NOOP
+ SC(2, 3)
+ NOOP
+ BEQ R3, tas1
+ RET
+
+ TEXT C_fcr0(SB), $0
+ MOVW FCR0, R1
+ RET
diff --git a/sys/src/libc/mips/vlop.s b/sys/src/libc/mips/vlop.s
new file mode 100755
index 000000000..17f487ad8
--- /dev/null
+++ b/sys/src/libc/mips/vlop.s
@@ -0,0 +1,17 @@
+TEXT _mulv(SB), $0
+ MOVW 8(FP), R2
+ MOVW 4(FP), R3
+ MOVW 16(FP), R4
+ MOVW 12(FP), R5
+ MULU R4, R2
+ MOVW LO, R6
+ MOVW HI, R7
+ MULU R3, R4
+ MOVW LO, R8
+ ADDU R8, R7
+ MULU R2, R5
+ MOVW LO, R8
+ ADDU R8, R7
+ MOVW R6, 4(R1)
+ MOVW R7, 0(R1)
+ RET
diff --git a/sys/src/libc/mips/vlrt.c b/sys/src/libc/mips/vlrt.c
new file mode 100755
index 000000000..099c2c55d
--- /dev/null
+++ b/sys/src/libc/mips/vlrt.c
@@ -0,0 +1,723 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ union
+ {
+ struct
+ {
+ ulong hi;
+ ulong lo;
+ };
+ struct
+ {
+ ushort hims;
+ ushort hils;
+ ushort loms;
+ ushort lols;
+ };
+ };
+};
+
+void abort(void);
+
+/* needed by profiler; can't be profiled. */
+#pragma profile off
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+#pragma profile on
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(qp) {
+ qp->lo = quolo;
+ qp->hi = quohi;
+ }
+ if(rp) {
+ rp->lo = numlo;
+ rp->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u.lo = 0;
+ u.hi = 0;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
diff --git a/sys/src/libc/mkfile b/sys/src/libc/mkfile
new file mode 100755
index 000000000..3634076f8
--- /dev/null
+++ b/sys/src/libc/mkfile
@@ -0,0 +1,51 @@
+</$objtype/mkfile
+
+PORTDIRS=9sys 9syscall fmt port
+DIRS=$PORTDIRS $CPUS
+OLDCPUS=68000 68020 sparc
+
+all install:V:
+ for(i in $PORTDIRS $objtype)@{
+ echo $i
+ cd $i
+ mk $MKFLAGS install
+ }
+
+clean:V:
+ for(i in $DIRS)@{
+ echo $i
+ cd $i
+ mk $MKFLAGS clean
+ }
+
+nuke:V:
+ for(i in $PORTDIRS $objtype)@{
+ echo $i
+ cd $i
+ mk $MKFLAGS nuke
+ }
+ # do not nuke other objtypes
+ for(i in $CPUS)@{
+ echo $i
+ cd $i
+ mk $MKFLAGS clean
+ }
+
+update:V:
+ for(i in $DIRS)@{
+ echo $i
+ cd $i
+ mk $MKFLAGS update
+ }
+ update $UPDATEFLAGS /386/lib/libc.a
+
+installall:V:
+ for(objtype in $CPUS) mk $MKFLAGS install
+
+everything:V:
+ rm -f */*.[012456789kqvxz]
+ for(objtype in $CPUS $OLDCPUS)@{
+ echo $objtype
+ mk $MKFLAGS install
+ }
+ rm -f */*.[012456789kqvxz]
diff --git a/sys/src/libc/port/_assert.c b/sys/src/libc/port/_assert.c
new file mode 100755
index 000000000..929e4c113
--- /dev/null
+++ b/sys/src/libc/port/_assert.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+void (*__assert)(char*);
+
+void
+_assert(char *s)
+{
+ if(__assert)
+ (*__assert)(s);
+ fprint(2, "assert failed: %s\n", s);
+ abort();
+}
diff --git a/sys/src/libc/port/abs.c b/sys/src/libc/port/abs.c
new file mode 100755
index 000000000..7e8adda55
--- /dev/null
+++ b/sys/src/libc/port/abs.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+int
+abs(int a)
+{
+ if(a < 0)
+ return -a;
+ return a;
+}
+
+long
+labs(long a)
+{
+ if(a < 0)
+ return -a;
+ return a;
+}
diff --git a/sys/src/libc/port/asin.c b/sys/src/libc/port/asin.c
new file mode 100755
index 000000000..7cb1455ed
--- /dev/null
+++ b/sys/src/libc/port/asin.c
@@ -0,0 +1,40 @@
+/*
+ * asin(arg) and acos(arg) return the arcsin, arccos,
+ * respectively of their arguments.
+ *
+ * Arctan is called after appropriate range reduction.
+ */
+
+#include <u.h>
+#include <libc.h>
+
+double
+asin(double arg)
+{
+ double temp;
+ int sign;
+
+ sign = 0;
+ if(arg < 0) {
+ arg = -arg;
+ sign++;
+ }
+ if(arg > 1)
+ return NaN();
+ temp = sqrt(1 - arg*arg);
+ if(arg > 0.7)
+ temp = PIO2 - atan(temp/arg);
+ else
+ temp = atan(arg/temp);
+ if(sign)
+ temp = -temp;
+ return temp;
+}
+
+double
+acos(double arg)
+{
+ if(arg > 1 || arg < -1)
+ return NaN();
+ return PIO2 - asin(arg);
+}
diff --git a/sys/src/libc/port/atan.c b/sys/src/libc/port/atan.c
new file mode 100755
index 000000000..4de3a2218
--- /dev/null
+++ b/sys/src/libc/port/atan.c
@@ -0,0 +1,78 @@
+/*
+ floating-point arctangent
+
+ atan returns the value of the arctangent of its
+ argument in the range [-pi/2,pi/2].
+
+ atan2 returns the arctangent of arg1/arg2
+ in the range [-pi,pi].
+
+ there are no error returns.
+
+ coefficients are #5077 from Hart & Cheney. (19.56D)
+*/
+
+#include <u.h>
+#include <libc.h>
+
+#define sq2p1 2.414213562373095048802e0
+#define sq2m1 .414213562373095048802e0
+#define p4 .161536412982230228262e2
+#define p3 .26842548195503973794141e3
+#define p2 .11530293515404850115428136e4
+#define p1 .178040631643319697105464587e4
+#define p0 .89678597403663861959987488e3
+#define q4 .5895697050844462222791e2
+#define q3 .536265374031215315104235e3
+#define q2 .16667838148816337184521798e4
+#define q1 .207933497444540981287275926e4
+#define q0 .89678597403663861962481162e3
+
+
+/*
+ xatan evaluates a series valid in the
+ range [-0.414...,+0.414...]. (tan(pi/8))
+ */
+
+static
+double
+xatan(double arg)
+{
+ double argsq, value;
+
+ argsq = arg*arg;
+ value = ((((p4*argsq + p3)*argsq + p2)*argsq + p1)*argsq + p0);
+ value = value/(((((argsq + q4)*argsq + q3)*argsq + q2)*argsq + q1)*argsq + q0);
+ return value*arg;
+}
+
+/*
+ satan reduces its argument (known to be positive)
+ to the range [0,0.414...] and calls xatan.
+ */
+
+static
+double
+satan(double arg)
+{
+
+ if(arg < sq2m1)
+ return xatan(arg);
+ if(arg > sq2p1)
+ return PIO2 - xatan(1/arg);
+ return PIO2/2 + xatan((arg-1)/(arg+1));
+}
+
+/*
+ atan makes its argument positive and
+ calls the inner routine satan.
+ */
+
+double
+atan(double arg)
+{
+
+ if(arg > 0)
+ return satan(arg);
+ return -satan(-arg);
+}
diff --git a/sys/src/libc/port/atan2.c b/sys/src/libc/port/atan2.c
new file mode 100755
index 000000000..480eb4c67
--- /dev/null
+++ b/sys/src/libc/port/atan2.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+/*
+ atan2 discovers what quadrant the angle
+ is in and calls atan.
+*/
+
+double
+atan2(double arg1, double arg2)
+{
+
+ if(arg1+arg2 == arg1) {
+ if(arg1 >= 0)
+ return PIO2;
+ return -PIO2;
+ }
+ arg1 = atan(arg1/arg2);
+ if(arg2 < 0) {
+ if(arg1 <= 0)
+ return arg1 + PI;
+ return arg1 - PI;
+ }
+ return arg1;
+}
diff --git a/sys/src/libc/port/atexit.c b/sys/src/libc/port/atexit.c
new file mode 100755
index 000000000..e9cb6c297
--- /dev/null
+++ b/sys/src/libc/port/atexit.c
@@ -0,0 +1,60 @@
+#include <u.h>
+#include <libc.h>
+
+#define NEXIT 33
+
+typedef struct Onex Onex;
+struct Onex{
+ void (*f)(void);
+ int pid;
+};
+
+static Lock onexlock;
+Onex onex[NEXIT];
+
+int
+atexit(void (*f)(void))
+{
+ int i;
+
+ lock(&onexlock);
+ for(i=0; i<NEXIT; i++)
+ if(onex[i].f == 0) {
+ onex[i].pid = getpid();
+ onex[i].f = f;
+ unlock(&onexlock);
+ return 1;
+ }
+ unlock(&onexlock);
+ return 0;
+}
+
+void
+atexitdont(void (*f)(void))
+{
+ int i, pid;
+
+ pid = getpid();
+ for(i=0; i<NEXIT; i++)
+ if(onex[i].f == f && onex[i].pid == pid)
+ onex[i].f = 0;
+}
+
+#pragma profile off
+
+void
+exits(char *s)
+{
+ int i, pid;
+ void (*f)(void);
+
+ pid = getpid();
+ for(i = NEXIT-1; i >= 0; i--)
+ if((f = onex[i].f) && pid == onex[i].pid) {
+ onex[i].f = 0;
+ (*f)();
+ }
+ _exits(s);
+}
+
+#pragma profile on
diff --git a/sys/src/libc/port/atnotify.c b/sys/src/libc/port/atnotify.c
new file mode 100755
index 000000000..60e8ad0c0
--- /dev/null
+++ b/sys/src/libc/port/atnotify.c
@@ -0,0 +1,58 @@
+#include <u.h>
+#include <libc.h>
+
+#define NFN 33
+static int (*onnot[NFN])(void*, char*);
+static Lock onnotlock;
+
+static
+void
+notifier(void *v, char *s)
+{
+ int i;
+
+ for(i=0; i<NFN; i++)
+ if(onnot[i] && ((*onnot[i])(v, s))){
+ noted(NCONT);
+ return;
+ }
+ noted(NDFLT);
+}
+
+int
+atnotify(int (*f)(void*, char*), int in)
+{
+ int i, n, ret;
+ static int init;
+
+ if(!init){
+ notify(notifier);
+ init = 1; /* assign = */
+ }
+ ret = 0;
+ lock(&onnotlock);
+ if(in){
+ for(i=0; i<NFN; i++)
+ if(onnot[i] == 0) {
+ onnot[i] = f;
+ ret = 1;
+ break;
+ }
+ }else{
+ n = 0;
+ for(i=0; i<NFN; i++)
+ if(onnot[i]){
+ if(ret==0 && onnot[i]==f){
+ onnot[i] = 0;
+ ret = 1;
+ }else
+ n++;
+ }
+ if(n == 0){
+ init = 0;
+ notify(0);
+ }
+ }
+ unlock(&onnotlock);
+ return ret;
+}
diff --git a/sys/src/libc/port/atof.c b/sys/src/libc/port/atof.c
new file mode 100755
index 000000000..ed8cbeeb8
--- /dev/null
+++ b/sys/src/libc/port/atof.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+double
+atof(char *cp)
+{
+ return strtod(cp, 0);
+}
diff --git a/sys/src/libc/port/atol.c b/sys/src/libc/port/atol.c
new file mode 100755
index 000000000..6928da7bd
--- /dev/null
+++ b/sys/src/libc/port/atol.c
@@ -0,0 +1,53 @@
+#include <u.h>
+#include <libc.h>
+
+long
+atol(char *s)
+{
+ long n;
+ int f, c;
+
+ n = 0;
+ f = 0;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ if(*s == '-' || *s == '+') {
+ if(*s++ == '-')
+ f = 1;
+ while(*s == ' ' || *s == '\t')
+ s++;
+ }
+ if(s[0]=='0' && s[1]) {
+ if(s[1]=='x' || s[1]=='X'){
+ s += 2;
+ for(;;) {
+ c = *s;
+ if(c >= '0' && c <= '9')
+ n = n*16 + c - '0';
+ else
+ if(c >= 'a' && c <= 'f')
+ n = n*16 + c - 'a' + 10;
+ else
+ if(c >= 'A' && c <= 'F')
+ n = n*16 + c - 'A' + 10;
+ else
+ break;
+ s++;
+ }
+ } else
+ while(*s >= '0' && *s <= '7')
+ n = n*8 + *s++ - '0';
+ } else
+ while(*s >= '0' && *s <= '9')
+ n = n*10 + *s++ - '0';
+ if(f)
+ n = -n;
+ return n;
+}
+
+int
+atoi(char *s)
+{
+
+ return atol(s);
+}
diff --git a/sys/src/libc/port/atoll.c b/sys/src/libc/port/atoll.c
new file mode 100755
index 000000000..b4497de59
--- /dev/null
+++ b/sys/src/libc/port/atoll.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+vlong
+atoll(char *s)
+{
+ return strtoll(s, nil, 0);
+}
diff --git a/sys/src/libc/port/charstod.c b/sys/src/libc/port/charstod.c
new file mode 100755
index 000000000..0df775481
--- /dev/null
+++ b/sys/src/libc/port/charstod.c
@@ -0,0 +1,81 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Reads a floating-point number by interpreting successive characters
+ * returned by (*f)(vp). The last call it makes to f terminates the
+ * scan, so is not a character in the number. It may therefore be
+ * necessary to back up the input stream up one byte after calling charstod.
+ */
+
+#define ADVANCE *s++ = c; if(s>=e) return NaN(); c = (*f)(vp)
+
+double
+charstod(int(*f)(void*), void *vp)
+{
+ char str[400], *s, *e, *start;
+ int c;
+
+ s = str;
+ e = str + sizeof str - 1;
+ c = (*f)(vp);
+ while(c == ' ' || c == '\t')
+ c = (*f)(vp);
+ if(c == '-' || c == '+'){
+ ADVANCE;
+ }
+ start = s;
+ while(c >= '0' && c <= '9'){
+ ADVANCE;
+ }
+ if(c == '.'){
+ ADVANCE;
+ while(c >= '0' && c <= '9'){
+ ADVANCE;
+ }
+ }
+ if(s > start && (c == 'e' || c == 'E')){
+ ADVANCE;
+ if(c == '-' || c == '+'){
+ ADVANCE;
+ }
+ while(c >= '0' && c <= '9'){
+ ADVANCE;
+ }
+ }else if(s == start && (c == 'i' || c == 'I')){
+ ADVANCE;
+ if(c != 'n' && c != 'N')
+ return NaN();
+ ADVANCE;
+ if(c != 'f' && c != 'F')
+ return NaN();
+ ADVANCE;
+ if(c != 'i' && c != 'I')
+ return NaN();
+ ADVANCE;
+ if(c != 'n' && c != 'N')
+ return NaN();
+ ADVANCE;
+ if(c != 'i' && c != 'I')
+ return NaN();
+ ADVANCE;
+ if(c != 't' && c != 'T')
+ return NaN();
+ ADVANCE;
+ if(c != 'y' && c != 'Y')
+ return NaN();
+ ADVANCE; /* so caller can back up uniformly */
+ USED(c);
+ }else if(s == str && (c == 'n' || c == 'N')){
+ ADVANCE;
+ if(c != 'a' && c != 'A')
+ return NaN();
+ ADVANCE;
+ if(c != 'n' && c != 'N')
+ return NaN();
+ ADVANCE; /* so caller can back up uniformly */
+ USED(c);
+ }
+ *s = 0;
+ return strtod(str, &s);
+}
diff --git a/sys/src/libc/port/cistrcmp.c b/sys/src/libc/port/cistrcmp.c
new file mode 100755
index 000000000..a545615b7
--- /dev/null
+++ b/sys/src/libc/port/cistrcmp.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+int
+cistrcmp(char *s1, char *s2)
+{
+ int c1, c2;
+
+ while(*s1){
+ c1 = *(uchar*)s1++;
+ c2 = *(uchar*)s2++;
+
+ if(c1 == c2)
+ continue;
+
+ if(c1 >= 'A' && c1 <= 'Z')
+ c1 -= 'A' - 'a';
+
+ if(c2 >= 'A' && c2 <= 'Z')
+ c2 -= 'A' - 'a';
+
+ if(c1 != c2)
+ return c1 - c2;
+ }
+ return -*s2;
+}
diff --git a/sys/src/libc/port/cistrncmp.c b/sys/src/libc/port/cistrncmp.c
new file mode 100755
index 000000000..8f24d411f
--- /dev/null
+++ b/sys/src/libc/port/cistrncmp.c
@@ -0,0 +1,28 @@
+#include <u.h>
+#include <libc.h>
+
+int
+cistrncmp(char *s1, char *s2, int n)
+{
+ int c1, c2;
+
+ while(*s1 && n-- > 0){
+ c1 = *(uchar*)s1++;
+ c2 = *(uchar*)s2++;
+
+ if(c1 == c2)
+ continue;
+
+ if(c1 >= 'A' && c1 <= 'Z')
+ c1 -= 'A' - 'a';
+
+ if(c2 >= 'A' && c2 <= 'Z')
+ c2 -= 'A' - 'a';
+
+ if(c1 != c2)
+ return c1 - c2;
+ }
+ if(n <= 0)
+ return 0;
+ return -*s2;
+}
diff --git a/sys/src/libc/port/cistrstr.c b/sys/src/libc/port/cistrstr.c
new file mode 100755
index 000000000..0a1132266
--- /dev/null
+++ b/sys/src/libc/port/cistrstr.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+cistrstr(char *s, char *sub)
+{
+ int c, csub, n;
+
+ csub = *sub;
+ if(csub == '\0')
+ return s;
+ if(csub >= 'A' && csub <= 'Z')
+ csub -= 'A' - 'a';
+ sub++;
+ n = strlen(sub);
+ for(; c = *s; s++){
+ if(c >= 'A' && c <= 'Z')
+ c -= 'A' - 'a';
+ if(c == csub && cistrncmp(s+1, sub, n) == 0)
+ return s;
+ }
+ return nil;
+}
diff --git a/sys/src/libc/port/cleanname.c b/sys/src/libc/port/cleanname.c
new file mode 100755
index 000000000..1dae7bb84
--- /dev/null
+++ b/sys/src/libc/port/cleanname.c
@@ -0,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+#define SEP(x) ((x)=='/' || (x) == 0)
+char*
+cleanname(char *name)
+{
+ char *p, *q, *dotdot;
+ int rooted, erasedprefix;
+
+ rooted = name[0] == '/';
+ erasedprefix = 0;
+
+ /*
+ * invariants:
+ * p points at beginning of path element we're considering.
+ * q points just past the last path element we wrote (no slash).
+ * dotdot points just past the point where .. cannot backtrack
+ * any further (no slash).
+ */
+ p = q = dotdot = name+rooted;
+ while(*p) {
+ if(p[0] == '/') /* null element */
+ p++;
+ else if(p[0] == '.' && SEP(p[1])) {
+ if(p == name)
+ erasedprefix = 1;
+ p += 1; /* don't count the separator in case it is nul */
+ } else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
+ p += 2;
+ if(q > dotdot) { /* can backtrack */
+ while(--q > dotdot && *q != '/')
+ ;
+ } else if(!rooted) { /* /.. is / but ./../ is .. */
+ if(q != name)
+ *q++ = '/';
+ *q++ = '.';
+ *q++ = '.';
+ dotdot = q;
+ }
+ if(q == name)
+ erasedprefix = 1; /* erased entire path via dotdot */
+ } else { /* real path element */
+ if(q != name+rooted)
+ *q++ = '/';
+ while((*q = *p) != '/' && *q != 0)
+ p++, q++;
+ }
+ }
+ if(q == name) /* empty string is really ``.'' */
+ *q++ = '.';
+ *q = '\0';
+ if(erasedprefix && name[0] == '#'){
+ /* this was not a #x device path originally - make it not one now */
+ memmove(name+2, name, strlen(name)+1);
+ name[0] = '.';
+ name[1] = '/';
+ }
+ return name;
+}
diff --git a/sys/src/libc/port/crypt.c b/sys/src/libc/port/crypt.c
new file mode 100755
index 000000000..0524c9422
--- /dev/null
+++ b/sys/src/libc/port/crypt.c
@@ -0,0 +1,68 @@
+/*
+ * Data Encryption Standard
+ * D.P.Mitchell 83/06/08.
+ *
+ * block_cipher(key, block, decrypting)
+ *
+ * these routines use the non-standard 7 byte format
+ * for DES keys.
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <libsec.h>
+
+/*
+ * destructively encrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+int
+encrypt(void *key, void *vbuf, int n)
+{
+ ulong ekey[32];
+ uchar *buf;
+ int i, r;
+
+ if(n < 8)
+ return 0;
+ key_setup(key, ekey);
+ buf = vbuf;
+ n--;
+ r = n % 7;
+ n /= 7;
+ for(i = 0; i < n; i++){
+ block_cipher(ekey, buf, 0);
+ buf += 7;
+ }
+ if(r)
+ block_cipher(ekey, buf - 7 + r, 0);
+ return 1;
+}
+
+/*
+ * destructively decrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+int
+decrypt(void *key, void *vbuf, int n)
+{
+ ulong ekey[128];
+ uchar *buf;
+ int i, r;
+
+ if(n < 8)
+ return 0;
+ key_setup(key, ekey);
+ buf = vbuf;
+ n--;
+ r = n % 7;
+ n /= 7;
+ buf += n * 7;
+ if(r)
+ block_cipher(ekey, buf - 7 + r, 1);
+ for(i = 0; i < n; i++){
+ buf -= 7;
+ block_cipher(ekey, buf, 1);
+ }
+ return 1;
+}
diff --git a/sys/src/libc/port/ctype.c b/sys/src/libc/port/ctype.c
new file mode 100755
index 000000000..cfe1ed48a
--- /dev/null
+++ b/sys/src/libc/port/ctype.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+uchar _ctype[256] =
+{
+/* 0 1 2 3 4 5 6 7 */
+
+/* 0*/ _C, _C, _C, _C, _C, _C, _C, _C,
+/* 10*/ _C, _S|_C, _S|_C, _S|_C, _S|_C, _S|_C, _C, _C,
+/* 20*/ _C, _C, _C, _C, _C, _C, _C, _C,
+/* 30*/ _C, _C, _C, _C, _C, _C, _C, _C,
+/* 40*/ _S|_B, _P, _P, _P, _P, _P, _P, _P,
+/* 50*/ _P, _P, _P, _P, _P, _P, _P, _P,
+/* 60*/ _N|_X, _N|_X, _N|_X, _N|_X, _N|_X, _N|_X, _N|_X, _N|_X,
+/* 70*/ _N|_X, _N|_X, _P, _P, _P, _P, _P, _P,
+/*100*/ _P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U,
+/*110*/ _U, _U, _U, _U, _U, _U, _U, _U,
+/*120*/ _U, _U, _U, _U, _U, _U, _U, _U,
+/*130*/ _U, _U, _U, _P, _P, _P, _P, _P,
+/*140*/ _P, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L,
+/*150*/ _L, _L, _L, _L, _L, _L, _L, _L,
+/*160*/ _L, _L, _L, _L, _L, _L, _L, _L,
+/*170*/ _L, _L, _L, _P, _P, _P, _P, _C,
+};
diff --git a/sys/src/libc/port/encodefmt.c b/sys/src/libc/port/encodefmt.c
new file mode 100755
index 000000000..721a58c9f
--- /dev/null
+++ b/sys/src/libc/port/encodefmt.c
@@ -0,0 +1,77 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+int
+encodefmt(Fmt *f)
+{
+ char *out;
+ char *buf;
+ int len;
+ int ilen;
+ int rv;
+ uchar *b;
+ char *p;
+ char obuf[64]; // rsc optimization
+
+ if(!(f->flags&FmtPrec) || f->prec < 1)
+ goto error;
+
+ b = va_arg(f->args, uchar*);
+ if(b == 0)
+ return fmtstrcpy(f, "<nil>");
+
+ ilen = f->prec;
+ f->prec = 0;
+ f->flags &= ~FmtPrec;
+ switch(f->r){
+ case '<':
+ len = (8*ilen+4)/5 + 3;
+ break;
+ case '[':
+ len = (8*ilen+5)/6 + 4;
+ break;
+ case 'H':
+ len = 2*ilen + 1;
+ break;
+ default:
+ goto error;
+ }
+
+ if(len > sizeof(obuf)){
+ buf = malloc(len);
+ if(buf == nil)
+ goto error;
+ } else
+ buf = obuf;
+
+ // convert
+ out = buf;
+ switch(f->r){
+ case '<':
+ rv = enc32(out, len, b, ilen);
+ break;
+ case '[':
+ rv = enc64(out, len, b, ilen);
+ break;
+ case 'H':
+ rv = enc16(out, len, b, ilen);
+ if(rv >= 0 && (f->flags & FmtLong))
+ for(p = buf; *p; p++)
+ *p = tolower(*p);
+ break;
+ default:
+ rv = -1;
+ break;
+ }
+ if(rv < 0)
+ goto error;
+
+ fmtstrcpy(f, buf);
+ if(buf != obuf)
+ free(buf);
+ return 0;
+
+error:
+ return fmtstrcpy(f, "<encodefmt>");
+}
diff --git a/sys/src/libc/port/execl.c b/sys/src/libc/port/execl.c
new file mode 100755
index 000000000..40574fe63
--- /dev/null
+++ b/sys/src/libc/port/execl.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+int
+execl(char *f, ...)
+{
+
+ return exec(f, &f+1);
+}
diff --git a/sys/src/libc/port/exp.c b/sys/src/libc/port/exp.c
new file mode 100755
index 000000000..0a3e9e444
--- /dev/null
+++ b/sys/src/libc/port/exp.c
@@ -0,0 +1,40 @@
+/*
+ exp returns the exponential function of its
+ floating-point argument.
+
+ The coefficients are #1069 from Hart and Cheney. (22.35D)
+*/
+
+#include <u.h>
+#include <libc.h>
+
+#define p0 .2080384346694663001443843411e7
+#define p1 .3028697169744036299076048876e5
+#define p2 .6061485330061080841615584556e2
+#define q0 .6002720360238832528230907598e7
+#define q1 .3277251518082914423057964422e6
+#define q2 .1749287689093076403844945335e4
+#define log2e 1.4426950408889634073599247
+#define sqrt2 1.4142135623730950488016887
+#define maxf 10000
+
+double
+exp(double arg)
+{
+ double fract, temp1, temp2, xsq;
+ int ent;
+
+ if(arg == 0)
+ return 1;
+ if(arg < -maxf)
+ return 0;
+ if(arg > maxf)
+ return Inf(1);
+ arg *= log2e;
+ ent = floor(arg);
+ fract = (arg-ent) - 0.5;
+ xsq = fract*fract;
+ temp1 = ((p2*xsq+p1)*xsq+p0)*fract;
+ temp2 = ((xsq+q2)*xsq+q1)*xsq + q0;
+ return ldexp(sqrt2*(temp2+temp1)/(temp2-temp1), ent);
+}
diff --git a/sys/src/libc/port/fabs.c b/sys/src/libc/port/fabs.c
new file mode 100755
index 000000000..9e90a6786
--- /dev/null
+++ b/sys/src/libc/port/fabs.c
@@ -0,0 +1,11 @@
+#include <u.h>
+#include <libc.h>
+
+double
+fabs(double arg)
+{
+
+ if(arg < 0)
+ return -arg;
+ return arg;
+}
diff --git a/sys/src/libc/port/floor.c b/sys/src/libc/port/floor.c
new file mode 100755
index 000000000..e1ee76489
--- /dev/null
+++ b/sys/src/libc/port/floor.c
@@ -0,0 +1,27 @@
+#include <u.h>
+#include <libc.h>
+/*
+ * floor and ceil-- greatest integer <= arg
+ * (resp least >=)
+ */
+
+double
+floor(double d)
+{
+ double fract;
+
+ if(d < 0) {
+ fract = modf(-d, &d);
+ if(fract != 0.0)
+ d += 1;
+ d = -d;
+ } else
+ modf(d, &d);
+ return d;
+}
+
+double
+ceil(double d)
+{
+ return -floor(-d);
+}
diff --git a/sys/src/libc/port/fmod.c b/sys/src/libc/port/fmod.c
new file mode 100755
index 000000000..3b97a5b43
--- /dev/null
+++ b/sys/src/libc/port/fmod.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * floating-point mod function without infinity or NaN checking
+ */
+double
+fmod (double x, double y)
+{
+ int sign, yexp, rexp;
+ double r, yfr, rfr;
+
+ if (y == 0)
+ return x;
+ if (y < 0)
+ y = -y;
+ yfr = frexp(y, &yexp);
+ sign = 0;
+ if(x < 0) {
+ r = -x;
+ sign++;
+ } else
+ r = x;
+ while(r >= y) {
+ rfr = frexp(r, &rexp);
+ r -= ldexp(y, rexp - yexp - (rfr < yfr));
+ }
+ if(sign)
+ r = -r;
+ return r;
+}
diff --git a/sys/src/libc/port/frand.c b/sys/src/libc/port/frand.c
new file mode 100755
index 000000000..e13e1d860
--- /dev/null
+++ b/sys/src/libc/port/frand.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+#define MASK 0x7fffffffL
+#define NORM (1.0/(1.0+MASK))
+
+double
+frand(void)
+{
+ double x;
+
+ do {
+ x = lrand() * NORM;
+ x = (x + lrand()) * NORM;
+ } while(x >= 1);
+ return x;
+}
diff --git a/sys/src/libc/port/frexp.c b/sys/src/libc/port/frexp.c
new file mode 100755
index 000000000..7809bd151
--- /dev/null
+++ b/sys/src/libc/port/frexp.c
@@ -0,0 +1,105 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * this is big/little endian non-portable
+ * it gets the endian from the FPdbleword
+ * union in u.h.
+ */
+#define MASK 0x7ffL
+#define SHIFT 20
+#define BIAS 1022L
+
+double
+frexp(double d, int *ep)
+{
+ FPdbleword x;
+
+ if(d == 0) {
+ *ep = 0;
+ return 0;
+ }
+ x.x = d;
+ *ep = ((x.hi >> SHIFT) & MASK) - BIAS;
+ x.hi &= ~(MASK << SHIFT);
+ x.hi |= BIAS << SHIFT;
+ return x.x;
+}
+
+double
+ldexp(double d, int deltae)
+{
+ int e, bits;
+ FPdbleword x;
+ ulong z;
+
+ if(d == 0)
+ return 0;
+ x.x = d;
+ e = (x.hi >> SHIFT) & MASK;
+ if(deltae >= 0 || e+deltae >= 1){ /* no underflow */
+ e += deltae;
+ if(e >= MASK){ /* overflow */
+ if(d < 0)
+ return Inf(-1);
+ return Inf(1);
+ }
+ }else{ /* underflow gracefully */
+ deltae = -deltae;
+ /* need to shift d right deltae */
+ if(e > 1){ /* shift e-1 by exponent manipulation */
+ deltae -= e-1;
+ e = 1;
+ }
+ if(deltae > 0 && e==1){ /* shift 1 by switch from 1.fff to 0.1ff */
+ deltae--;
+ e = 0;
+ x.lo >>= 1;
+ x.lo |= (x.hi&1)<<31;
+ z = x.hi & ((1<<SHIFT)-1);
+ x.hi &= ~((1<<SHIFT)-1);
+ x.hi |= (1<<(SHIFT-1)) | (z>>1);
+ }
+ while(deltae > 0){ /* shift bits down */
+ bits = deltae;
+ if(bits > SHIFT)
+ bits = SHIFT;
+ x.lo >>= bits;
+ x.lo |= (x.hi&((1<<bits)-1)) << (32-bits);
+ z = x.hi & ((1<<SHIFT)-1);
+ x.hi &= ~((1<<SHIFT)-1);
+ x.hi |= z>>bits;
+ deltae -= bits;
+ }
+ }
+ x.hi &= ~(MASK << SHIFT);
+ x.hi |= (long)e << SHIFT;
+ return x.x;
+}
+
+double
+modf(double d, double *ip)
+{
+ FPdbleword x;
+ int e;
+
+ if(d < 1) {
+ if(d < 0) {
+ x.x = modf(-d, ip);
+ *ip = -*ip;
+ return -x.x;
+ }
+ *ip = 0;
+ return d;
+ }
+ x.x = d;
+ e = ((x.hi >> SHIFT) & MASK) - BIAS;
+ if(e <= SHIFT+1) {
+ x.hi &= ~(0x1fffffL >> e);
+ x.lo = 0;
+ } else
+ if(e <= SHIFT+33)
+ x.lo &= ~(0x7fffffffL >> (e-SHIFT-2));
+ *ip = x.x;
+ return d - x.x;
+}
diff --git a/sys/src/libc/port/getcallerpc.c b/sys/src/libc/port/getcallerpc.c
new file mode 100755
index 000000000..f45f8c00c
--- /dev/null
+++ b/sys/src/libc/port/getcallerpc.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+uintptr
+getcallerpc(void*)
+{
+ return 0;
+}
diff --git a/sys/src/libc/port/getfields.c b/sys/src/libc/port/getfields.c
new file mode 100755
index 000000000..b12c0cc2b
--- /dev/null
+++ b/sys/src/libc/port/getfields.c
@@ -0,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+
+int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+ Rune r;
+ int nr, intok, narg;
+
+ if(max <= 0)
+ return 0;
+
+ narg = 0;
+ args[narg] = str;
+ if(!mflag)
+ narg++;
+ intok = 0;
+ for(;; str += nr) {
+ nr = chartorune(&r, str);
+ if(r == 0)
+ break;
+ if(utfrune(set, r)) {
+ if(narg >= max)
+ break;
+ *str = 0;
+ intok = 0;
+ args[narg] = str + nr;
+ if(!mflag)
+ narg++;
+ } else {
+ if(!intok && mflag)
+ narg++;
+ intok = 1;
+ }
+ }
+ return narg;
+}
diff --git a/sys/src/libc/port/getuser.c b/sys/src/libc/port/getuser.c
new file mode 100755
index 000000000..a987244ee
--- /dev/null
+++ b/sys/src/libc/port/getuser.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+char *
+getuser(void)
+{
+ static char user[64];
+ int fd;
+ int n;
+
+ fd = open("/dev/user", OREAD);
+ if(fd < 0)
+ return "none";
+ n = read(fd, user, (sizeof user)-1);
+ close(fd);
+ if(n <= 0)
+ strcpy(user, "none");
+ else
+ user[n] = 0;
+ return user;
+}
diff --git a/sys/src/libc/port/hangup.c b/sys/src/libc/port/hangup.c
new file mode 100755
index 000000000..83d0735cb
--- /dev/null
+++ b/sys/src/libc/port/hangup.c
@@ -0,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/*
+ * force a connection to hangup
+ */
+int
+hangup(int ctl)
+{
+ return write(ctl, "hangup", sizeof("hangup")-1) != sizeof("hangup")-1;
+}
diff --git a/sys/src/libc/port/hypot.c b/sys/src/libc/port/hypot.c
new file mode 100755
index 000000000..cb8a33341
--- /dev/null
+++ b/sys/src/libc/port/hypot.c
@@ -0,0 +1,41 @@
+/*
+ * hypot -- sqrt(p*p+q*q), but overflows only if the result does.
+ * See Cleve Moler and Donald Morrison,
+ * ``Replacing Square Roots by Pythagorean Sums,''
+ * IBM Journal of Research and Development,
+ * Vol. 27, Number 6, pp. 577-581, Nov. 1983
+ */
+
+#include <u.h>
+#include <libc.h>
+
+double
+hypot(double p, double q)
+{
+ double r, s, pfac;
+
+ if(p < 0)
+ p = -p;
+ if(q < 0)
+ q = -q;
+ if(p < q) {
+ r = p;
+ p = q;
+ q = r;
+ }
+ if(p == 0)
+ return 0;
+ pfac = p;
+ r = q = q/p;
+ p = 1;
+ for(;;) {
+ r *= r;
+ s = r+4;
+ if(s == 4)
+ return p*pfac;
+ r /= s;
+ p += 2*r*p;
+ q *= r;
+ r = q/p;
+ }
+}
diff --git a/sys/src/libc/port/lnrand.c b/sys/src/libc/port/lnrand.c
new file mode 100755
index 000000000..5b648d08c
--- /dev/null
+++ b/sys/src/libc/port/lnrand.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+#define MASK 0x7fffffffL
+
+long
+lnrand(long n)
+{
+ long slop, v;
+
+ if(n < 0)
+ return n;
+ slop = MASK % n;
+ do
+ v = lrand();
+ while(v <= slop);
+ return v % n;
+}
diff --git a/sys/src/libc/port/lock.c b/sys/src/libc/port/lock.c
new file mode 100755
index 000000000..af07b9cc7
--- /dev/null
+++ b/sys/src/libc/port/lock.c
@@ -0,0 +1,41 @@
+#include <u.h>
+#include <libc.h>
+
+void
+lock(Lock *lk)
+{
+ int i;
+
+ /* once fast */
+ if(!_tas(&lk->val))
+ return;
+ /* a thousand times pretty fast */
+ for(i=0; i<1000; i++){
+ if(!_tas(&lk->val))
+ return;
+ sleep(0);
+ }
+ /* now nice and slow */
+ for(i=0; i<1000; i++){
+ if(!_tas(&lk->val))
+ return;
+ sleep(100);
+ }
+ /* take your time */
+ while(_tas(&lk->val))
+ sleep(1000);
+}
+
+int
+canlock(Lock *lk)
+{
+ if(_tas(&lk->val))
+ return 0;
+ return 1;
+}
+
+void
+unlock(Lock *lk)
+{
+ lk->val = 0;
+}
diff --git a/sys/src/libc/port/log.c b/sys/src/libc/port/log.c
new file mode 100755
index 000000000..dd84b4cfc
--- /dev/null
+++ b/sys/src/libc/port/log.c
@@ -0,0 +1,58 @@
+/*
+ log returns the natural logarithm of its floating
+ point argument.
+
+ The coefficients are #2705 from Hart & Cheney. (19.38D)
+
+ It calls frexp.
+*/
+
+#include <u.h>
+#include <libc.h>
+
+#define log2 0.693147180559945309e0
+#define ln10o1 .4342944819032518276511
+#define sqrto2 0.707106781186547524e0
+#define p0 -.240139179559210510e2
+#define p1 0.309572928215376501e2
+#define p2 -.963769093377840513e1
+#define p3 0.421087371217979714e0
+#define q0 -.120069589779605255e2
+#define q1 0.194809660700889731e2
+#define q2 -.891110902798312337e1
+
+double
+log(double arg)
+{
+ double x, z, zsq, temp;
+ int exp;
+
+ if(arg <= 0)
+ return NaN();
+ x = frexp(arg, &exp);
+ while(x < 0.5) {
+ x *= 2;
+ exp--;
+ }
+ if(x < sqrto2) {
+ x *= 2;
+ exp--;
+ }
+
+ z = (x-1) / (x+1);
+ zsq = z*z;
+
+ temp = ((p3*zsq + p2)*zsq + p1)*zsq + p0;
+ temp = temp/(((zsq + q2)*zsq + q1)*zsq + q0);
+ temp = temp*z + exp*log2;
+ return temp;
+}
+
+double
+log10(double arg)
+{
+
+ if(arg <= 0)
+ return NaN();
+ return log(arg) * ln10o1;
+}
diff --git a/sys/src/libc/port/lrand.c b/sys/src/libc/port/lrand.c
new file mode 100755
index 000000000..2ebb39622
--- /dev/null
+++ b/sys/src/libc/port/lrand.c
@@ -0,0 +1,83 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * algorithm by
+ * D. P. Mitchell & J. A. Reeds
+ */
+
+#define LEN 607
+#define TAP 273
+#define MASK 0x7fffffffL
+#define A 48271
+#define M 2147483647
+#define Q 44488
+#define R 3399
+#define NORM (1.0/(1.0+MASK))
+
+static ulong rng_vec[LEN];
+static ulong* rng_tap = rng_vec;
+static ulong* rng_feed = 0;
+static Lock lk;
+
+static void
+isrand(long seed)
+{
+ long lo, hi, x;
+ int i;
+
+ rng_tap = rng_vec;
+ rng_feed = rng_vec+LEN-TAP;
+ seed = seed%M;
+ if(seed < 0)
+ seed += M;
+ if(seed == 0)
+ seed = 89482311;
+ x = seed;
+ /*
+ * Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1)
+ */
+ for(i = -20; i < LEN; i++) {
+ hi = x / Q;
+ lo = x % Q;
+ x = A*lo - R*hi;
+ if(x < 0)
+ x += M;
+ if(i >= 0)
+ rng_vec[i] = x;
+ }
+}
+
+void
+srand(long seed)
+{
+ lock(&lk);
+ isrand(seed);
+ unlock(&lk);
+}
+
+long
+lrand(void)
+{
+ ulong x;
+
+ lock(&lk);
+
+ rng_tap--;
+ if(rng_tap < rng_vec) {
+ if(rng_feed == 0) {
+ isrand(1);
+ rng_tap--;
+ }
+ rng_tap += LEN;
+ }
+ rng_feed--;
+ if(rng_feed < rng_vec)
+ rng_feed += LEN;
+ x = (*rng_feed + *rng_tap) & MASK;
+ *rng_feed = x;
+
+ unlock(&lk);
+
+ return x;
+}
diff --git a/sys/src/libc/port/malloc.acid b/sys/src/libc/port/malloc.acid
new file mode 100755
index 000000000..514bb8d98
--- /dev/null
+++ b/sys/src/libc/port/malloc.acid
@@ -0,0 +1,366 @@
+sizeof_1_ = 8;
+aggr _1_
+{
+ 'D' 0 lo;
+ 'D' 4 hi;
+};
+
+defn
+_1_(addr) {
+ complex _1_ addr;
+ print(" lo ", addr.lo, "\n");
+ print(" hi ", addr.hi, "\n");
+};
+
+sizeofFPdbleword = 8;
+aggr FPdbleword
+{
+ 'F' 0 x;
+ {
+ 'D' 0 lo;
+ 'D' 4 hi;
+ };
+};
+
+defn
+FPdbleword(addr) {
+ complex FPdbleword addr;
+ print(" x ", addr.x, "\n");
+ print("_1_ {\n");
+ _1_(addr+0);
+ print("}\n");
+};
+
+UTFmax = 3;
+Runesync = 128;
+Runeself = 128;
+Runeerror = 128;
+sizeofFconv = 24;
+aggr Fconv
+{
+ 'X' 0 out;
+ 'X' 4 eout;
+ 'D' 8 f1;
+ 'D' 12 f2;
+ 'D' 16 f3;
+ 'D' 20 chr;
+};
+
+defn
+Fconv(addr) {
+ complex Fconv addr;
+ print(" out ", addr.out\X, "\n");
+ print(" eout ", addr.eout\X, "\n");
+ print(" f1 ", addr.f1, "\n");
+ print(" f2 ", addr.f2, "\n");
+ print(" f3 ", addr.f3, "\n");
+ print(" chr ", addr.chr, "\n");
+};
+
+sizeofTm = 40;
+aggr Tm
+{
+ 'D' 0 sec;
+ 'D' 4 min;
+ 'D' 8 hour;
+ 'D' 12 mday;
+ 'D' 16 mon;
+ 'D' 20 year;
+ 'D' 24 wday;
+ 'D' 28 yday;
+ 'a' 32 zone;
+ 'D' 36 tzoff;
+};
+
+defn
+Tm(addr) {
+ complex Tm addr;
+ print(" sec ", addr.sec, "\n");
+ print(" min ", addr.min, "\n");
+ print(" hour ", addr.hour, "\n");
+ print(" mday ", addr.mday, "\n");
+ print(" mon ", addr.mon, "\n");
+ print(" year ", addr.year, "\n");
+ print(" wday ", addr.wday, "\n");
+ print(" yday ", addr.yday, "\n");
+ print(" zone ", addr.zone, "\n");
+ print(" tzoff ", addr.tzoff, "\n");
+};
+
+PNPROC = 1;
+PNGROUP = 2;
+sizeofLock = 4;
+aggr Lock
+{
+ 'D' 0 val;
+};
+
+defn
+Lock(addr) {
+ complex Lock addr;
+ print(" val ", addr.val, "\n");
+};
+
+sizeofQLp = 12;
+aggr QLp
+{
+ 'D' 0 inuse;
+ 'A' QLp 4 next;
+ 'C' 8 state;
+};
+
+defn
+QLp(addr) {
+ complex QLp addr;
+ print(" inuse ", addr.inuse, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" state ", addr.state, "\n");
+};
+
+sizeofQLock = 16;
+aggr QLock
+{
+ Lock 0 lock;
+ 'D' 4 locked;
+ 'A' QLp 8 $head;
+ 'A' QLp 12 $tail;
+};
+
+defn
+QLock(addr) {
+ complex QLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" locked ", addr.locked, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+sizeofRWLock = 20;
+aggr RWLock
+{
+ Lock 0 lock;
+ 'D' 4 readers;
+ 'D' 8 writer;
+ 'A' QLp 12 $head;
+ 'A' QLp 16 $tail;
+};
+
+defn
+RWLock(addr) {
+ complex RWLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" readers ", addr.readers, "\n");
+ print(" writer ", addr.writer, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+RFNAMEG = 1;
+RFENVG = 2;
+RFFDG = 4;
+RFNOTEG = 8;
+RFPROC = 16;
+RFMEM = 32;
+RFNOWAIT = 64;
+RFCNAMEG = 1024;
+RFCENVG = 2048;
+RFCFDG = 4096;
+RFREND = 8192;
+RFNOMNT = 16384;
+sizeofQid = 16;
+aggr Qid
+{
+ 'W' 0 path;
+ 'U' 8 vers;
+ 'b' 12 type;
+};
+
+defn
+Qid(addr) {
+ complex Qid addr;
+ print(" path ", addr.path, "\n");
+ print(" vers ", addr.vers, "\n");
+ print(" type ", addr.type, "\n");
+};
+
+sizeofDir = 60;
+aggr Dir
+{
+ 'u' 0 type;
+ 'U' 4 dev;
+ Qid 8 qid;
+ 'U' 24 mode;
+ 'U' 28 atime;
+ 'U' 32 mtime;
+ 'V' 36 length;
+ 'X' 44 name;
+ 'X' 48 uid;
+ 'X' 52 gid;
+ 'X' 56 muid;
+};
+
+defn
+Dir(addr) {
+ complex Dir addr;
+ print(" type ", addr.type, "\n");
+ print(" dev ", addr.dev, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" mode ", addr.mode, "\n");
+ print(" atime ", addr.atime, "\n");
+ print(" mtime ", addr.mtime, "\n");
+ print(" length ", addr.length, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" gid ", addr.gid\X, "\n");
+ print(" muid ", addr.muid\X, "\n");
+};
+
+sizeofWaitmsg = 20;
+aggr Waitmsg
+{
+ 'D' 0 pid;
+ 'a' 4 time;
+ 'X' 16 msg;
+};
+
+defn
+Waitmsg(addr) {
+ complex Waitmsg addr;
+ print(" pid ", addr.pid, "\n");
+ print(" time ", addr.time, "\n");
+ print(" msg ", addr.msg\X, "\n");
+};
+
+sizeofIOchunk = 8;
+aggr IOchunk
+{
+ 'X' 0 addr;
+ 'U' 4 len;
+};
+
+defn
+IOchunk(addr) {
+ complex IOchunk addr;
+ print(" addr ", addr.addr\X, "\n");
+ print(" len ", addr.len, "\n");
+};
+
+sizeofPool = 88;
+aggr Pool
+{
+ 'X' 0 name;
+ 'U' 4 maxsize;
+ 'U' 8 cursize;
+ 'U' 12 curfree;
+ 'U' 16 curalloc;
+ 'U' 20 minarena;
+ 'U' 24 quantum;
+ 'U' 28 minblock;
+ 'X' 32 freeroot;
+ 'X' 36 arenalist;
+ 'X' 40 alloc;
+ 'X' 44 merge;
+ 'X' 48 move;
+ 'D' 52 flags;
+ 'D' 56 nfree;
+ 'D' 60 lastcompact;
+ 'X' 64 lock;
+ 'X' 68 unlock;
+ 'X' 72 print;
+ 'X' 76 panic;
+ 'X' 80 logstack;
+ 'X' 84 private;
+};
+
+defn
+Pool(addr) {
+ complex Pool addr;
+ print(" name ", addr.name\X, "\n");
+ print(" maxsize ", addr.maxsize, "\n");
+ print(" cursize ", addr.cursize, "\n");
+ print(" curfree ", addr.curfree, "\n");
+ print(" curalloc ", addr.curalloc, "\n");
+ print(" minarena ", addr.minarena, "\n");
+ print(" quantum ", addr.quantum, "\n");
+ print(" minblock ", addr.minblock, "\n");
+ print(" freeroot ", addr.freeroot\X, "\n");
+ print(" arenalist ", addr.arenalist\X, "\n");
+ print(" alloc ", addr.alloc\X, "\n");
+ print(" merge ", addr.merge\X, "\n");
+ print(" move ", addr.move\X, "\n");
+ print(" flags ", addr.flags, "\n");
+ print(" nfree ", addr.nfree, "\n");
+ print(" lastcompact ", addr.lastcompact, "\n");
+ print(" lock ", addr.lock\X, "\n");
+ print(" unlock ", addr.unlock\X, "\n");
+ print(" print ", addr.print\X, "\n");
+ print(" panic ", addr.panic\X, "\n");
+ print(" logstack ", addr.logstack\X, "\n");
+ print(" private ", addr.private\X, "\n");
+};
+
+sizeofTraverse = 20;
+aggr Traverse
+{
+ 'X' 0 visit;
+ 'D' 4 maxvisit;
+ 'X' 8 a;
+ 'X' 12 b;
+ 'X' 16 prev;
+};
+
+defn
+Traverse(addr) {
+ complex Traverse addr;
+ print(" visit ", addr.visit\X, "\n");
+ print(" maxvisit ", addr.maxvisit, "\n");
+ print(" a ", addr.a\X, "\n");
+ print(" b ", addr.b\X, "\n");
+ print(" prev ", addr.prev\X, "\n");
+};
+
+complex Pool mainmem;
+complex Pool imagmem;
+POOL_ANTAGONISM = 1;
+POOL_PARANOIA = 2;
+POOL_VERBOSITY = 4;
+POOL_DEBUGGING = 8;
+POOL_LOGGING = 16;
+POOL_TOLERANCE = 32;
+sizeofPrivate = 8;
+aggr Private
+{
+ Lock 0 lk;
+ 'D' 4 printfd;
+};
+
+defn
+Private(addr) {
+ complex Private addr;
+ print("Lock lk {\n");
+ Lock(addr.lk);
+ print("}\n");
+ print(" printfd ", addr.printfd, "\n");
+};
+
+complex Private sbrkmempriv;
+complex Pool sbrkmem;
+complex Pool mainmem;
+complex Pool imagmem;
+complex Pool plock:p;
+complex Private plock:pv;
+complex Pool punlock:p;
+complex Private punlock:pv;
+complex Pool pprint:p;
+complex Private pprint:pv;
+complex Pool ppanic:p;
+complex Private ppanic:pv;
+Npadlong = 2;
+MallocOffset = 0;
+ReallocOffset = 1;
diff --git a/sys/src/libc/port/malloc.c b/sys/src/libc/port/malloc.c
new file mode 100755
index 000000000..741316926
--- /dev/null
+++ b/sys/src/libc/port/malloc.c
@@ -0,0 +1,338 @@
+#include <u.h>
+#include <libc.h>
+#include <pool.h>
+#include <tos.h>
+
+static void* sbrkalloc(ulong);
+static int sbrkmerge(void*, void*);
+static void plock(Pool*);
+static void punlock(Pool*);
+static void pprint(Pool*, char*, ...);
+static void ppanic(Pool*, char*, ...);
+
+typedef struct Private Private;
+struct Private {
+ Lock lk;
+ int pid;
+ int printfd; /* gets debugging output if set */
+};
+
+Private sbrkmempriv;
+
+static Pool sbrkmem = {
+ .name= "sbrkmem",
+ .maxsize= (3840UL-1)*1024*1024, /* up to ~0xf0000000 */
+ .minarena= 4*1024,
+ .quantum= 32,
+ .alloc= sbrkalloc,
+ .merge= sbrkmerge,
+ .flags= 0,
+
+ .lock= plock,
+ .unlock= punlock,
+ .print= pprint,
+ .panic= ppanic,
+ .private= &sbrkmempriv,
+};
+Pool *mainmem = &sbrkmem;
+Pool *imagmem = &sbrkmem;
+
+/*
+ * we do minimal bookkeeping so we can tell pool
+ * whether two blocks are adjacent and thus mergeable.
+ */
+static void*
+sbrkalloc(ulong n)
+{
+ ulong *x;
+
+ n += 2*sizeof(ulong); /* two longs for us */
+ x = sbrk(n);
+ if(x == (void*)-1)
+ return nil;
+ x[0] = (n+7)&~7; /* sbrk rounds size up to mult. of 8 */
+ x[1] = 0xDeadBeef;
+ return x+2;
+}
+
+static int
+sbrkmerge(void *x, void *y)
+{
+ ulong *lx, *ly;
+
+ lx = x;
+ if(lx[-1] != 0xDeadBeef)
+ abort();
+
+ if((uchar*)lx+lx[-2] == (uchar*)y) {
+ ly = y;
+ lx[-2] += ly[-2];
+ return 1;
+ }
+ return 0;
+}
+
+static void
+plock(Pool *p)
+{
+ Private *pv;
+ pv = p->private;
+ lock(&pv->lk);
+ if(pv->pid != 0)
+ abort();
+ pv->pid = _tos->pid;
+}
+
+static void
+punlock(Pool *p)
+{
+ Private *pv;
+ pv = p->private;
+ if(pv->pid != _tos->pid)
+ abort();
+ pv->pid = 0;
+ unlock(&pv->lk);
+}
+
+static int
+checkenv(void)
+{
+ int n, fd;
+ char buf[20];
+ fd = open("/env/MALLOCFD", OREAD);
+ if(fd < 0)
+ return -1;
+ if((n = read(fd, buf, sizeof buf)) < 0) {
+ close(fd);
+ return -1;
+ }
+ if(n >= sizeof buf)
+ n = sizeof(buf)-1;
+ buf[n] = 0;
+ n = atoi(buf);
+ if(n == 0)
+ n = -1;
+ return n;
+}
+
+static void
+pprint(Pool *p, char *fmt, ...)
+{
+ va_list v;
+ Private *pv;
+
+ pv = p->private;
+ if(pv->printfd == 0)
+ pv->printfd = checkenv();
+
+ if(pv->printfd <= 0)
+ pv->printfd = 2;
+
+ va_start(v, fmt);
+ vfprint(pv->printfd, fmt, v);
+ va_end(v);
+}
+
+static char panicbuf[256];
+static void
+ppanic(Pool *p, char *fmt, ...)
+{
+ va_list v;
+ int n;
+ char *msg;
+ Private *pv;
+
+ pv = p->private;
+ assert(canlock(&pv->lk)==0);
+
+ if(pv->printfd == 0)
+ pv->printfd = checkenv();
+ if(pv->printfd <= 0)
+ pv->printfd = 2;
+
+ msg = panicbuf;
+ va_start(v, fmt);
+ n = vseprint(msg, msg+sizeof panicbuf, fmt, v) - msg;
+ write(2, "panic: ", 7);
+ write(2, msg, n);
+ write(2, "\n", 1);
+ if(pv->printfd != 2){
+ write(pv->printfd, "panic: ", 7);
+ write(pv->printfd, msg, n);
+ write(pv->printfd, "\n", 1);
+ }
+ va_end(v);
+// unlock(&pv->lk);
+ abort();
+}
+
+/* - everything from here down should be the same in libc, libdebugmalloc, and the kernel - */
+/* - except the code for malloc(), which alternately doesn't clear or does. - */
+
+/*
+ * Npadlong is the number of 32-bit longs to leave at the beginning of
+ * each allocated buffer for our own bookkeeping. We return to the callers
+ * a pointer that points immediately after our bookkeeping area. Incoming pointers
+ * must be decremented by that much, and outgoing pointers incremented.
+ * The malloc tag is stored at MallocOffset from the beginning of the block,
+ * and the realloc tag at ReallocOffset. The offsets are from the true beginning
+ * of the block, not the beginning the caller sees.
+ *
+ * The extra if(Npadlong != 0) in various places is a hint for the compiler to
+ * compile out function calls that would otherwise be no-ops.
+ */
+
+/* non tracing
+ *
+enum {
+ Npadlong = 0,
+ MallocOffset = 0,
+ ReallocOffset = 0,
+};
+ *
+ */
+
+/* tracing */
+enum {
+ Npadlong = 2,
+ MallocOffset = 0,
+ ReallocOffset = 1
+};
+
+void*
+malloc(ulong size)
+{
+ void *v;
+
+ v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+ if(Npadlong && v != nil) {
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ return v;
+}
+
+void*
+mallocz(ulong size, int clr)
+{
+ void *v;
+
+ v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
+ if(Npadlong && v != nil){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ if(clr && v != nil)
+ memset(v, 0, size);
+ return v;
+}
+
+void*
+mallocalign(ulong size, ulong align, long offset, ulong span)
+{
+ void *v;
+
+ v = poolallocalign(mainmem, size+Npadlong*sizeof(ulong), align, offset-Npadlong*sizeof(ulong), span);
+ if(Npadlong && v != nil){
+ v = (ulong*)v+Npadlong;
+ setmalloctag(v, getcallerpc(&size));
+ setrealloctag(v, 0);
+ }
+ return v;
+}
+
+void
+free(void *v)
+{
+ if(v != nil)
+ poolfree(mainmem, (ulong*)v-Npadlong);
+}
+
+void*
+realloc(void *v, ulong size)
+{
+ void *nv;
+
+ if(size == 0){
+ free(v);
+ return nil;
+ }
+
+ if(v)
+ v = (ulong*)v-Npadlong;
+ size += Npadlong*sizeof(ulong);
+
+ if(nv = poolrealloc(mainmem, v, size)){
+ nv = (ulong*)nv+Npadlong;
+ setrealloctag(nv, getcallerpc(&v));
+ if(v == nil)
+ setmalloctag(nv, getcallerpc(&v));
+ }
+ return nv;
+}
+
+ulong
+msize(void *v)
+{
+ return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
+}
+
+void*
+calloc(ulong n, ulong szelem)
+{
+ void *v;
+ if(v = mallocz(n*szelem, 1))
+ setmalloctag(v, getcallerpc(&n));
+ return v;
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+ ulong *u;
+ USED(v, pc);
+ if(Npadlong <= MallocOffset || v == nil)
+ return;
+ u = v;
+ u[-Npadlong+MallocOffset] = pc;
+}
+
+void
+setrealloctag(void *v, ulong pc)
+{
+ ulong *u;
+ USED(v, pc);
+ if(Npadlong <= ReallocOffset || v == nil)
+ return;
+ u = v;
+ u[-Npadlong+ReallocOffset] = pc;
+}
+
+ulong
+getmalloctag(void *v)
+{
+ USED(v);
+ if(Npadlong <= MallocOffset)
+ return ~0;
+ return ((ulong*)v)[-Npadlong+MallocOffset];
+}
+
+ulong
+getrealloctag(void *v)
+{
+ USED(v);
+ if(Npadlong <= ReallocOffset)
+ return ((ulong*)v)[-Npadlong+ReallocOffset];
+ return ~0;
+}
+
+void*
+malloctopoolblock(void *v)
+{
+ if(v == nil)
+ return nil;
+
+ return &((ulong*)v)[-Npadlong];
+}
diff --git a/sys/src/libc/port/memccpy.c b/sys/src/libc/port/memccpy.c
new file mode 100755
index 000000000..9268ba72e
--- /dev/null
+++ b/sys/src/libc/port/memccpy.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+void*
+memccpy(void *a1, void *a2, int c, ulong n)
+{
+ uchar *s1, *s2;
+
+ s1 = a1;
+ s2 = a2;
+ c &= 0xFF;
+ while(n > 0) {
+ if((*s1++ = *s2++) == c)
+ return s1;
+ n--;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/memchr.c b/sys/src/libc/port/memchr.c
new file mode 100755
index 000000000..fb4b1d148
--- /dev/null
+++ b/sys/src/libc/port/memchr.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+void*
+memchr(void *ap, int c, ulong n)
+{
+ uchar *sp;
+
+ sp = ap;
+ c &= 0xFF;
+ while(n > 0) {
+ if(*sp++ == c)
+ return sp-1;
+ n--;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/memcmp.c b/sys/src/libc/port/memcmp.c
new file mode 100755
index 000000000..211752b03
--- /dev/null
+++ b/sys/src/libc/port/memcmp.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+
+int
+memcmp(void *a1, void *a2, ulong n)
+{
+ uchar *s1, *s2;
+ uint c1, c2;
+
+ s1 = a1;
+ s2 = a2;
+ while(n > 0) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ n--;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/memmove.c b/sys/src/libc/port/memmove.c
new file mode 100755
index 000000000..16ef43277
--- /dev/null
+++ b/sys/src/libc/port/memmove.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+
+void*
+memmove(void *a1, void *a2, ulong n)
+{
+ char *s1, *s2;
+
+ if((long)n < 0)
+ abort();
+ s1 = a1;
+ s2 = a2;
+ if((s2 < s1) && (s2+n > s1))
+ goto back;
+ while(n > 0) {
+ *s1++ = *s2++;
+ n--;
+ }
+ return a1;
+
+back:
+ s1 += n;
+ s2 += n;
+ while(n > 0) {
+ *--s1 = *--s2;
+ n--;
+ }
+ return a1;
+}
+
+void*
+memcpy(void *a1, void *a2, ulong n)
+{
+ return memmove(a1, a2, n);
+}
diff --git a/sys/src/libc/port/memset.c b/sys/src/libc/port/memset.c
new file mode 100755
index 000000000..431d6bb30
--- /dev/null
+++ b/sys/src/libc/port/memset.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+void*
+memset(void *ap, int c, ulong n)
+{
+ char *p;
+
+ p = ap;
+ while(n > 0) {
+ *p++ = c;
+ n--;
+ }
+ return ap;
+}
diff --git a/sys/src/libc/port/mkfile b/sys/src/libc/port/mkfile
new file mode 100755
index 000000000..dc2d93694
--- /dev/null
+++ b/sys/src/libc/port/mkfile
@@ -0,0 +1,127 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+CFILES=\
+ _assert.c\
+ abs.c\
+ asin.c\
+ atan.c\
+ atan2.c\
+ atexit.c\
+ atnotify.c\
+ atof.c\
+ atol.c\
+ atoll.c\
+ cistrcmp.c\
+ cistrncmp.c\
+ cistrstr.c\
+ charstod.c\
+ cleanname.c\
+ crypt.c\
+ ctype.c\
+ encodefmt.c\
+ execl.c\
+ exp.c\
+ fabs.c\
+ floor.c\
+ fmod.c\
+ frand.c\
+ frexp.c\
+ getcallerpc.c\
+ getfields.c\
+ getuser.c\
+ hangup.c\
+ hypot.c\
+ lnrand.c\
+ lock.c\
+ log.c\
+ lrand.c\
+ malloc.c\
+ memccpy.c\
+ memchr.c\
+ memcmp.c\
+ memmove.c\
+ memset.c\
+ mktemp.c\
+ muldiv.c\
+ nan.c\
+ needsrcquote.c\
+ netcrypt.c\
+ netmkaddr.c\
+ nrand.c\
+ ntruerand.c\
+ perror.c\
+ pool.c\
+ pow.c\
+ pow10.c\
+ profile.c\
+ qsort.c\
+ quote.c\
+ rand.c\
+ readn.c\
+ rune.c\
+ runestrcat.c\
+ runestrchr.c\
+ runestrcmp.c\
+ runestrcpy.c\
+ runestrecpy.c\
+ runestrdup.c\
+ runestrncat.c\
+ runestrncmp.c\
+ runestrncpy.c\
+ runestrrchr.c\
+ runestrlen.c\
+ runestrstr.c\
+ runetype.c\
+ sin.c\
+ sinh.c\
+ sqrt.c\
+ strcat.c\
+ strchr.c\
+ strcmp.c\
+ strcpy.c\
+ strecpy.c\
+ strcspn.c\
+ strdup.c\
+ strlen.c\
+ strncat.c\
+ strncmp.c\
+ strncpy.c\
+ strpbrk.c\
+ strrchr.c\
+ strspn.c\
+ strstr.c\
+ strtod.c\
+ strtok.c\
+ strtol.c\
+ strtoll.c\
+ strtoul.c\
+ strtoull.c\
+ tan.c\
+ tanh.c\
+ tokenize.c\
+ toupper.c\
+ utfecpy.c\
+ utflen.c\
+ utfnlen.c\
+ utfrune.c\
+ utfrrune.c\
+ utfutf.c\
+ u16.c\
+ u32.c\
+ u64.c\
+
+ALLOFILES=${CFILES:%.c=%.$O}
+
+# cull things in the per-machine directories from this list
+OFILES= `{rc ./reduce $O $objtype $ALLOFILES}
+
+HFILES=/sys/include/libc.h
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+
+</sys/src/cmd/mksyslib
+
+profile.$O: /sys/include/tos.h
diff --git a/sys/src/libc/port/mktemp.c b/sys/src/libc/port/mktemp.c
new file mode 100755
index 000000000..384d7fcf7
--- /dev/null
+++ b/sys/src/libc/port/mktemp.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+mktemp(char *as)
+{
+ char *s;
+ unsigned pid;
+ int i;
+ char err[ERRMAX];
+
+ pid = getpid();
+ s = as;
+ while(*s++)
+ ;
+ s--;
+ while(*--s == 'X') {
+ *s = pid % 10 + '0';
+ pid = pid/10;
+ }
+ s++;
+ i = 'a';
+ while(access(as, 0) != -1) {
+ if (i == 'z')
+ return "/";
+ *s = i++;
+ }
+ err[0] = '\0';
+ errstr(err, sizeof err); /* clear the error */
+ return as;
+}
diff --git a/sys/src/libc/port/muldiv.c b/sys/src/libc/port/muldiv.c
new file mode 100755
index 000000000..c304daf6e
--- /dev/null
+++ b/sys/src/libc/port/muldiv.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+
+ulong
+umuldiv(ulong a, ulong b, ulong c)
+{
+ double d;
+
+ d = ((double)a * (double)b) / (double)c;
+ if(d >= 4294967296.)
+ abort();
+ return d;
+}
+
+long
+muldiv(long a, long b, long c)
+{
+ int s;
+ long v;
+
+ s = 0;
+ if(a < 0) {
+ s = !s;
+ a = -a;
+ }
+ if(b < 0) {
+ s = !s;
+ b = -b;
+ }
+ if(c < 0) {
+ s = !s;
+ c = -c;
+ }
+ v = umuldiv(a, b, c);
+ if(s)
+ v = -v;
+ return v;
+}
diff --git a/sys/src/libc/port/nan.c b/sys/src/libc/port/nan.c
new file mode 100755
index 000000000..f5a4cec34
--- /dev/null
+++ b/sys/src/libc/port/nan.c
@@ -0,0 +1,54 @@
+#include <u.h>
+#include <libc.h>
+
+#define NANEXP (2047<<20)
+#define NANMASK (2047<<20)
+#define NANSIGN (1<<31)
+
+double
+NaN(void)
+{
+ FPdbleword a;
+
+ a.hi = NANEXP;
+ a.lo = 1;
+ return a.x;
+}
+
+int
+isNaN(double d)
+{
+ FPdbleword a;
+
+ a.x = d;
+ if((a.hi & NANMASK) != NANEXP)
+ return 0;
+ return !isInf(d, 0);
+}
+
+double
+Inf(int sign)
+{
+ FPdbleword a;
+
+ a.hi = NANEXP;
+ a.lo = 0;
+ if(sign < 0)
+ a.hi |= NANSIGN;
+ return a.x;
+}
+
+int
+isInf(double d, int sign)
+{
+ FPdbleword a;
+
+ a.x = d;
+ if(a.lo != 0)
+ return 0;
+ if(a.hi == NANEXP)
+ return sign >= 0;
+ if(a.hi == (NANEXP|NANSIGN))
+ return sign <= 0;
+ return 0;
+}
diff --git a/sys/src/libc/port/needsrcquote.c b/sys/src/libc/port/needsrcquote.c
new file mode 100755
index 000000000..f211ea1f9
--- /dev/null
+++ b/sys/src/libc/port/needsrcquote.c
@@ -0,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+
+int
+needsrcquote(int c)
+{
+ if(c <= ' ')
+ return 1;
+ if(utfrune("`^#*[]=|\\?${}()'<>&;", c))
+ return 1;
+ return 0;
+}
diff --git a/sys/src/libc/port/netcrypt.c b/sys/src/libc/port/netcrypt.c
new file mode 100755
index 000000000..08fa6761c
--- /dev/null
+++ b/sys/src/libc/port/netcrypt.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int
+netcrypt(void *key, void *chal)
+{
+ uchar buf[8], *p;
+
+ strncpy((char*)buf, chal, 7);
+ buf[7] = '\0';
+ for(p = buf; *p && *p != '\n'; p++)
+ ;
+ *p = '\0';
+ encrypt(key, buf, 8);
+ sprint(chal, "%.2ux%.2ux%.2ux%.2ux", buf[0], buf[1], buf[2], buf[3]);
+ return 1;
+}
diff --git a/sys/src/libc/port/netmkaddr.c b/sys/src/libc/port/netmkaddr.c
new file mode 100755
index 000000000..fd53f4684
--- /dev/null
+++ b/sys/src/libc/port/netmkaddr.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/*
+ * make an address, add the defaults
+ */
+char *
+netmkaddr(char *linear, char *defnet, char *defsrv)
+{
+ static char addr[256];
+ char *cp;
+
+ /*
+ * dump network name
+ */
+ cp = strchr(linear, '!');
+ if(cp == 0){
+ if(defnet==0){
+ if(defsrv)
+ snprint(addr, sizeof(addr), "net!%s!%s",
+ linear, defsrv);
+ else
+ snprint(addr, sizeof(addr), "net!%s", linear);
+ }
+ else {
+ if(defsrv)
+ snprint(addr, sizeof(addr), "%s!%s!%s", defnet,
+ linear, defsrv);
+ else
+ snprint(addr, sizeof(addr), "%s!%s", defnet,
+ linear);
+ }
+ return addr;
+ }
+
+ /*
+ * if there is already a service, use it
+ */
+ cp = strchr(cp+1, '!');
+ if(cp)
+ return linear;
+
+ /*
+ * add default service
+ */
+ if(defsrv == 0)
+ return linear;
+ snprint(addr, sizeof(addr), "%s!%s", linear, defsrv);
+
+ return addr;
+}
diff --git a/sys/src/libc/port/nrand.c b/sys/src/libc/port/nrand.c
new file mode 100755
index 000000000..31dbe27ff
--- /dev/null
+++ b/sys/src/libc/port/nrand.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+#define MASK 0x7fffffffL
+
+int
+nrand(int n)
+{
+ long slop, v;
+
+ if(n < 0)
+ return n;
+ if(n == 1)
+ return 0;
+ /* and if n == 0, you deserve what you get */
+ slop = MASK % n;
+ do
+ v = lrand();
+ while(v <= slop);
+ return v % n;
+}
diff --git a/sys/src/libc/port/ntruerand.c b/sys/src/libc/port/ntruerand.c
new file mode 100755
index 000000000..248eda647
--- /dev/null
+++ b/sys/src/libc/port/ntruerand.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+
+ulong
+ntruerand(ulong n)
+{
+ ulong m, r;
+
+ /*
+ * set m to the one less than the maximum multiple of n <= 2^32,
+ * so we want a random number <= m.
+ */
+ if(n > (1UL<<31))
+ m = n-1;
+ else
+ /* 2^32 - 2^32%n - 1 = (2^32 - 1) - (2*(2^31%n))%n */
+ m = 0xFFFFFFFFUL - (2*((1UL<<31)%n))%n;
+
+ while((r = truerand()) > m)
+ ;
+
+ return r%n;
+}
diff --git a/sys/src/libc/port/perror.c b/sys/src/libc/port/perror.c
new file mode 100755
index 000000000..c4c03c7bc
--- /dev/null
+++ b/sys/src/libc/port/perror.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+void
+perror(char *s)
+{
+ char buf[ERRMAX];
+
+ buf[0] = '\0';
+ errstr(buf, sizeof buf);
+ if(s && *s)
+ fprint(2, "%s: %s\n", s, buf);
+ else
+ fprint(2, "%s\n", buf);
+}
diff --git a/sys/src/libc/port/pool.acid b/sys/src/libc/port/pool.acid
new file mode 100755
index 000000000..da58aa441
--- /dev/null
+++ b/sys/src/libc/port/pool.acid
@@ -0,0 +1,628 @@
+sizeof_1_ = 8;
+aggr _1_
+{
+ 'U' 0 lo;
+ 'U' 4 hi;
+};
+
+defn
+_1_(addr) {
+ complex _1_ addr;
+ print(" lo ", addr.lo, "\n");
+ print(" hi ", addr.hi, "\n");
+};
+
+sizeofFPdbleword = 8;
+aggr FPdbleword
+{
+ 'F' 0 x;
+ {
+ 'U' 0 lo;
+ 'U' 4 hi;
+ };
+};
+
+defn
+FPdbleword(addr) {
+ complex FPdbleword addr;
+ print(" x ", addr.x, "\n");
+ print("_1_ {\n");
+ _1_(addr+0);
+ print("}\n");
+};
+
+UTFmax = 3;
+Runesync = 128;
+Runeself = 128;
+Runeerror = 65533;
+sizeofFmt = 48;
+aggr Fmt
+{
+ 'b' 0 runes;
+ 'X' 4 start;
+ 'X' 8 to;
+ 'X' 12 stop;
+ 'X' 16 flush;
+ 'X' 20 farg;
+ 'D' 24 nfmt;
+ 'X' 28 args;
+ 'D' 32 r;
+ 'D' 36 width;
+ 'D' 40 prec;
+ 'U' 44 flags;
+};
+
+defn
+Fmt(addr) {
+ complex Fmt addr;
+ print(" runes ", addr.runes, "\n");
+ print(" start ", addr.start\X, "\n");
+ print(" to ", addr.to\X, "\n");
+ print(" stop ", addr.stop\X, "\n");
+ print(" flush ", addr.flush\X, "\n");
+ print(" farg ", addr.farg\X, "\n");
+ print(" nfmt ", addr.nfmt, "\n");
+ print(" args ", addr.args\X, "\n");
+ print(" r ", addr.r, "\n");
+ print(" width ", addr.width, "\n");
+ print(" prec ", addr.prec, "\n");
+ print(" flags ", addr.flags, "\n");
+};
+
+FmtWidth = 1;
+FmtLeft = 2;
+FmtPrec = 4;
+FmtSharp = 8;
+FmtSpace = 16;
+FmtSign = 32;
+FmtZero = 64;
+FmtUnsigned = 128;
+FmtShort = 256;
+FmtLong = 512;
+FmtVLong = 1024;
+FmtComma = 2048;
+FmtByte = 4096;
+FmtFlag = 8192;
+sizeofTm = 40;
+aggr Tm
+{
+ 'D' 0 sec;
+ 'D' 4 min;
+ 'D' 8 hour;
+ 'D' 12 mday;
+ 'D' 16 mon;
+ 'D' 20 year;
+ 'D' 24 wday;
+ 'D' 28 yday;
+ 'a' 32 zone;
+ 'D' 36 tzoff;
+};
+
+defn
+Tm(addr) {
+ complex Tm addr;
+ print(" sec ", addr.sec, "\n");
+ print(" min ", addr.min, "\n");
+ print(" hour ", addr.hour, "\n");
+ print(" mday ", addr.mday, "\n");
+ print(" mon ", addr.mon, "\n");
+ print(" year ", addr.year, "\n");
+ print(" wday ", addr.wday, "\n");
+ print(" yday ", addr.yday, "\n");
+ print(" zone ", addr.zone, "\n");
+ print(" tzoff ", addr.tzoff, "\n");
+};
+
+PNPROC = 1;
+PNGROUP = 2;
+Profoff = 0;
+Profuser = 1;
+Profkernel = 2;
+Proftime = 3;
+Profsample = 4;
+sizeofLock = 4;
+aggr Lock
+{
+ 'D' 0 val;
+};
+
+defn
+Lock(addr) {
+ complex Lock addr;
+ print(" val ", addr.val, "\n");
+};
+
+sizeofQLp = 12;
+aggr QLp
+{
+ 'D' 0 inuse;
+ 'A' QLp 4 next;
+ 'C' 8 state;
+};
+
+defn
+QLp(addr) {
+ complex QLp addr;
+ print(" inuse ", addr.inuse, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" state ", addr.state, "\n");
+};
+
+sizeofQLock = 16;
+aggr QLock
+{
+ Lock 0 lock;
+ 'D' 4 locked;
+ 'A' QLp 8 $head;
+ 'A' QLp 12 $tail;
+};
+
+defn
+QLock(addr) {
+ complex QLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" locked ", addr.locked, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+sizeofRWLock = 20;
+aggr RWLock
+{
+ Lock 0 lock;
+ 'D' 4 readers;
+ 'D' 8 writer;
+ 'A' QLp 12 $head;
+ 'A' QLp 16 $tail;
+};
+
+defn
+RWLock(addr) {
+ complex RWLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" readers ", addr.readers, "\n");
+ print(" writer ", addr.writer, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+sizeofRendez = 12;
+aggr Rendez
+{
+ 'A' QLock 0 l;
+ 'A' QLp 4 $head;
+ 'A' QLp 8 $tail;
+};
+
+defn
+Rendez(addr) {
+ complex Rendez addr;
+ print(" l ", addr.l\X, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+sizeofNetConnInfo = 36;
+aggr NetConnInfo
+{
+ 'X' 0 dir;
+ 'X' 4 root;
+ 'X' 8 spec;
+ 'X' 12 lsys;
+ 'X' 16 lserv;
+ 'X' 20 rsys;
+ 'X' 24 rserv;
+ 'X' 28 laddr;
+ 'X' 32 raddr;
+};
+
+defn
+NetConnInfo(addr) {
+ complex NetConnInfo addr;
+ print(" dir ", addr.dir\X, "\n");
+ print(" root ", addr.root\X, "\n");
+ print(" spec ", addr.spec\X, "\n");
+ print(" lsys ", addr.lsys\X, "\n");
+ print(" lserv ", addr.lserv\X, "\n");
+ print(" rsys ", addr.rsys\X, "\n");
+ print(" rserv ", addr.rserv\X, "\n");
+ print(" laddr ", addr.laddr\X, "\n");
+ print(" raddr ", addr.raddr\X, "\n");
+};
+
+RFNAMEG = 1;
+RFENVG = 2;
+RFFDG = 4;
+RFNOTEG = 8;
+RFPROC = 16;
+RFMEM = 32;
+RFNOWAIT = 64;
+RFCNAMEG = 1024;
+RFCENVG = 2048;
+RFCFDG = 4096;
+RFREND = 8192;
+RFNOMNT = 16384;
+sizeofQid = 16;
+aggr Qid
+{
+ 'W' 0 path;
+ 'U' 8 vers;
+ 'b' 12 type;
+};
+
+defn
+Qid(addr) {
+ complex Qid addr;
+ print(" path ", addr.path, "\n");
+ print(" vers ", addr.vers, "\n");
+ print(" type ", addr.type, "\n");
+};
+
+sizeofDir = 60;
+aggr Dir
+{
+ 'u' 0 type;
+ 'U' 4 dev;
+ Qid 8 qid;
+ 'U' 24 mode;
+ 'U' 28 atime;
+ 'U' 32 mtime;
+ 'V' 36 length;
+ 'X' 44 name;
+ 'X' 48 uid;
+ 'X' 52 gid;
+ 'X' 56 muid;
+};
+
+defn
+Dir(addr) {
+ complex Dir addr;
+ print(" type ", addr.type, "\n");
+ print(" dev ", addr.dev, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" mode ", addr.mode, "\n");
+ print(" atime ", addr.atime, "\n");
+ print(" mtime ", addr.mtime, "\n");
+ print(" length ", addr.length, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" gid ", addr.gid\X, "\n");
+ print(" muid ", addr.muid\X, "\n");
+};
+
+sizeofWaitmsg = 20;
+aggr Waitmsg
+{
+ 'D' 0 pid;
+ 'a' 4 time;
+ 'X' 16 msg;
+};
+
+defn
+Waitmsg(addr) {
+ complex Waitmsg addr;
+ print(" pid ", addr.pid, "\n");
+ print(" time ", addr.time, "\n");
+ print(" msg ", addr.msg\X, "\n");
+};
+
+sizeofIOchunk = 8;
+aggr IOchunk
+{
+ 'X' 0 addr;
+ 'U' 4 len;
+};
+
+defn
+IOchunk(addr) {
+ complex IOchunk addr;
+ print(" addr ", addr.addr\X, "\n");
+ print(" len ", addr.len, "\n");
+};
+
+sizeofPool = 88;
+aggr Pool
+{
+ 'X' 0 name;
+ 'U' 4 maxsize;
+ 'U' 8 cursize;
+ 'U' 12 curfree;
+ 'U' 16 curalloc;
+ 'U' 20 minarena;
+ 'U' 24 quantum;
+ 'U' 28 minblock;
+ 'X' 32 freeroot;
+ 'X' 36 arenalist;
+ 'X' 40 alloc;
+ 'X' 44 merge;
+ 'X' 48 move;
+ 'D' 52 flags;
+ 'D' 56 nfree;
+ 'D' 60 lastcompact;
+ 'X' 64 lock;
+ 'X' 68 unlock;
+ 'X' 72 print;
+ 'X' 76 panic;
+ 'X' 80 logstack;
+ 'X' 84 private;
+};
+
+defn
+Pool(addr) {
+ complex Pool addr;
+ print(" name ", addr.name\X, "\n");
+ print(" maxsize ", addr.maxsize, "\n");
+ print(" cursize ", addr.cursize, "\n");
+ print(" curfree ", addr.curfree, "\n");
+ print(" curalloc ", addr.curalloc, "\n");
+ print(" minarena ", addr.minarena, "\n");
+ print(" quantum ", addr.quantum, "\n");
+ print(" minblock ", addr.minblock, "\n");
+ print(" freeroot ", addr.freeroot\X, "\n");
+ print(" arenalist ", addr.arenalist\X, "\n");
+ print(" alloc ", addr.alloc\X, "\n");
+ print(" merge ", addr.merge\X, "\n");
+ print(" move ", addr.move\X, "\n");
+ print(" flags ", addr.flags, "\n");
+ print(" nfree ", addr.nfree, "\n");
+ print(" lastcompact ", addr.lastcompact, "\n");
+ print(" lock ", addr.lock\X, "\n");
+ print(" unlock ", addr.unlock\X, "\n");
+ print(" print ", addr.print\X, "\n");
+ print(" panic ", addr.panic\X, "\n");
+ print(" logstack ", addr.logstack\X, "\n");
+ print(" private ", addr.private\X, "\n");
+};
+
+complex Pool mainmem;
+complex Pool imagmem;
+POOL_ANTAGONISM = 1;
+POOL_PARANOIA = 2;
+POOL_VERBOSITY = 4;
+POOL_DEBUGGING = 8;
+POOL_LOGGING = 16;
+POOL_TOLERANCE = 32;
+POOL_NOREUSE = 64;
+sizeofBhdr = 8;
+aggr Bhdr
+{
+ 'U' 0 magic;
+ 'U' 4 size;
+};
+
+defn
+Bhdr(addr) {
+ complex Bhdr addr;
+ print(" magic ", addr.magic, "\n");
+ print(" size ", addr.size, "\n");
+};
+
+NOT_MAGIC = 3735943697;
+DEAD_MAGIC = 3735936685;
+TAIL_MAGIC0 = 190;
+TAIL_MAGIC1 = 239;
+sizeofBtail = 8;
+aggr Btail
+{
+ 'b' 0 magic0;
+ 'a' 1 datasize;
+ 'b' 3 magic1;
+ 'U' 4 size;
+};
+
+defn
+Btail(addr) {
+ complex Btail addr;
+ print(" magic0 ", addr.magic0, "\n");
+ print(" datasize ", addr.datasize, "\n");
+ print(" magic1 ", addr.magic1, "\n");
+ print(" size ", addr.size, "\n");
+};
+
+sizeofFree = 24;
+aggr Free
+{
+ {
+ 'U' 0 magic;
+ 'U' 4 size;
+ };
+ 'A' Free 8 left;
+ 'A' Free 12 right;
+ 'A' Free 16 next;
+ 'A' Free 20 prev;
+};
+
+defn
+Free(addr) {
+ complex Free addr;
+ print("Bhdr {\n");
+ Bhdr(addr+0);
+ print("}\n");
+ print(" left ", addr.left\X, "\n");
+ print(" right ", addr.right\X, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" prev ", addr.prev\X, "\n");
+};
+
+FREE_MAGIC = 3126770193;
+sizeofAlloc = 8;
+aggr Alloc
+{
+ {
+ 'U' 0 magic;
+ 'U' 4 size;
+ };
+};
+
+defn
+Alloc(addr) {
+ complex Alloc addr;
+ print("Bhdr {\n");
+ Bhdr(addr+0);
+ print("}\n");
+};
+
+ALLOC_MAGIC = 168889353;
+UNALLOC_MAGIC = 3400535327;
+sizeofArena = 24;
+aggr Arena
+{
+ {
+ 'U' 0 magic;
+ 'U' 4 size;
+ };
+ 'A' Arena 8 aup;
+ 'A' Arena 12 down;
+ 'U' 16 asize;
+ 'U' 20 pad;
+};
+
+defn
+Arena(addr) {
+ complex Arena addr;
+ print("Bhdr {\n");
+ Bhdr(addr+0);
+ print("}\n");
+ print(" aup ", addr.aup\X, "\n");
+ print(" down ", addr.down\X, "\n");
+ print(" asize ", addr.asize, "\n");
+ print(" pad ", addr.pad, "\n");
+};
+
+ARENA_MAGIC = 3231835599;
+ARENATAIL_MAGIC = 3965590029;
+ALIGN_MAGIC = 2716979649;
+MINBLOCKSIZE = 32;
+complex Free checklist:t;
+complex Free checklist:q;
+complex Free checktree:t;
+complex Free ltreewalk:t;
+complex Free treelookup:t;
+complex Free treeinsert:tree;
+complex Free treeinsert:node;
+complex Free treeinsert:loc;
+complex Free treeinsert:repl;
+complex Free treedelete:tree;
+complex Free treedelete:node;
+complex Free treedelete:loc;
+complex Free treedelete:lsucc;
+complex Free treedelete:succ;
+complex Free treelookupgt:t;
+complex Free treelookupgt:lastgood;
+complex Free listadd:list;
+complex Free listadd:node;
+complex Free listdelete:list;
+complex Free listdelete:node;
+complex Pool pooladd:p;
+complex Alloc pooladd:anode;
+complex Free pooladd:lst;
+complex Free pooladd:olst;
+complex Free pooladd:node;
+complex Free pooladd:parent;
+complex Pool pooldel:p;
+complex Free pooldel:node;
+complex Free pooldel:lst;
+complex Free pooldel:olst;
+complex Free pooldel:parent;
+complex Pool dsize2bsize:p;
+complex Pool bsize2asize:p;
+complex Pool blockmerge:pool;
+complex Bhdr blockmerge:a;
+complex Bhdr blockmerge:b;
+complex Btail blockmerge:t;
+complex Bhdr blocksetsize:b;
+complex Btail blocksetsize:t;
+complex Alloc getdsize:b;
+complex Btail getdsize:t;
+complex Pool blocksetdsize:p;
+complex Alloc blocksetdsize:b;
+complex Btail blocksetdsize:t;
+complex Pool trim:p;
+complex Alloc trim:b;
+complex Alloc trim:frag;
+complex Pool freefromfront:p;
+complex Alloc freefromfront:b;
+complex Alloc freefromfront:bb;
+complex Arena arenasetsize:a;
+complex Bhdr arenasetsize:atail;
+complex Pool poolnewarena:p;
+complex Arena poolnewarena:a;
+complex Arena poolnewarena:ap;
+complex Arena poolnewarena:lastap;
+complex Alloc poolnewarena:b;
+complex Pool blockgrow:p;
+complex Bhdr blockgrow:b;
+complex Alloc blockgrow:a;
+complex Bhdr blockgrow:bnxt;
+complex Alloc blockgrow:a;
+complex Pool arenamerge:p;
+complex Arena arenamerge:bot;
+complex Arena arenamerge:top;
+complex Bhdr arenamerge:bbot;
+complex Bhdr arenamerge:btop;
+complex Btail arenamerge:t;
+complex Pool dumpblock:p;
+complex Bhdr dumpblock:b;
+complex Pool printblock:p;
+complex Bhdr printblock:b;
+complex Pool panicblock:p;
+complex Bhdr panicblock:b;
+complex Pool blockcheck:p;
+complex Bhdr blockcheck:b;
+complex Alloc blockcheck:a;
+complex Btail blockcheck:t;
+FLOATING_MAGIC = 3419130827;
+complex Pool arenacompact:p;
+complex Arena arenacompact:a;
+complex Bhdr arenacompact:b;
+complex Bhdr arenacompact:wb;
+complex Bhdr arenacompact:eb;
+complex Bhdr arenacompact:nxt;
+complex Pool poolcompactl:pool;
+complex Arena poolcompactl:a;
+complex Pool B2D:p;
+complex Alloc B2D:a;
+complex Pool D2B:p;
+complex Alloc D2B:a;
+complex Pool poolallocl:p;
+complex Free poolallocl:fb;
+complex Alloc poolallocl:ab;
+complex Pool poolreallocl:p;
+complex Alloc poolreallocl:a;
+complex Bhdr poolreallocl:left;
+complex Bhdr poolreallocl:right;
+complex Bhdr poolreallocl:newb;
+complex Btail poolreallocl:t;
+complex Pool poolallocalignl:p;
+complex Alloc poolallocalignl:b;
+complex Pool poolfreel:p;
+complex Alloc poolfreel:ab;
+complex Bhdr poolfreel:back;
+complex Bhdr poolfreel:fwd;
+complex Pool poolalloc:p;
+complex Pool poolallocalign:p;
+complex Pool poolcompact:p;
+complex Pool poolrealloc:p;
+complex Pool poolfree:p;
+complex Pool poolmsize:p;
+complex Alloc poolmsize:b;
+complex Pool poolcheckarena:p;
+complex Arena poolcheckarena:a;
+complex Bhdr poolcheckarena:b;
+complex Bhdr poolcheckarena:atail;
+complex Pool poolcheckl:p;
+complex Arena poolcheckl:a;
+complex Pool poolcheck:p;
+complex Pool poolblockcheck:p;
+complex Pool pooldumpl:p;
+complex Arena pooldumpl:a;
+complex Pool pooldump:p;
+complex Pool pooldumparena:p;
+complex Arena pooldumparena:a;
+complex Bhdr pooldumparena:b;
diff --git a/sys/src/libc/port/pool.c b/sys/src/libc/port/pool.c
new file mode 100755
index 000000000..915fca3f1
--- /dev/null
+++ b/sys/src/libc/port/pool.c
@@ -0,0 +1,1466 @@
+/*
+ * This allocator takes blocks from a coarser allocator (p->alloc) and
+ * uses them as arenas.
+ *
+ * An arena is split into a sequence of blocks of variable size. The
+ * blocks begin with a Bhdr that denotes the length (including the Bhdr)
+ * of the block. An arena begins with an Arena header block (Arena,
+ * ARENA_MAGIC) and ends with a Bhdr block with magic ARENATAIL_MAGIC and
+ * size 0. Intermediate blocks are either allocated or free. At the end
+ * of each intermediate block is a Btail, which contains information
+ * about where the block starts. This is useful for walking backwards.
+ *
+ * Free blocks (Free*) have a magic value of FREE_MAGIC in their Bhdr
+ * headers. They are kept in a binary tree (p->freeroot) traversible by
+ * walking ->left and ->right. Each node of the binary tree is a pointer
+ * to a circular doubly-linked list (next, prev) of blocks of identical
+ * size. Blocks are added to this ``tree of lists'' by pooladd(), and
+ * removed by pooldel().
+ *
+ * When freed, adjacent blocks are coalesced to create larger blocks when
+ * possible.
+ *
+ * Allocated blocks (Alloc*) have one of two magic values: ALLOC_MAGIC or
+ * UNALLOC_MAGIC. When blocks are released from the pool, they have
+ * magic value UNALLOC_MAGIC. Once the block has been trimmed by trim()
+ * and the amount of user-requested data has been recorded in the
+ * datasize field of the tail, the magic value is changed to ALLOC_MAGIC.
+ * All blocks returned to callers should be of type ALLOC_MAGIC, as
+ * should all blocks passed to us by callers. The amount of data the user
+ * asked us for can be found by subtracting the short in tail->datasize
+ * from header->size. Further, the up to at most four bytes between the
+ * end of the user-requested data block and the actual Btail structure are
+ * marked with a magic value, which is checked to detect user overflow.
+ *
+ * The arenas returned by p->alloc are kept in a doubly-linked list
+ * (p->arenalist) running through the arena headers, sorted by descending
+ * base address (prev, next). When a new arena is allocated, we attempt
+ * to merge it with its two neighbors via p->merge.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <pool.h>
+
+typedef struct Alloc Alloc;
+typedef struct Arena Arena;
+typedef struct Bhdr Bhdr;
+typedef struct Btail Btail;
+typedef struct Free Free;
+
+struct Bhdr {
+ ulong magic;
+ ulong size;
+};
+enum {
+ NOT_MAGIC = 0xdeadfa11,
+ DEAD_MAGIC = 0xdeaddead,
+};
+#define B2NB(b) ((Bhdr*)((uchar*)(b)+(b)->size))
+
+#define SHORT(x) (((x)[0] << 8) | (x)[1])
+#define PSHORT(p, x) \
+ (((uchar*)(p))[0] = ((x)>>8)&0xFF, \
+ ((uchar*)(p))[1] = (x)&0xFF)
+
+enum {
+ TAIL_MAGIC0 = 0xBE,
+ TAIL_MAGIC1 = 0xEF
+};
+struct Btail {
+ uchar magic0;
+ uchar datasize[2];
+ uchar magic1;
+ ulong size; /* same as Bhdr->size */
+};
+#define B2T(b) ((Btail*)((uchar*)(b)+(b)->size-sizeof(Btail)))
+#define B2PT(b) ((Btail*)((uchar*)(b)-sizeof(Btail)))
+#define T2HDR(t) ((Bhdr*)((uchar*)(t)+sizeof(Btail)-(t)->size))
+struct Free {
+ Bhdr;
+ Free* left;
+ Free* right;
+ Free* next;
+ Free* prev;
+};
+enum {
+ FREE_MAGIC = 0xBA5EBA11,
+};
+
+/*
+ * the point of the notused fields is to make 8c differentiate
+ * between Bhdr and Allocblk, and between Kempt and Unkempt.
+ */
+struct Alloc {
+ Bhdr;
+};
+enum {
+ ALLOC_MAGIC = 0x0A110C09,
+ UNALLOC_MAGIC = 0xCAB00D1E+1,
+};
+
+struct Arena {
+ Bhdr;
+ Arena* aup;
+ Arena* down;
+ ulong asize;
+ ulong pad; /* to a multiple of 8 bytes */
+};
+enum {
+ ARENA_MAGIC = 0xC0A1E5CE+1,
+ ARENATAIL_MAGIC = 0xEC5E1A0C+1,
+};
+#define A2TB(a) ((Bhdr*)((uchar*)(a)+(a)->asize-sizeof(Bhdr)))
+#define A2B(a) B2NB(a)
+
+enum {
+ ALIGN_MAGIC = 0xA1F1D1C1,
+};
+
+enum {
+ MINBLOCKSIZE = sizeof(Free)+sizeof(Btail)
+};
+
+static uchar datamagic[] = { 0xFE, 0xF1, 0xF0, 0xFA };
+
+#define Poison (void*)0xCafeBabe
+
+#define _B2D(a) ((void*)((uchar*)a+sizeof(Bhdr)))
+#define _D2B(v) ((Alloc*)((uchar*)v-sizeof(Bhdr)))
+
+// static void* _B2D(void*);
+// static void* _D2B(void*);
+static void* B2D(Pool*, Alloc*);
+static Alloc* D2B(Pool*, void*);
+static Arena* arenamerge(Pool*, Arena*, Arena*);
+static void blockcheck(Pool*, Bhdr*);
+static Alloc* blockmerge(Pool*, Bhdr*, Bhdr*);
+static Alloc* blocksetdsize(Pool*, Alloc*, ulong);
+static Bhdr* blocksetsize(Bhdr*, ulong);
+static ulong bsize2asize(Pool*, ulong);
+static ulong dsize2bsize(Pool*, ulong);
+static ulong getdsize(Alloc*);
+static Alloc* trim(Pool*, Alloc*, ulong);
+static Free* listadd(Free*, Free*);
+static Free* listdelete(Free*, Free*);
+static void logstack(Pool*);
+static Free** ltreewalk(Free**, ulong);
+static void memmark(void*, int, ulong);
+static Free* pooladd(Pool*, Alloc*);
+static void* poolallocl(Pool*, ulong);
+static void poolcheckl(Pool*);
+static void poolcheckarena(Pool*, Arena*);
+static int poolcompactl(Pool*);
+static Alloc* pooldel(Pool*, Free*);
+static void pooldumpl(Pool*);
+static void pooldumparena(Pool*, Arena*);
+static void poolfreel(Pool*, void*);
+static void poolnewarena(Pool*, ulong);
+static void* poolreallocl(Pool*, void*, ulong);
+static Free* treedelete(Free*, Free*);
+static Free* treeinsert(Free*, Free*);
+static Free* treelookup(Free*, ulong);
+static Free* treelookupgt(Free*, ulong);
+
+/*
+ * Debugging
+ *
+ * Antagonism causes blocks to always be filled with garbage if their
+ * contents are undefined. This tickles both programs and the library.
+ * It's a linear time hit but not so noticeable during nondegenerate use.
+ * It would be worth leaving in except that it negates the benefits of the
+ * kernel's demand-paging. The tail magic and end-of-data magic
+ * provide most of the user-visible benefit that antagonism does anyway.
+ *
+ * Paranoia causes the library to recheck the entire pool on each lock
+ * or unlock. A failed check on unlock means we tripped over ourselves,
+ * while a failed check on lock tends to implicate the user. Paranoia has
+ * the potential to slow things down a fair amount for pools with large
+ * numbers of allocated blocks. It completely negates all benefits won
+ * by the binary tree. Turning on paranoia in the kernel makes it painfully
+ * slow.
+ *
+ * Verbosity induces the dumping of the pool via p->print at each lock operation.
+ * By default, only one line is logged for each alloc, free, and realloc.
+ */
+
+/* the if(!x);else avoids ``dangling else'' problems */
+#define antagonism if(!(p->flags & POOL_ANTAGONISM)){}else
+#define paranoia if(!(p->flags & POOL_PARANOIA)){}else
+#define verbosity if(!(p->flags & POOL_VERBOSITY)){}else
+
+#define DPRINT if(!(p->flags & POOL_DEBUGGING)){}else p->print
+#define LOG if(!(p->flags & POOL_LOGGING)){}else p->print
+
+/*
+ * Tree walking
+ */
+
+static void
+checklist(Free *t)
+{
+ Free *q;
+
+ for(q=t->next; q!=t; q=q->next){
+ assert(q->size == t->size);
+ assert(q->next==nil || q->next->prev==q);
+ assert(q->prev==nil || q->prev->next==q);
+ // assert(q->left==nil);
+ // assert(q->right==nil);
+ assert(q->magic==FREE_MAGIC);
+ }
+}
+
+static void
+checktree(Free *t, int a, int b)
+{
+ assert(t->magic==FREE_MAGIC);
+ assert(a < t->size && t->size < b);
+ assert(t->next==nil || t->next->prev==t);
+ assert(t->prev==nil || t->prev->next==t);
+ checklist(t);
+ if(t->left)
+ checktree(t->left, a, t->size);
+ if(t->right)
+ checktree(t->right, t->size, b);
+
+}
+
+/* ltreewalk: return address of pointer to node of size == size */
+static Free**
+ltreewalk(Free **t, ulong size)
+{
+ assert(t != nil /* ltreewalk */);
+
+ for(;;) {
+ if(*t == nil)
+ return t;
+
+ assert((*t)->magic == FREE_MAGIC);
+
+ if(size == (*t)->size)
+ return t;
+ if(size < (*t)->size)
+ t = &(*t)->left;
+ else
+ t = &(*t)->right;
+ }
+}
+
+/* treelookup: find node in tree with size == size */
+static Free*
+treelookup(Free *t, ulong size)
+{
+ return *ltreewalk(&t, size);
+}
+
+/* treeinsert: insert node into tree */
+static Free*
+treeinsert(Free *tree, Free *node)
+{
+ Free **loc, *repl;
+
+ assert(node != nil /* treeinsert */);
+
+ loc = ltreewalk(&tree, node->size);
+ if(*loc == nil) {
+ node->left = nil;
+ node->right = nil;
+ } else { /* replace existing node */
+ repl = *loc;
+ node->left = repl->left;
+ node->right = repl->right;
+ }
+ *loc = node;
+ return tree;
+}
+
+/* treedelete: remove node from tree */
+static Free*
+treedelete(Free *tree, Free *node)
+{
+ Free **loc, **lsucc, *succ;
+
+ assert(node != nil /* treedelete */);
+
+ loc = ltreewalk(&tree, node->size);
+ assert(*loc == node);
+
+ if(node->left == nil)
+ *loc = node->right;
+ else if(node->right == nil)
+ *loc = node->left;
+ else {
+ /* have two children, use inorder successor as replacement */
+ for(lsucc = &node->right; (*lsucc)->left; lsucc = &(*lsucc)->left)
+ ;
+ succ = *lsucc;
+ *lsucc = succ->right;
+ succ->left = node->left;
+ succ->right = node->right;
+ *loc = succ;
+ }
+
+ node->left = node->right = Poison;
+ return tree;
+}
+
+/* treelookupgt: find smallest node in tree with size >= size */
+static Free*
+treelookupgt(Free *t, ulong size)
+{
+ Free *lastgood; /* last node we saw that was big enough */
+
+ lastgood = nil;
+ for(;;) {
+ if(t == nil)
+ return lastgood;
+ if(size == t->size)
+ return t;
+ if(size < t->size) {
+ lastgood = t;
+ t = t->left;
+ } else
+ t = t->right;
+ }
+}
+
+/*
+ * List maintenance
+ */
+
+/* listadd: add a node to a doubly linked list */
+static Free*
+listadd(Free *list, Free *node)
+{
+ if(list == nil) {
+ node->next = node;
+ node->prev = node;
+ return node;
+ }
+
+ node->prev = list->prev;
+ node->next = list;
+
+ node->prev->next = node;
+ node->next->prev = node;
+
+ return list;
+}
+
+/* listdelete: remove node from a doubly linked list */
+static Free*
+listdelete(Free *list, Free *node)
+{
+ if(node->next == node) { /* singular list */
+ node->prev = node->next = Poison;
+ return nil;
+ }
+
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+
+ if(list == node)
+ list = node->next;
+
+ node->prev = node->next = Poison;
+ return list;
+}
+
+/*
+ * Pool maintenance
+ */
+
+/* pooladd: add anode to the free pool */
+static Free*
+pooladd(Pool *p, Alloc *anode)
+{
+ Free *lst, *olst;
+ Free *node;
+ Free **parent;
+
+ antagonism {
+ memmark(_B2D(anode), 0xF7, anode->size-sizeof(Bhdr)-sizeof(Btail));
+ }
+
+ node = (Free*)anode;
+ node->magic = FREE_MAGIC;
+ parent = ltreewalk(&p->freeroot, node->size);
+ olst = *parent;
+ lst = listadd(olst, node);
+ if(olst != lst) /* need to update tree */
+ *parent = treeinsert(*parent, lst);
+ p->curfree += node->size;
+ return node;
+}
+
+/* pooldel: remove node from the free pool */
+static Alloc*
+pooldel(Pool *p, Free *node)
+{
+ Free *lst, *olst;
+ Free **parent;
+
+ parent = ltreewalk(&p->freeroot, node->size);
+ olst = *parent;
+ assert(olst != nil /* pooldel */);
+
+ lst = listdelete(olst, node);
+ if(lst == nil)
+ *parent = treedelete(*parent, olst);
+ else if(lst != olst)
+ *parent = treeinsert(*parent, lst);
+
+ node->left = node->right = Poison;
+ p->curfree -= node->size;
+
+ antagonism {
+ memmark(_B2D(node), 0xF9, node->size-sizeof(Bhdr)-sizeof(Btail));
+ }
+
+ node->magic = UNALLOC_MAGIC;
+ return (Alloc*)node;
+}
+
+/*
+ * Block maintenance
+ */
+/* block allocation */
+static ulong
+dsize2bsize(Pool *p, ulong sz)
+{
+ sz += sizeof(Bhdr)+sizeof(Btail);
+ if(sz < p->minblock)
+ sz = p->minblock;
+ if(sz < MINBLOCKSIZE)
+ sz = MINBLOCKSIZE;
+ sz = (sz+p->quantum-1)&~(p->quantum-1);
+ return sz;
+}
+
+static ulong
+bsize2asize(Pool *p, ulong sz)
+{
+ sz += sizeof(Arena)+sizeof(Btail);
+ if(sz < p->minarena)
+ sz = p->minarena;
+ sz = (sz+p->quantum)&~(p->quantum-1);
+ return sz;
+}
+
+/* blockmerge: merge a and b, known to be adjacent */
+/* both are removed from pool if necessary. */
+static Alloc*
+blockmerge(Pool *pool, Bhdr *a, Bhdr *b)
+{
+ Btail *t;
+
+ assert(B2NB(a) == b);
+
+ if(a->magic == FREE_MAGIC)
+ pooldel(pool, (Free*)a);
+ if(b->magic == FREE_MAGIC)
+ pooldel(pool, (Free*)b);
+
+ t = B2T(a);
+ t->size = (ulong)Poison;
+ t->magic0 = NOT_MAGIC;
+ t->magic1 = NOT_MAGIC;
+ PSHORT(t->datasize, NOT_MAGIC);
+
+ a->size += b->size;
+ t = B2T(a);
+ t->size = a->size;
+ PSHORT(t->datasize, 0xFFFF);
+
+ b->size = NOT_MAGIC;
+ b->magic = NOT_MAGIC;
+
+ a->magic = UNALLOC_MAGIC;
+ return (Alloc*)a;
+}
+
+/* blocksetsize: set the total size of a block, fixing tail pointers */
+static Bhdr*
+blocksetsize(Bhdr *b, ulong bsize)
+{
+ Btail *t;
+
+ assert(b->magic != FREE_MAGIC /* blocksetsize */);
+
+ b->size = bsize;
+ t = B2T(b);
+ t->size = b->size;
+ t->magic0 = TAIL_MAGIC0;
+ t->magic1 = TAIL_MAGIC1;
+ return b;
+}
+
+/* getdsize: return the requested data size for an allocated block */
+static ulong
+getdsize(Alloc *b)
+{
+ Btail *t;
+ t = B2T(b);
+ return b->size - SHORT(t->datasize);
+}
+
+/* blocksetdsize: set the user data size of a block */
+static Alloc*
+blocksetdsize(Pool *p, Alloc *b, ulong dsize)
+{
+ Btail *t;
+ uchar *q, *eq;
+
+ assert(b->size >= dsize2bsize(p, dsize));
+ assert(b->size - dsize < 0x10000);
+
+ t = B2T(b);
+ PSHORT(t->datasize, b->size - dsize);
+
+ q=(uchar*)_B2D(b)+dsize;
+ eq = (uchar*)t;
+ if(eq > q+4)
+ eq = q+4;
+ for(; q<eq; q++)
+ *q = datamagic[((ulong)(uintptr)q)%nelem(datamagic)];
+
+ return b;
+}
+
+/* trim: trim a block down to what is needed to hold dsize bytes of user data */
+static Alloc*
+trim(Pool *p, Alloc *b, ulong dsize)
+{
+ ulong extra, bsize;
+ Alloc *frag;
+
+ bsize = dsize2bsize(p, dsize);
+ extra = b->size - bsize;
+ if(b->size - dsize >= 0x10000 ||
+ (extra >= bsize>>2 && extra >= MINBLOCKSIZE && extra >= p->minblock)) {
+ blocksetsize(b, bsize);
+ frag = (Alloc*) B2NB(b);
+
+ antagonism {
+ memmark(frag, 0xF1, extra);
+ }
+
+ frag->magic = UNALLOC_MAGIC;
+ blocksetsize(frag, extra);
+ pooladd(p, frag);
+ }
+
+ b->magic = ALLOC_MAGIC;
+ blocksetdsize(p, b, dsize);
+ return b;
+}
+
+static Alloc*
+freefromfront(Pool *p, Alloc *b, ulong skip)
+{
+ Alloc *bb;
+
+ skip = skip&~(p->quantum-1);
+ if(skip >= 0x1000 || (skip >= b->size>>2 && skip >= MINBLOCKSIZE && skip >= p->minblock)){
+ bb = (Alloc*)((uchar*)b+skip);
+ blocksetsize(bb, b->size-skip);
+ bb->magic = UNALLOC_MAGIC;
+ blocksetsize(b, skip);
+ b->magic = UNALLOC_MAGIC;
+ pooladd(p, b);
+ return bb;
+ }
+ return b;
+}
+
+/*
+ * Arena maintenance
+ */
+
+/* arenasetsize: set arena size, updating tail */
+static void
+arenasetsize(Arena *a, ulong asize)
+{
+ Bhdr *atail;
+
+ a->asize = asize;
+ atail = A2TB(a);
+ atail->magic = ARENATAIL_MAGIC;
+ atail->size = 0;
+}
+
+/* poolnewarena: allocate new arena */
+static void
+poolnewarena(Pool *p, ulong asize)
+{
+ Arena *a;
+ Arena *ap, *lastap;
+ Alloc *b;
+
+ LOG(p, "newarena %lud\n", asize);
+ if(p->cursize+asize > p->maxsize) {
+ if(poolcompactl(p) == 0){
+ LOG(p, "pool too big: %lud+%lud > %lud\n",
+ p->cursize, asize, p->maxsize);
+ werrstr("memory pool too large");
+ }
+ return;
+ }
+
+ if((a = p->alloc(asize)) == nil) {
+ /* assume errstr set by p->alloc */
+ return;
+ }
+
+ p->cursize += asize;
+
+ /* arena hdr */
+ a->magic = ARENA_MAGIC;
+ blocksetsize(a, sizeof(Arena));
+ arenasetsize(a, asize);
+ blockcheck(p, a);
+
+ /* create one large block in arena */
+ b = (Alloc*)A2B(a);
+ b->magic = UNALLOC_MAGIC;
+ blocksetsize(b, (uchar*)A2TB(a)-(uchar*)b);
+ blockcheck(p, b);
+ pooladd(p, b);
+ blockcheck(p, b);
+
+ /* sort arena into descending sorted arena list */
+ for(lastap=nil, ap=p->arenalist; ap > a; lastap=ap, ap=ap->down)
+ ;
+
+ if(a->down = ap) /* assign = */
+ a->down->aup = a;
+
+ if(a->aup = lastap) /* assign = */
+ a->aup->down = a;
+ else
+ p->arenalist = a;
+
+ /* merge with surrounding arenas if possible */
+ /* must do a with up before down with a (think about it) */
+ if(a->aup)
+ arenamerge(p, a, a->aup);
+ if(a->down)
+ arenamerge(p, a->down, a);
+}
+
+/* blockresize: grow a block to encompass space past its end, possibly by */
+/* trimming it into two different blocks. */
+static void
+blockgrow(Pool *p, Bhdr *b, ulong nsize)
+{
+ if(b->magic == FREE_MAGIC) {
+ Alloc *a;
+ Bhdr *bnxt;
+ a = pooldel(p, (Free*)b);
+ blockcheck(p, a);
+ blocksetsize(a, nsize);
+ blockcheck(p, a);
+ bnxt = B2NB(a);
+ if(bnxt->magic == FREE_MAGIC)
+ a = blockmerge(p, a, bnxt);
+ blockcheck(p, a);
+ pooladd(p, a);
+ } else {
+ Alloc *a;
+ ulong dsize;
+
+ a = (Alloc*)b;
+ dsize = getdsize(a);
+ blocksetsize(a, nsize);
+ trim(p, a, dsize);
+ }
+}
+
+/* arenamerge: attempt to coalesce to arenas that might be adjacent */
+static Arena*
+arenamerge(Pool *p, Arena *bot, Arena *top)
+{
+ Bhdr *bbot, *btop;
+ Btail *t;
+
+ blockcheck(p, bot);
+ blockcheck(p, top);
+ assert(bot->aup == top && top > bot);
+
+ if(p->merge == nil || p->merge(bot, top) == 0)
+ return nil;
+
+ /* remove top from list */
+ if(bot->aup = top->aup) /* assign = */
+ bot->aup->down = bot;
+ else
+ p->arenalist = bot;
+
+ /* save ptrs to last block in bot, first block in top */
+ t = B2PT(A2TB(bot));
+ bbot = T2HDR(t);
+ btop = A2B(top);
+ blockcheck(p, bbot);
+ blockcheck(p, btop);
+
+ /* grow bottom arena to encompass top */
+ arenasetsize(bot, top->asize + ((uchar*)top - (uchar*)bot));
+
+ /* grow bottom block to encompass space between arenas */
+ blockgrow(p, bbot, (uchar*)btop-(uchar*)bbot);
+ blockcheck(p, bbot);
+ return bot;
+}
+
+/* dumpblock: print block's vital stats */
+static void
+dumpblock(Pool *p, Bhdr *b)
+{
+ ulong *dp;
+ ulong dsize;
+ uchar *cp;
+
+ dp = (ulong*)b;
+ p->print(p, "pool %s block %p\nhdr %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\n",
+ p->name, b, dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6]);
+
+ dp = (ulong*)B2T(b);
+ p->print(p, "tail %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux | %.8lux %.8lux\n",
+ dp[-6], dp[-5], dp[-4], dp[-3], dp[-2], dp[-1], dp[0], dp[1]);
+
+ if(b->magic == ALLOC_MAGIC){
+ dsize = getdsize((Alloc*)b);
+ if(dsize >= b->size) /* user data size corrupt */
+ return;
+
+ cp = (uchar*)_B2D(b)+dsize;
+ p->print(p, "user data ");
+ p->print(p, "%.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux",
+ cp[-8], cp[-7], cp[-6], cp[-5], cp[-4], cp[-3], cp[-2], cp[-1]);
+ p->print(p, " | %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ }
+}
+
+static void
+printblock(Pool *p, Bhdr *b, char *msg)
+{
+ p->print(p, "%s\n", msg);
+ dumpblock(p, b);
+}
+
+static void
+panicblock(Pool *p, Bhdr *b, char *msg)
+{
+ p->print(p, "%s\n", msg);
+ dumpblock(p, b);
+ p->panic(p, "pool panic");
+}
+
+/* blockcheck: ensure a block consistent with our expectations */
+/* should only be called when holding pool lock */
+static void
+blockcheck(Pool *p, Bhdr *b)
+{
+ Alloc *a;
+ Btail *t;
+ int i, n;
+ uchar *q, *bq, *eq;
+ ulong dsize;
+
+ switch(b->magic) {
+ default:
+ panicblock(p, b, "bad magic");
+ case FREE_MAGIC:
+ case UNALLOC_MAGIC:
+ t = B2T(b);
+ if(t->magic0 != TAIL_MAGIC0 || t->magic1 != TAIL_MAGIC1)
+ panicblock(p, b, "corrupt tail magic");
+ if(T2HDR(t) != b)
+ panicblock(p, b, "corrupt tail ptr");
+ break;
+ case DEAD_MAGIC:
+ t = B2T(b);
+ if(t->magic0 != TAIL_MAGIC0 || t->magic1 != TAIL_MAGIC1)
+ panicblock(p, b, "corrupt tail magic");
+ if(T2HDR(t) != b)
+ panicblock(p, b, "corrupt tail ptr");
+ n = getdsize((Alloc*)b);
+ q = _B2D(b);
+ q += 8;
+ for(i=8; i<n; i++)
+ if(*q++ != 0xDA)
+ panicblock(p, b, "dangling pointer write");
+ break;
+ case ARENA_MAGIC:
+ b = A2TB((Arena*)b);
+ if(b->magic != ARENATAIL_MAGIC)
+ panicblock(p, b, "bad arena size");
+ /* fall through */
+ case ARENATAIL_MAGIC:
+ if(b->size != 0)
+ panicblock(p, b, "bad arena tail size");
+ break;
+ case ALLOC_MAGIC:
+ a = (Alloc*)b;
+ t = B2T(b);
+ dsize = getdsize(a);
+ bq = (uchar*)_B2D(a)+dsize;
+ eq = (uchar*)t;
+
+ if(t->magic0 != TAIL_MAGIC0){
+ /* if someone wrote exactly one byte over and it was a NUL, we sometimes only complain. */
+ if((p->flags & POOL_TOLERANCE) && bq == eq && t->magic0 == 0)
+ printblock(p, b, "mem user overflow (magic0)");
+ else
+ panicblock(p, b, "corrupt tail magic0");
+ }
+
+ if(t->magic1 != TAIL_MAGIC1)
+ panicblock(p, b, "corrupt tail magic1");
+ if(T2HDR(t) != b)
+ panicblock(p, b, "corrupt tail ptr");
+
+ if(dsize2bsize(p, dsize) > a->size)
+ panicblock(p, b, "too much block data");
+
+ if(eq > bq+4)
+ eq = bq+4;
+ for(q=bq; q<eq; q++){
+ if(*q != datamagic[((uintptr)q)%nelem(datamagic)]){
+ if(q == bq && *q == 0 && (p->flags & POOL_TOLERANCE)){
+ printblock(p, b, "mem user overflow");
+ continue;
+ }
+ panicblock(p, b, "mem user overflow");
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * compact an arena by shifting all the free blocks to the end.
+ * assumes pool lock is held.
+ */
+enum {
+ FLOATING_MAGIC = 0xCBCBCBCB, /* temporarily neither allocated nor in the free tree */
+};
+
+static int
+arenacompact(Pool *p, Arena *a)
+{
+ Bhdr *b, *wb, *eb, *nxt;
+ int compacted;
+
+ if(p->move == nil)
+ p->panic(p, "don't call me when pool->move is nil\n");
+
+ poolcheckarena(p, a);
+ eb = A2TB(a);
+ compacted = 0;
+ for(b=wb=A2B(a); b && b < eb; b=nxt) {
+ nxt = B2NB(b);
+ switch(b->magic) {
+ case FREE_MAGIC:
+ pooldel(p, (Free*)b);
+ b->magic = FLOATING_MAGIC;
+ break;
+ case ALLOC_MAGIC:
+ if(wb != b) {
+ memmove(wb, b, b->size);
+ p->move(_B2D(b), _B2D(wb));
+ compacted = 1;
+ }
+ wb = B2NB(wb);
+ break;
+ }
+ }
+
+ /*
+ * the only free data is now at the end of the arena, pointed
+ * at by wb. all we need to do is set its size and get out.
+ */
+ if(wb < eb) {
+ wb->magic = UNALLOC_MAGIC;
+ blocksetsize(wb, (uchar*)eb-(uchar*)wb);
+ pooladd(p, (Alloc*)wb);
+ }
+
+ return compacted;
+}
+
+/*
+ * compact a pool by compacting each individual arena.
+ * 'twould be nice to shift blocks from one arena to the
+ * next but it's a pain to code.
+ */
+static int
+poolcompactl(Pool *pool)
+{
+ Arena *a;
+ int compacted;
+
+ if(pool->move == nil || pool->lastcompact == pool->nfree)
+ return 0;
+
+ pool->lastcompact = pool->nfree;
+ compacted = 0;
+ for(a=pool->arenalist; a; a=a->down)
+ compacted |= arenacompact(pool, a);
+ return compacted;
+}
+
+/*
+static int
+poolcompactl(Pool*)
+{
+ return 0;
+}
+*/
+
+/*
+ * Actual allocators
+ */
+
+/*
+static void*
+_B2D(void *a)
+{
+ return (uchar*)a+sizeof(Bhdr);
+}
+*/
+
+static void*
+B2D(Pool *p, Alloc *a)
+{
+ if(a->magic != ALLOC_MAGIC)
+ p->panic(p, "B2D called on unworthy block");
+ return _B2D(a);
+}
+
+/*
+static void*
+_D2B(void *v)
+{
+ Alloc *a;
+ a = (Alloc*)((uchar*)v-sizeof(Bhdr));
+ return a;
+}
+*/
+
+static Alloc*
+D2B(Pool *p, void *v)
+{
+ Alloc *a;
+ ulong *u;
+
+ if((uintptr)v&(sizeof(ulong)-1))
+ v = (char*)v - ((uintptr)v&(sizeof(ulong)-1));
+ u = v;
+ while(u[-1] == ALIGN_MAGIC)
+ u--;
+ a = _D2B(u);
+ if(a->magic != ALLOC_MAGIC)
+ p->panic(p, "D2B called on non-block %p (double-free?)", v);
+ return a;
+}
+
+/* poolallocl: attempt to allocate block to hold dsize user bytes; assumes lock held */
+static void*
+poolallocl(Pool *p, ulong dsize)
+{
+ ulong bsize;
+ Free *fb;
+ Alloc *ab;
+
+ if(dsize >= 0x80000000UL){ /* for sanity, overflow */
+ werrstr("invalid allocation size");
+ return nil;
+ }
+
+ bsize = dsize2bsize(p, dsize);
+
+ fb = treelookupgt(p->freeroot, bsize);
+ if(fb == nil) {
+ poolnewarena(p, bsize2asize(p, bsize));
+ if((fb = treelookupgt(p->freeroot, bsize)) == nil) {
+ /* assume poolnewarena failed and set %r */
+ return nil;
+ }
+ }
+
+ ab = trim(p, pooldel(p, fb), dsize);
+ p->curalloc += ab->size;
+ antagonism {
+ memset(B2D(p, ab), 0xDF, dsize);
+ }
+ return B2D(p, ab);
+}
+
+/* poolreallocl: attempt to grow v to ndsize bytes; assumes lock held */
+static void*
+poolreallocl(Pool *p, void *v, ulong ndsize)
+{
+ Alloc *a;
+ Bhdr *left, *right, *newb;
+ Btail *t;
+ ulong nbsize;
+ ulong odsize;
+ ulong obsize;
+ void *nv;
+
+ if(v == nil) /* for ANSI */
+ return poolallocl(p, ndsize);
+ if(ndsize == 0) {
+ poolfreel(p, v);
+ return nil;
+ }
+ a = D2B(p, v);
+ blockcheck(p, a);
+ odsize = getdsize(a);
+ obsize = a->size;
+
+ /* can reuse the same block? */
+ nbsize = dsize2bsize(p, ndsize);
+ if(nbsize <= a->size) {
+ Returnblock:
+ if(v != _B2D(a))
+ memmove(_B2D(a), v, odsize);
+ a = trim(p, a, ndsize);
+ p->curalloc -= obsize;
+ p->curalloc += a->size;
+ v = B2D(p, a);
+ return v;
+ }
+
+ /* can merge with surrounding blocks? */
+ right = B2NB(a);
+ if(right->magic == FREE_MAGIC && a->size+right->size >= nbsize) {
+ a = blockmerge(p, a, right);
+ goto Returnblock;
+ }
+
+ t = B2PT(a);
+ left = T2HDR(t);
+ if(left->magic == FREE_MAGIC && left->size+a->size >= nbsize) {
+ a = blockmerge(p, left, a);
+ goto Returnblock;
+ }
+
+ if(left->magic == FREE_MAGIC && right->magic == FREE_MAGIC
+ && left->size+a->size+right->size >= nbsize) {
+ a = blockmerge(p, blockmerge(p, left, a), right);
+ goto Returnblock;
+ }
+
+ if((nv = poolallocl(p, ndsize)) == nil)
+ return nil;
+
+ /* maybe the new block is next to us; if so, merge */
+ left = T2HDR(B2PT(a));
+ right = B2NB(a);
+ newb = D2B(p, nv);
+ if(left == newb || right == newb) {
+ if(left == newb || left->magic == FREE_MAGIC)
+ a = blockmerge(p, left, a);
+ if(right == newb || right->magic == FREE_MAGIC)
+ a = blockmerge(p, a, right);
+ assert(a->size >= nbsize);
+ goto Returnblock;
+ }
+
+ /* enough cleverness */
+ memmove(nv, v, odsize);
+ antagonism {
+ memset((char*)nv+odsize, 0xDE, ndsize-odsize);
+ }
+ poolfreel(p, v);
+ return nv;
+}
+
+static void*
+alignptr(void *v, ulong align, long offset)
+{
+ char *c;
+ ulong off;
+
+ c = v;
+ if(align){
+ off = (uintptr)c%align;
+ if(off != offset){
+ c += offset - off;
+ if(off > offset)
+ c += align;
+ }
+ }
+ return c;
+}
+
+/* poolspanallocl: allocate as described below; assumes pool locked */
+static void*
+poolallocalignl(Pool *p, ulong dsize, ulong align, long offset, ulong span)
+{
+ ulong asize;
+ void *v;
+ char *c;
+ ulong *u;
+ int skip;
+ Alloc *b;
+
+ /*
+ * allocate block
+ * dsize bytes
+ * addr == offset (modulo align)
+ * does not cross span-byte block boundary
+ *
+ * to satisfy alignment, just allocate an extra
+ * align bytes and then shift appropriately.
+ *
+ * to satisfy span, try once and see if we're
+ * lucky. the second time, allocate 2x asize
+ * so that we definitely get one not crossing
+ * the boundary.
+ */
+ if(align){
+ if(offset < 0)
+ offset = align - ((-offset)%align);
+ else
+ offset %= align;
+ }
+ asize = dsize+align;
+ v = poolallocl(p, asize);
+ if(v == nil)
+ return nil;
+ if(span && (uintptr)v/span != ((uintptr)v+asize)/span){
+ /* try again */
+ poolfreel(p, v);
+ v = poolallocl(p, 2*asize);
+ if(v == nil)
+ return nil;
+ }
+
+ /*
+ * figure out what pointer we want to return
+ */
+ c = alignptr(v, align, offset);
+ if(span && (uintptr)c/span != (uintptr)(c+dsize-1)/span){
+ c += span - (uintptr)c%span;
+ c = alignptr(c, align, offset);
+ if((uintptr)c/span != (uintptr)(c+dsize-1)/span){
+ poolfreel(p, v);
+ werrstr("cannot satisfy dsize %lud span %lud with align %lud+%ld", dsize, span, align, offset);
+ return nil;
+ }
+ }
+ skip = c - (char*)v;
+
+ /*
+ * free up the skip bytes before that pointer
+ * or mark it as unavailable.
+ */
+ b = _D2B(v);
+ b = freefromfront(p, b, skip);
+ v = _B2D(b);
+ skip = c - (char*)v;
+ if(c > (char*)v){
+ u = v;
+ while(c >= (char*)u+sizeof(ulong))
+ *u++ = ALIGN_MAGIC;
+ }
+ trim(p, b, skip+dsize);
+ assert(D2B(p, c) == b);
+ antagonism {
+ memset(c, 0xDD, dsize);
+ }
+ return c;
+}
+
+/* poolfree: free block obtained from poolalloc; assumes lock held */
+static void
+poolfreel(Pool *p, void *v)
+{
+ Alloc *ab;
+ Bhdr *back, *fwd;
+
+ if(v == nil) /* for ANSI */
+ return;
+
+ ab = D2B(p, v);
+ blockcheck(p, ab);
+
+ if(p->flags&POOL_NOREUSE){
+ int n;
+
+ ab->magic = DEAD_MAGIC;
+ n = getdsize(ab)-8;
+ if(n > 0)
+ memset((uchar*)v+8, 0xDA, n);
+ return;
+ }
+
+ p->nfree++;
+ p->curalloc -= ab->size;
+ back = T2HDR(B2PT(ab));
+ if(back->magic == FREE_MAGIC)
+ ab = blockmerge(p, back, ab);
+
+ fwd = B2NB(ab);
+ if(fwd->magic == FREE_MAGIC)
+ ab = blockmerge(p, ab, fwd);
+
+ pooladd(p, ab);
+}
+
+void*
+poolalloc(Pool *p, ulong n)
+{
+ void *v;
+
+ p->lock(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ v = poolallocl(p, n);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ if(p->logstack && (p->flags & POOL_LOGGING)) p->logstack(p);
+ LOG(p, "poolalloc %p %lud = %p\n", p, n, v);
+ p->unlock(p);
+ return v;
+}
+
+void*
+poolallocalign(Pool *p, ulong n, ulong align, long offset, ulong span)
+{
+ void *v;
+
+ p->lock(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ v = poolallocalignl(p, n, align, offset, span);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ if(p->logstack && (p->flags & POOL_LOGGING)) p->logstack(p);
+ LOG(p, "poolalignspanalloc %p %lud %lud %lud %ld = %p\n", p, n, align, span, offset, v);
+ p->unlock(p);
+ return v;
+}
+
+int
+poolcompact(Pool *p)
+{
+ int rv;
+
+ p->lock(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ rv = poolcompactl(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ LOG(p, "poolcompact %p\n", p);
+ p->unlock(p);
+ return rv;
+}
+
+void*
+poolrealloc(Pool *p, void *v, ulong n)
+{
+ void *nv;
+
+ p->lock(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ nv = poolreallocl(p, v, n);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ if(p->logstack && (p->flags & POOL_LOGGING)) p->logstack(p);
+ LOG(p, "poolrealloc %p %p %ld = %p\n", p, v, n, nv);
+ p->unlock(p);
+ return nv;
+}
+
+void
+poolfree(Pool *p, void *v)
+{
+ p->lock(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ poolfreel(p, v);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ if(p->logstack && (p->flags & POOL_LOGGING)) p->logstack(p);
+ LOG(p, "poolfree %p %p\n", p, v);
+ p->unlock(p);
+}
+
+/*
+ * Return the real size of a block, and let the user use it.
+ */
+ulong
+poolmsize(Pool *p, void *v)
+{
+ Alloc *b;
+ ulong dsize;
+
+ p->lock(p);
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ if(v == nil) /* consistency with other braindead ANSI-ness */
+ dsize = 0;
+ else {
+ b = D2B(p, v);
+ dsize = (b->size&~(p->quantum-1)) - sizeof(Bhdr) - sizeof(Btail);
+ assert(dsize >= getdsize(b));
+ blocksetdsize(p, b, dsize);
+ }
+ paranoia {
+ poolcheckl(p);
+ }
+ verbosity {
+ pooldumpl(p);
+ }
+ if(p->logstack && (p->flags & POOL_LOGGING)) p->logstack(p);
+ LOG(p, "poolmsize %p %p = %ld\n", p, v, dsize);
+ p->unlock(p);
+ return dsize;
+}
+
+/*
+ * Debugging
+ */
+
+static void
+poolcheckarena(Pool *p, Arena *a)
+{
+ Bhdr *b;
+ Bhdr *atail;
+
+ atail = A2TB(a);
+ for(b=a; b->magic != ARENATAIL_MAGIC && b<atail; b=B2NB(b))
+ blockcheck(p, b);
+ blockcheck(p, b);
+ if(b != atail)
+ p->panic(p, "found wrong tail");
+}
+
+static void
+poolcheckl(Pool *p)
+{
+ Arena *a;
+
+ for(a=p->arenalist; a; a=a->down)
+ poolcheckarena(p, a);
+ if(p->freeroot)
+ checktree(p->freeroot, 0, 1<<30);
+}
+
+void
+poolcheck(Pool *p)
+{
+ p->lock(p);
+ poolcheckl(p);
+ p->unlock(p);
+}
+
+void
+poolblockcheck(Pool *p, void *v)
+{
+ if(v == nil)
+ return;
+
+ p->lock(p);
+ blockcheck(p, D2B(p, v));
+ p->unlock(p);
+}
+
+static void
+pooldumpl(Pool *p)
+{
+ Arena *a;
+
+ p->print(p, "pool %p %s\n", p, p->name);
+ for(a=p->arenalist; a; a=a->down)
+ pooldumparena(p, a);
+}
+
+void
+pooldump(Pool *p)
+{
+ p->lock(p);
+ pooldumpl(p);
+ p->unlock(p);
+}
+
+static void
+pooldumparena(Pool *p, Arena *a)
+{
+ Bhdr *b;
+
+ for(b=a; b->magic != ARENATAIL_MAGIC; b=B2NB(b))
+ p->print(p, "(%p %.8lux %lud)", b, b->magic, b->size);
+ p->print(p, "\n");
+}
+
+/*
+ * mark the memory in such a way that we know who marked it
+ * (via the signature) and we know where the marking started.
+ */
+static void
+memmark(void *v, int sig, ulong size)
+{
+ uchar *p, *ep;
+ ulong *lp, *elp;
+ lp = v;
+ elp = lp+size/4;
+ while(lp < elp)
+ *lp++ = (sig<<24) ^ ((uintptr)lp-(uintptr)v);
+ p = (uchar*)lp;
+ ep = (uchar*)v+size;
+ while(p<ep)
+ *p++ = sig;
+}
diff --git a/sys/src/libc/port/pow.c b/sys/src/libc/port/pow.c
new file mode 100755
index 000000000..0a5a0b5cd
--- /dev/null
+++ b/sys/src/libc/port/pow.c
@@ -0,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+
+double
+pow(double x, double y) /* return x ^ y (exponentiation) */
+{
+ double xy, y1, ye;
+ long i;
+ int ex, ey, flip;
+
+ if(y == 0.0)
+ return 1.0;
+
+ flip = 0;
+ if(y < 0.){
+ y = -y;
+ flip = 1;
+ }
+ y1 = modf(y, &ye);
+ if(y1 != 0.0){
+ if(x <= 0.)
+ goto zreturn;
+ if(y1 > 0.5) {
+ y1 -= 1.;
+ ye += 1.;
+ }
+ xy = exp(y1 * log(x));
+ }else
+ xy = 1.0;
+ if(ye > 0x7FFFFFFF){ /* should be ~0UL but compiler can't convert double to ulong */
+ if(x <= 0.){
+ zreturn:
+ if(x==0. && !flip)
+ return 0.;
+ return NaN();
+ }
+ if(flip){
+ if(y == .5)
+ return 1./sqrt(x);
+ y = -y;
+ }else if(y == .5)
+ return sqrt(x);
+ return exp(y * log(x));
+ }
+ x = frexp(x, &ex);
+ ey = 0;
+ i = ye;
+ if(i)
+ for(;;){
+ if(i & 1){
+ xy *= x;
+ ey += ex;
+ }
+ i >>= 1;
+ if(i == 0)
+ break;
+ x *= x;
+ ex <<= 1;
+ if(x < .5){
+ x += x;
+ ex -= 1;
+ }
+ }
+ if(flip){
+ xy = 1. / xy;
+ ey = -ey;
+ }
+ return ldexp(xy, ey);
+}
diff --git a/sys/src/libc/port/pow10.c b/sys/src/libc/port/pow10.c
new file mode 100755
index 000000000..7e09f3cc4
--- /dev/null
+++ b/sys/src/libc/port/pow10.c
@@ -0,0 +1,49 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * this table might overflow 127-bit exponent representations.
+ * in that case, truncate it after 1.0e38.
+ * it is important to get all one can from this
+ * routine since it is used in atof to scale numbers.
+ * the presumption is that C converts fp numbers better
+ * than multipication of lower powers of 10.
+ */
+static
+double tab[] =
+{
+ 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
+ 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19,
+ 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29,
+ 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39,
+ 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
+ 1.0e50, 1.0e51, 1.0e52, 1.0e53, 1.0e54, 1.0e55, 1.0e56, 1.0e57, 1.0e58, 1.0e59,
+ 1.0e60, 1.0e61, 1.0e62, 1.0e63, 1.0e64, 1.0e65, 1.0e66, 1.0e67, 1.0e68, 1.0e69,
+ 1.0e70, 1.0e71, 1.0e72, 1.0e73, 1.0e74, 1.0e75, 1.0e76, 1.0e77, 1.0e78, 1.0e79,
+ 1.0e80, 1.0e81, 1.0e82, 1.0e83, 1.0e84, 1.0e85, 1.0e86, 1.0e87, 1.0e88, 1.0e89,
+ 1.0e90, 1.0e91, 1.0e92, 1.0e93, 1.0e94, 1.0e95, 1.0e96, 1.0e97, 1.0e98, 1.0e99,
+ 1.0e100,1.0e101,1.0e102,1.0e103,1.0e104,1.0e105,1.0e106,1.0e107,1.0e108,1.0e109,
+ 1.0e110,1.0e111,1.0e112,1.0e113,1.0e114,1.0e115,1.0e116,1.0e117,1.0e118,1.0e119,
+ 1.0e120,1.0e121,1.0e122,1.0e123,1.0e124,1.0e125,1.0e126,1.0e127,1.0e128,1.0e129,
+ 1.0e130,1.0e131,1.0e132,1.0e133,1.0e134,1.0e135,1.0e136,1.0e137,1.0e138,1.0e139,
+ 1.0e140,1.0e141,1.0e142,1.0e143,1.0e144,1.0e145,1.0e146,1.0e147,1.0e148,1.0e149,
+ 1.0e150,1.0e151,1.0e152,1.0e153,1.0e154,1.0e155,1.0e156,1.0e157,1.0e158,1.0e159,
+};
+
+double
+pow10(int n)
+{
+ int m;
+
+ if(n < 0) {
+ n = -n;
+ if(n < sizeof(tab)/sizeof(tab[0]))
+ return 1/tab[n];
+ m = n/2;
+ return 1/(pow10(m) * pow10(n-m));
+ }
+ if(n < sizeof(tab)/sizeof(tab[0]))
+ return tab[n];
+ m = n/2;
+ return pow10(m) * pow10(n-m);
+}
diff --git a/sys/src/libc/port/profile.c b/sys/src/libc/port/profile.c
new file mode 100755
index 000000000..5647e3bf1
--- /dev/null
+++ b/sys/src/libc/port/profile.c
@@ -0,0 +1,280 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+extern long _callpc(void**);
+extern long _savearg(void);
+
+static ulong khz;
+static ulong perr;
+static int havecycles;
+
+typedef struct Plink Plink;
+struct Plink
+{
+ Plink *old;
+ Plink *down;
+ Plink *link;
+ long pc;
+ long count;
+ vlong time;
+};
+
+#pragma profile off
+
+ulong
+_profin(void)
+{
+ void *dummy;
+ long pc;
+ Plink *pp, *p;
+ ulong arg;
+ vlong t;
+
+ arg = _savearg();
+ pc = _callpc(&dummy);
+ pp = _tos->prof.pp;
+ if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
+ return arg;
+
+ for(p=pp->down; p; p=p->link)
+ if(p->pc == pc)
+ goto out;
+ p = _tos->prof.next + 1;
+ if(p >= _tos->prof.last) {
+ _tos->prof.pp = 0;
+ perr++;
+ return arg;
+ }
+ _tos->prof.next = p;
+ p->link = pp->down;
+ pp->down = p;
+ p->pc = pc;
+ p->old = pp;
+ p->down = 0;
+ p->count = 0;
+ p->time = 0LL;
+
+out:
+ _tos->prof.pp = p;
+ p->count++;
+ switch(_tos->prof.what){
+ case Profkernel:
+ p->time = p->time - _tos->pcycles;
+ goto proftime;
+ case Profuser:
+ /* Add kernel cycles on proc entry */
+ p->time = p->time + _tos->kcycles;
+ /* fall through */
+ case Proftime:
+ proftime: /* Subtract cycle counter on proc entry */
+ cycles((uvlong*)&t);
+ p->time = p->time - t;
+ break;
+ case Profsample:
+ p->time = p->time - _tos->clock;
+ break;
+ }
+ return arg; /* disgusting linkage */
+}
+
+ulong
+_profout(void)
+{
+ Plink *p;
+ ulong arg;
+ vlong t;
+
+ arg = _savearg();
+ p = _tos->prof.pp;
+ if (p == nil || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
+ return arg; /* Not our process */
+ switch(_tos->prof.what){
+ case Profkernel: /* Add proc cycles on proc entry */
+ p->time = p->time + _tos->pcycles;
+ goto proftime;
+ case Profuser: /* Subtract kernel cycles on proc entry */
+ p->time = p->time - _tos->kcycles;
+ /* fall through */
+ case Proftime:
+ proftime: /* Add cycle counter on proc entry */
+ cycles((uvlong*)&t);
+ p->time = p->time + t;
+ break;
+ case Profsample:
+ p->time = p->time + _tos->clock;
+ break;
+ }
+ _tos->prof.pp = p->old;
+ return arg;
+}
+
+void
+_profdump(void)
+{
+ int f;
+ long n;
+ Plink *p;
+ char *vp;
+ char filename[64];
+
+ if (_tos->prof.what == 0)
+ return; /* No profiling */
+ if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
+ return; /* Not our process */
+ if(perr)
+ fprint(2, "%lud Prof errors\n", perr);
+ _tos->prof.pp = nil;
+ if (_tos->prof.pid)
+ snprint(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
+ else
+ snprint(filename, sizeof filename - 1, "prof.out");
+ f = create(filename, 1, 0666);
+ if(f < 0) {
+ perror("create prof.out");
+ return;
+ }
+ _tos->prof.pid = ~0; /* make sure data gets dumped once */
+ switch(_tos->prof.what){
+ case Profkernel:
+ cycles((uvlong*)&_tos->prof.first->time);
+ _tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
+ break;
+ case Profuser:
+ cycles((uvlong*)&_tos->prof.first->time);
+ _tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
+ break;
+ case Proftime:
+ cycles((uvlong*)&_tos->prof.first->time);
+ break;
+ case Profsample:
+ _tos->prof.first->time = _tos->clock;
+ break;
+ }
+ vp = (char*)_tos->prof.first;
+
+ for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
+
+ /*
+ * short down
+ */
+ n = 0xffff;
+ if(p->down)
+ n = p->down - _tos->prof.first;
+ vp[0] = n>>8;
+ vp[1] = n;
+
+ /*
+ * short right
+ */
+ n = 0xffff;
+ if(p->link)
+ n = p->link - _tos->prof.first;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+
+ /*
+ * long pc
+ */
+ n = p->pc;
+ vp[0] = n>>24;
+ vp[1] = n>>16;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+
+ /*
+ * long count
+ */
+ n = p->count;
+ vp[0] = n>>24;
+ vp[1] = n>>16;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+
+ /*
+ * vlong time
+ */
+ if (havecycles){
+ n = (vlong)(p->time / (vlong)khz);
+ }else
+ n = p->time;
+
+ vp[0] = n>>24;
+ vp[1] = n>>16;
+ vp[2] = n>>8;
+ vp[3] = n;
+ vp += 4;
+ }
+ write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
+ close(f);
+}
+
+void
+_profinit(int entries, int what)
+{
+ if (_tos->prof.what == 0)
+ return; /* Profiling not linked in */
+ _tos->prof.pp = nil;
+ _tos->prof.first = mallocz(entries*sizeof(Plink),1);
+ _tos->prof.last = _tos->prof.first + entries;
+ _tos->prof.next = _tos->prof.first;
+ _tos->prof.pid = _tos->pid;
+ _tos->prof.what = what;
+ _tos->clock = 1;
+}
+
+void
+_profmain(void)
+{
+ char ename[50];
+ int n, f;
+
+ n = 2000;
+ if (_tos->cyclefreq != 0LL){
+ khz = _tos->cyclefreq / 1000; /* Report times in milliseconds */
+ havecycles = 1;
+ }
+ f = open("/env/profsize", OREAD);
+ if(f >= 0) {
+ memset(ename, 0, sizeof(ename));
+ read(f, ename, sizeof(ename)-1);
+ close(f);
+ n = atol(ename);
+ }
+ _tos->prof.what = Profuser;
+ f = open("/env/proftype", OREAD);
+ if(f >= 0) {
+ memset(ename, 0, sizeof(ename));
+ read(f, ename, sizeof(ename)-1);
+ close(f);
+ if (strcmp(ename, "user") == 0)
+ _tos->prof.what = Profuser;
+ else if (strcmp(ename, "kernel") == 0)
+ _tos->prof.what = Profkernel;
+ else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
+ _tos->prof.what = Proftime;
+ else if (strcmp(ename, "sample") == 0)
+ _tos->prof.what = Profsample;
+ }
+ _tos->prof.first = sbrk(n*sizeof(Plink));
+ _tos->prof.last = sbrk(0);
+ _tos->prof.next = _tos->prof.first;
+ _tos->prof.pp = nil;
+ _tos->prof.pid = _tos->pid;
+ atexit(_profdump);
+ _tos->clock = 1;
+}
+
+void prof(void (*fn)(void*), void *arg, int entries, int what)
+{
+ _profinit(entries, what);
+ _tos->prof.pp = _tos->prof.next;
+ fn(arg);
+ _profdump();
+}
+
+#pragma profile on
+
diff --git a/sys/src/libc/port/qsort.c b/sys/src/libc/port/qsort.c
new file mode 100755
index 000000000..91c768c77
--- /dev/null
+++ b/sys/src/libc/port/qsort.c
@@ -0,0 +1,124 @@
+/*
+ * qsort -- simple quicksort
+ */
+
+#include <u.h>
+
+typedef
+struct
+{
+ int (*cmp)(void*, void*);
+ void (*swap)(char*, char*, long);
+ long es;
+} Sort;
+
+static void
+swapb(char *i, char *j, long es)
+{
+ char c;
+
+ do {
+ c = *i;
+ *i++ = *j;
+ *j++ = c;
+ es--;
+ } while(es != 0);
+
+}
+
+static void
+swapi(char *ii, char *ij, long es)
+{
+ long *i, *j, c;
+
+ i = (long*)ii;
+ j = (long*)ij;
+ do {
+ c = *i;
+ *i++ = *j;
+ *j++ = c;
+ es -= sizeof(long);
+ } while(es != 0);
+}
+
+static char*
+pivot(char *a, long n, Sort *p)
+{
+ long j;
+ char *pi, *pj, *pk;
+
+ j = n/6 * p->es;
+ pi = a + j; /* 1/6 */
+ j += j;
+ pj = pi + j; /* 1/2 */
+ pk = pj + j; /* 5/6 */
+ if(p->cmp(pi, pj) < 0) {
+ if(p->cmp(pi, pk) < 0) {
+ if(p->cmp(pj, pk) < 0)
+ return pj;
+ return pk;
+ }
+ return pi;
+ }
+ if(p->cmp(pj, pk) < 0) {
+ if(p->cmp(pi, pk) < 0)
+ return pi;
+ return pk;
+ }
+ return pj;
+}
+
+static void
+qsorts(char *a, long n, Sort *p)
+{
+ long j, es;
+ char *pi, *pj, *pn;
+
+ es = p->es;
+ while(n > 1) {
+ if(n > 10) {
+ pi = pivot(a, n, p);
+ } else
+ pi = a + (n>>1)*es;
+
+ p->swap(a, pi, es);
+ pi = a;
+ pn = a + n*es;
+ pj = pn;
+ for(;;) {
+ do
+ pi += es;
+ while(pi < pn && p->cmp(pi, a) < 0);
+ do
+ pj -= es;
+ while(pj > a && p->cmp(pj, a) > 0);
+ if(pj < pi)
+ break;
+ p->swap(pi, pj, es);
+ }
+ p->swap(a, pj, es);
+ j = (pj - a) / es;
+
+ n = n-j-1;
+ if(j >= n) {
+ qsorts(a, j, p);
+ a += (j+1)*es;
+ } else {
+ qsorts(a + (j+1)*es, n, p);
+ n = j;
+ }
+ }
+}
+
+void
+qsort(void *va, long n, long es, int (*cmp)(void*, void*))
+{
+ Sort s;
+
+ s.cmp = cmp;
+ s.es = es;
+ s.swap = swapi;
+ if(((uintptr)va | es) % sizeof(long))
+ s.swap = swapb;
+ qsorts((char*)va, n, &s);
+}
diff --git a/sys/src/libc/port/quote.c b/sys/src/libc/port/quote.c
new file mode 100755
index 000000000..99f70084c
--- /dev/null
+++ b/sys/src/libc/port/quote.c
@@ -0,0 +1,135 @@
+#include <u.h>
+#include <libc.h>
+
+int (*doquote)(int);
+
+extern int _needsquotes(char*, int*);
+extern int _runeneedsquotes(Rune*, int*);
+
+char*
+unquotestrdup(char *s)
+{
+ char *t, *ret;
+ int quoting;
+
+ ret = s = strdup(s); /* return unquoted copy */
+ if(ret == nil)
+ return ret;
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || (*t!=' ' && *t!='\t'))){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(t != s)
+ memmove(s, t, strlen(t)+1);
+ return ret;
+}
+
+Rune*
+unquoterunestrdup(Rune *s)
+{
+ Rune *t, *ret;
+ int quoting;
+
+ ret = s = runestrdup(s); /* return unquoted copy */
+ if(ret == nil)
+ return ret;
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || (*t!=' ' && *t!='\t'))){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(t != s)
+ memmove(s, t, (runestrlen(t)+1)*sizeof(Rune));
+ return ret;
+}
+
+char*
+quotestrdup(char *s)
+{
+ char *t, *u, *ret;
+ int quotelen;
+ Rune r;
+
+ if(_needsquotes(s, &quotelen) == 0)
+ return strdup(s);
+
+ ret = malloc(quotelen+1);
+ if(ret == nil)
+ return nil;
+ u = ret;
+ *u++ = '\'';
+ for(t=s; *t; t++){
+ r = *t;
+ if(r == L'\'')
+ *u++ = r; /* double the quote */
+ *u++ = r;
+ }
+ *u++ = '\'';
+ *u = '\0';
+ return ret;
+}
+
+Rune*
+quoterunestrdup(Rune *s)
+{
+ Rune *t, *u, *ret;
+ int quotelen;
+ Rune r;
+
+ if(_runeneedsquotes(s, &quotelen) == 0)
+ return runestrdup(s);
+
+ ret = malloc((quotelen+1)*sizeof(Rune));
+ if(ret == nil)
+ return nil;
+ u = ret;
+ *u++ = '\'';
+ for(t=s; *t; t++){
+ r = *t;
+ if(r == L'\'')
+ *u++ = r; /* double the quote */
+ *u++ = r;
+ }
+ *u++ = '\'';
+ *u = '\0';
+ return ret;
+}
diff --git a/sys/src/libc/port/rand.c b/sys/src/libc/port/rand.c
new file mode 100755
index 000000000..366e9e967
--- /dev/null
+++ b/sys/src/libc/port/rand.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+int
+rand(void)
+{
+ return lrand() & 0x7fff;
+}
diff --git a/sys/src/libc/port/readn.c b/sys/src/libc/port/readn.c
new file mode 100755
index 000000000..629dfd9ed
--- /dev/null
+++ b/sys/src/libc/port/readn.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+long
+readn(int f, void *av, long n)
+{
+ char *a;
+ long m, t;
+
+ a = av;
+ t = 0;
+ while(t < n){
+ m = read(f, a+t, n-t);
+ if(m <= 0){
+ if(t == 0)
+ return m;
+ break;
+ }
+ t += m;
+ }
+ return t;
+}
diff --git a/sys/src/libc/port/reduce b/sys/src/libc/port/reduce
new file mode 100755
index 000000000..584f8b9ab
--- /dev/null
+++ b/sys/src/libc/port/reduce
@@ -0,0 +1,16 @@
+O=$1
+shift
+objtype=$1
+shift
+
+ls -p ../$objtype/*.[cs] >[2]/dev/null | sed 's/..$//;s/^/^/' > /tmp/reduce.$pid
+#
+# if empty directory, just return the input files
+#
+if (! ~ $status '|') {
+ echo $*
+ rm /tmp/reduce.$pid
+ exit 0
+}
+echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' '
+rm /tmp/reduce.$pid
diff --git a/sys/src/libc/port/rune.c b/sys/src/libc/port/rune.c
new file mode 100755
index 000000000..b62da9e66
--- /dev/null
+++ b/sys/src/libc/port/rune.c
@@ -0,0 +1,162 @@
+#include <u.h>
+#include <libc.h>
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror,
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+runenlen(Rune *r, int nrune)
+{
+ int nb, c;
+
+ nb = 0;
+ while(nrune--) {
+ c = *r++;
+ if(c <= Rune1)
+ nb++;
+ else
+ if(c <= Rune2)
+ nb += 2;
+ else
+ nb += 3;
+ }
+ return nb;
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(uchar*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/runestrcat.c b/sys/src/libc/port/runestrcat.c
new file mode 100755
index 000000000..4c8363067
--- /dev/null
+++ b/sys/src/libc/port/runestrcat.c
@@ -0,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrcat(Rune *s1, Rune *s2)
+{
+
+ runestrcpy(runestrchr(s1, 0), s2);
+ return s1;
+}
diff --git a/sys/src/libc/port/runestrchr.c b/sys/src/libc/port/runestrchr.c
new file mode 100755
index 000000000..af7fc4e88
--- /dev/null
+++ b/sys/src/libc/port/runestrchr.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrchr(Rune *s, Rune c)
+{
+ Rune c0 = c;
+ Rune c1;
+
+ if(c == 0) {
+ while(*s++)
+ ;
+ return s-1;
+ }
+
+ while(c1 = *s++)
+ if(c1 == c0)
+ return s-1;
+ return 0;
+}
diff --git a/sys/src/libc/port/runestrcmp.c b/sys/src/libc/port/runestrcmp.c
new file mode 100755
index 000000000..e3bda3738
--- /dev/null
+++ b/sys/src/libc/port/runestrcmp.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runestrcmp(Rune *s1, Rune *s2)
+{
+ Rune c1, c2;
+
+ for(;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ if(c1 == 0)
+ return 0;
+ }
+}
diff --git a/sys/src/libc/port/runestrcpy.c b/sys/src/libc/port/runestrcpy.c
new file mode 100755
index 000000000..efddc0734
--- /dev/null
+++ b/sys/src/libc/port/runestrcpy.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrcpy(Rune *s1, Rune *s2)
+{
+ Rune *os1;
+
+ os1 = s1;
+ while(*s1++ = *s2++)
+ ;
+ return os1;
+}
diff --git a/sys/src/libc/port/runestrdup.c b/sys/src/libc/port/runestrdup.c
new file mode 100755
index 000000000..f66b430b6
--- /dev/null
+++ b/sys/src/libc/port/runestrdup.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrdup(Rune *s)
+{
+ Rune *ns;
+
+ ns = malloc(sizeof(Rune)*(runestrlen(s) + 1));
+ if(ns == 0)
+ return 0;
+
+ return runestrcpy(ns, s);
+}
diff --git a/sys/src/libc/port/runestrecpy.c b/sys/src/libc/port/runestrecpy.c
new file mode 100755
index 000000000..d3ca81044
--- /dev/null
+++ b/sys/src/libc/port/runestrecpy.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrecpy(Rune *s1, Rune *es1, Rune *s2)
+{
+ if(s1 >= es1)
+ return s1;
+
+ while(*s1++ = *s2++){
+ if(s1 == es1){
+ *--s1 = '\0';
+ break;
+ }
+ }
+ return s1;
+}
diff --git a/sys/src/libc/port/runestrlen.c b/sys/src/libc/port/runestrlen.c
new file mode 100755
index 000000000..2bf4a4eb6
--- /dev/null
+++ b/sys/src/libc/port/runestrlen.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+long
+runestrlen(Rune *s)
+{
+
+ return runestrchr(s, 0) - s;
+}
diff --git a/sys/src/libc/port/runestrncat.c b/sys/src/libc/port/runestrncat.c
new file mode 100755
index 000000000..9d6ee46a6
--- /dev/null
+++ b/sys/src/libc/port/runestrncat.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrncat(Rune *s1, Rune *s2, long n)
+{
+ Rune *os1;
+
+ os1 = s1;
+ s1 = runestrchr(s1, 0);
+ while(*s1++ = *s2++)
+ if(--n < 0) {
+ s1[-1] = 0;
+ break;
+ }
+ return os1;
+}
diff --git a/sys/src/libc/port/runestrncmp.c b/sys/src/libc/port/runestrncmp.c
new file mode 100755
index 000000000..5e566fa0c
--- /dev/null
+++ b/sys/src/libc/port/runestrncmp.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+int
+runestrncmp(Rune *s1, Rune *s2, long n)
+{
+ Rune c1, c2;
+
+ while(n > 0) {
+ c1 = *s1++;
+ c2 = *s2++;
+ n--;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ if(c1 == 0)
+ break;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/runestrncpy.c b/sys/src/libc/port/runestrncpy.c
new file mode 100755
index 000000000..7e84e33f7
--- /dev/null
+++ b/sys/src/libc/port/runestrncpy.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrncpy(Rune *s1, Rune *s2, long n)
+{
+ int i;
+ Rune *os1;
+
+ os1 = s1;
+ for(i = 0; i < n; i++)
+ if((*s1++ = *s2++) == 0) {
+ while(++i < n)
+ *s1++ = 0;
+ return os1;
+ }
+ return os1;
+}
diff --git a/sys/src/libc/port/runestrrchr.c b/sys/src/libc/port/runestrrchr.c
new file mode 100755
index 000000000..4c8074ba0
--- /dev/null
+++ b/sys/src/libc/port/runestrrchr.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+Rune*
+runestrrchr(Rune *s, Rune c)
+{
+ Rune *r;
+
+ if(c == 0)
+ return runestrchr(s, 0);
+ r = 0;
+ while(s = runestrchr(s, c))
+ r = s++;
+ return r;
+}
diff --git a/sys/src/libc/port/runestrstr.c b/sys/src/libc/port/runestrstr.c
new file mode 100755
index 000000000..b7f6964b2
--- /dev/null
+++ b/sys/src/libc/port/runestrstr.c
@@ -0,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+Rune*
+runestrstr(Rune *s1, Rune *s2)
+{
+ Rune *p, *pa, *pb;
+ int c0, c;
+
+ c0 = *s2;
+ if(c0 == 0)
+ return s1;
+ s2++;
+ for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) {
+ pa = p;
+ for(pb=s2;; pb++) {
+ c = *pb;
+ if(c == 0)
+ return p;
+ if(c != *++pa)
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/runetype.c b/sys/src/libc/port/runetype.c
new file mode 100755
index 000000000..f7ec1bb98
--- /dev/null
+++ b/sys/src/libc/port/runetype.c
@@ -0,0 +1,1181 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * alpha ranges -
+ * only covers ranges not in lower||upper
+ */
+static
+Rune _alpha2[] =
+{
+ 0x00d8, 0x00f6, /* Ø - ö */
+ 0x00f8, 0x01f5, /* ø - ǵ */
+ 0x0250, 0x02a8, /* ɐ - ʨ */
+ 0x038e, 0x03a1, /* Ύ - Ρ */
+ 0x03a3, 0x03ce, /* Σ - ώ */
+ 0x03d0, 0x03d6, /* ϐ - ϖ */
+ 0x03e2, 0x03f3, /* Ϣ - ϳ */
+ 0x0490, 0x04c4, /* Ґ - ӄ */
+ 0x0561, 0x0587, /* ա - և */
+ 0x05d0, 0x05ea, /* א - ת */
+ 0x05f0, 0x05f2, /* װ - ײ */
+ 0x0621, 0x063a, /* ء - غ */
+ 0x0640, 0x064a, /* ـ - ي */
+ 0x0671, 0x06b7, /* ٱ - ڷ */
+ 0x06ba, 0x06be, /* ں - ھ */
+ 0x06c0, 0x06ce, /* ۀ - ێ */
+ 0x06d0, 0x06d3, /* ې - ۓ */
+ 0x0905, 0x0939, /* अ - ह */
+ 0x0958, 0x0961, /* क़ - ॡ */
+ 0x0985, 0x098c, /* অ - ঌ */
+ 0x098f, 0x0990, /* এ - ঐ */
+ 0x0993, 0x09a8, /* ও - ন */
+ 0x09aa, 0x09b0, /* প - র */
+ 0x09b6, 0x09b9, /* শ - হ */
+ 0x09dc, 0x09dd, /* ড় - ঢ় */
+ 0x09df, 0x09e1, /* য় - ৡ */
+ 0x09f0, 0x09f1, /* ৰ - ৱ */
+ 0x0a05, 0x0a0a, /* ਅ - ਊ */
+ 0x0a0f, 0x0a10, /* ਏ - ਐ */
+ 0x0a13, 0x0a28, /* ਓ - ਨ */
+ 0x0a2a, 0x0a30, /* ਪ - ਰ */
+ 0x0a32, 0x0a33, /* ਲ - ਲ਼ */
+ 0x0a35, 0x0a36, /* ਵ - ਸ਼ */
+ 0x0a38, 0x0a39, /* ਸ - ਹ */
+ 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */
+ 0x0a85, 0x0a8b, /* અ - ઋ */
+ 0x0a8f, 0x0a91, /* એ - ઑ */
+ 0x0a93, 0x0aa8, /* ઓ - ન */
+ 0x0aaa, 0x0ab0, /* પ - ર */
+ 0x0ab2, 0x0ab3, /* લ - ળ */
+ 0x0ab5, 0x0ab9, /* વ - હ */
+ 0x0b05, 0x0b0c, /* ଅ - ଌ */
+ 0x0b0f, 0x0b10, /* ଏ - ଐ */
+ 0x0b13, 0x0b28, /* ଓ - ନ */
+ 0x0b2a, 0x0b30, /* ପ - ର */
+ 0x0b32, 0x0b33, /* ଲ - ଳ */
+ 0x0b36, 0x0b39, /* ଶ - ହ */
+ 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */
+ 0x0b5f, 0x0b61, /* ୟ - ୡ */
+ 0x0b85, 0x0b8a, /* அ - ஊ */
+ 0x0b8e, 0x0b90, /* எ - ஐ */
+ 0x0b92, 0x0b95, /* ஒ - க */
+ 0x0b99, 0x0b9a, /* ங - ச */
+ 0x0b9e, 0x0b9f, /* ஞ - ட */
+ 0x0ba3, 0x0ba4, /* ண - த */
+ 0x0ba8, 0x0baa, /* ந - ப */
+ 0x0bae, 0x0bb5, /* ம - வ */
+ 0x0bb7, 0x0bb9, /* ஷ - ஹ */
+ 0x0c05, 0x0c0c, /* అ - ఌ */
+ 0x0c0e, 0x0c10, /* ఎ - ఐ */
+ 0x0c12, 0x0c28, /* ఒ - న */
+ 0x0c2a, 0x0c33, /* ప - ళ */
+ 0x0c35, 0x0c39, /* వ - హ */
+ 0x0c60, 0x0c61, /* ౠ - ౡ */
+ 0x0c85, 0x0c8c, /* ಅ - ಌ */
+ 0x0c8e, 0x0c90, /* ಎ - ಐ */
+ 0x0c92, 0x0ca8, /* ಒ - ನ */
+ 0x0caa, 0x0cb3, /* ಪ - ಳ */
+ 0x0cb5, 0x0cb9, /* ವ - ಹ */
+ 0x0ce0, 0x0ce1, /* ೠ - ೡ */
+ 0x0d05, 0x0d0c, /* അ - ഌ */
+ 0x0d0e, 0x0d10, /* എ - ഐ */
+ 0x0d12, 0x0d28, /* ഒ - ന */
+ 0x0d2a, 0x0d39, /* പ - ഹ */
+ 0x0d60, 0x0d61, /* ൠ - ൡ */
+ 0x0e01, 0x0e30, /* ก - ะ */
+ 0x0e32, 0x0e33, /* า - ำ */
+ 0x0e40, 0x0e46, /* เ - ๆ */
+ 0x0e5a, 0x0e5b, /* ๚ - ๛ */
+ 0x0e81, 0x0e82, /* ກ - ຂ */
+ 0x0e87, 0x0e88, /* ງ - ຈ */
+ 0x0e94, 0x0e97, /* ດ - ທ */
+ 0x0e99, 0x0e9f, /* ນ - ຟ */
+ 0x0ea1, 0x0ea3, /* ມ - ຣ */
+ 0x0eaa, 0x0eab, /* ສ - ຫ */
+ 0x0ead, 0x0eae, /* ອ - ຮ */
+ 0x0eb2, 0x0eb3, /* າ - ຳ */
+ 0x0ec0, 0x0ec4, /* ເ - ໄ */
+ 0x0edc, 0x0edd, /* ໜ - ໝ */
+ 0x0f18, 0x0f19, /* ༘ - ༙ */
+ 0x0f40, 0x0f47, /* ཀ - ཇ */
+ 0x0f49, 0x0f69, /* ཉ - ཀྵ */
+ 0x10d0, 0x10f6, /* ა - ჶ */
+ 0x1100, 0x1159, /* ᄀ - ᅙ */
+ 0x115f, 0x11a2, /* ᅟ - ᆢ */
+ 0x11a8, 0x11f9, /* ᆨ - ᇹ */
+ 0x1e00, 0x1e9b, /* Ḁ - ẛ */
+ 0x1f50, 0x1f57, /* ὐ - ὗ */
+ 0x1f80, 0x1fb4, /* ᾀ - ᾴ */
+ 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */
+ 0x1fc2, 0x1fc4, /* ῂ - ῄ */
+ 0x1fc6, 0x1fcc, /* ῆ - ῌ */
+ 0x1fd0, 0x1fd3, /* ῐ - ΐ */
+ 0x1fd6, 0x1fdb, /* ῖ - Ί */
+ 0x1fe0, 0x1fec, /* ῠ - Ῥ */
+ 0x1ff2, 0x1ff4, /* ῲ - ῴ */
+ 0x1ff6, 0x1ffc, /* ῶ - ῼ */
+ 0x210a, 0x2113, /* ℊ - ℓ */
+ 0x2115, 0x211d, /* ℕ - ℝ */
+ 0x2120, 0x2122, /* ℠ - ™ */
+ 0x212a, 0x2131, /* K - ℱ */
+ 0x2133, 0x2138, /* ℳ - ℸ */
+ 0x3041, 0x3094, /* ぁ - ゔ */
+ 0x30a1, 0x30fa, /* ァ - ヺ */
+ 0x3105, 0x312c, /* ㄅ - ㄬ */
+ 0x3131, 0x318e, /* ㄱ - ㆎ */
+ 0x3192, 0x319f, /* ㆒ - ㆟ */
+ 0x3260, 0x327b, /* ㉠ - ㉻ */
+ 0x328a, 0x32b0, /* ㊊ - ㊰ */
+ 0x32d0, 0x32fe, /* ㋐ - ㋾ */
+ 0x3300, 0x3357, /* ㌀ - ㍗ */
+ 0x3371, 0x3376, /* ㍱ - ㍶ */
+ 0x337b, 0x3394, /* ㍻ - ㎔ */
+ 0x3399, 0x339e, /* ㎙ - ㎞ */
+ 0x33a9, 0x33ad, /* ㎩ - ㎭ */
+ 0x33b0, 0x33c1, /* ㎰ - ㏁ */
+ 0x33c3, 0x33c5, /* ㏃ - ㏅ */
+ 0x33c7, 0x33d7, /* ㏇ - ㏗ */
+ 0x33d9, 0x33dd, /* ㏙ - ㏝ */
+ 0x4e00, 0x9fff, /* 一 - 鿿 */
+ 0xac00, 0xd7a3, /* 가 - 힣 */
+ 0xf900, 0xfb06, /* 豈 - st */
+ 0xfb13, 0xfb17, /* ﬓ - ﬗ */
+ 0xfb1f, 0xfb28, /* ײַ - ﬨ */
+ 0xfb2a, 0xfb36, /* שׁ - זּ */
+ 0xfb38, 0xfb3c, /* טּ - לּ */
+ 0xfb40, 0xfb41, /* נּ - סּ */
+ 0xfb43, 0xfb44, /* ףּ - פּ */
+ 0xfb46, 0xfbb1, /* צּ - ﮱ */
+ 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */
+ 0xfd50, 0xfd8f, /* ﵐ - ﶏ */
+ 0xfd92, 0xfdc7, /* ﶒ - ﷇ */
+ 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */
+ 0xfe70, 0xfe72, /* ﹰ - ﹲ */
+ 0xfe76, 0xfefc, /* ﹶ - ﻼ */
+ 0xff66, 0xff6f, /* ヲ - ッ */
+ 0xff71, 0xff9d, /* ア - ン */
+ 0xffa0, 0xffbe, /* ᅠ - ᄒ */
+ 0xffc2, 0xffc7, /* ᅡ - ᅦ */
+ 0xffca, 0xffcf, /* ᅧ - ᅬ */
+ 0xffd2, 0xffd7, /* ᅭ - ᅲ */
+ 0xffda, 0xffdc, /* ᅳ - ᅵ */
+};
+
+/*
+ * alpha singlets -
+ * only covers ranges not in lower||upper
+ */
+static
+Rune _alpha1[] =
+{
+ 0x00aa, /* ª */
+ 0x00b5, /* µ */
+ 0x00ba, /* º */
+ 0x03da, /* Ϛ */
+ 0x03dc, /* Ϝ */
+ 0x03de, /* Ϟ */
+ 0x03e0, /* Ϡ */
+ 0x06d5, /* ە */
+ 0x09b2, /* ল */
+ 0x0a5e, /* ਫ਼ */
+ 0x0a8d, /* ઍ */
+ 0x0ae0, /* ૠ */
+ 0x0b9c, /* ஜ */
+ 0x0cde, /* ೞ */
+ 0x0e4f, /* ๏ */
+ 0x0e84, /* ຄ */
+ 0x0e8a, /* ຊ */
+ 0x0e8d, /* ຍ */
+ 0x0ea5, /* ລ */
+ 0x0ea7, /* ວ */
+ 0x0eb0, /* ະ */
+ 0x0ebd, /* ຽ */
+ 0x1fbe, /* ι */
+ 0x207f, /* ⁿ */
+ 0x20a8, /* ₨ */
+ 0x2102, /* ℂ */
+ 0x2107, /* ℇ */
+ 0x2124, /* ℤ */
+ 0x2126, /* Ω */
+ 0x2128, /* ℨ */
+ 0xfb3e, /* מּ */
+ 0xfe74, /* ﹴ */
+};
+
+/*
+ * space ranges
+ */
+static
+Rune _space2[] =
+{
+ 0x0009, 0x000a, /* tab and newline */
+ 0x0020, 0x0020, /* space */
+ 0x0085, 0x0085,
+ 0x00a0, 0x00a0, /*   */
+ 0x1680, 0x1680,
+ 0x180e, 0x180e,
+ 0x2000, 0x200b, /*   - ​ */
+ 0x2028, 0x2029, /* 
 - 
 */
+ 0x202f, 0x202f,
+ 0x205f, 0x205f,
+ 0x3000, 0x3000, /*   */
+ 0xfeff, 0xfeff, /*  */
+};
+
+/*
+ * lower case ranges
+ * 3rd col is conversion excess 500
+ */
+static
+Rune _toupper2[] =
+{
+ 0x0061, 0x007a, 468, /* a-z A-Z */
+ 0x00e0, 0x00f6, 468, /* à-ö À-Ö */
+ 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */
+ 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */
+ 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */
+ 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */
+ 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */
+ 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */
+ 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */
+ 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */
+ 0x0430, 0x044f, 468, /* а-я А-Я */
+ 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */
+ 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */
+ 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */
+ 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */
+ 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */
+ 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */
+ 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */
+ 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */
+ 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */
+ 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */
+ 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */
+ 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */
+ 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */
+ 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */
+ 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */
+ 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */
+ 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */
+ 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */
+ 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */
+ 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */
+ 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */
+ 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */
+ 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */
+ 0xff41, 0xff5a, 468, /* a-z A-Z */
+};
+
+/*
+ * lower case singlets
+ * 2nd col is conversion excess 500
+ */
+static
+Rune _toupper1[] =
+{
+ 0x00ff, 621, /* ÿ Ÿ */
+ 0x0101, 499, /* ā Ā */
+ 0x0103, 499, /* ă Ă */
+ 0x0105, 499, /* ą Ą */
+ 0x0107, 499, /* ć Ć */
+ 0x0109, 499, /* ĉ Ĉ */
+ 0x010b, 499, /* ċ Ċ */
+ 0x010d, 499, /* č Č */
+ 0x010f, 499, /* ď Ď */
+ 0x0111, 499, /* đ Đ */
+ 0x0113, 499, /* ē Ē */
+ 0x0115, 499, /* ĕ Ĕ */
+ 0x0117, 499, /* ė Ė */
+ 0x0119, 499, /* ę Ę */
+ 0x011b, 499, /* ě Ě */
+ 0x011d, 499, /* ĝ Ĝ */
+ 0x011f, 499, /* ğ Ğ */
+ 0x0121, 499, /* ġ Ġ */
+ 0x0123, 499, /* ģ Ģ */
+ 0x0125, 499, /* ĥ Ĥ */
+ 0x0127, 499, /* ħ Ħ */
+ 0x0129, 499, /* ĩ Ĩ */
+ 0x012b, 499, /* ī Ī */
+ 0x012d, 499, /* ĭ Ĭ */
+ 0x012f, 499, /* į Į */
+ 0x0131, 268, /* ı I */
+ 0x0133, 499, /* ij IJ */
+ 0x0135, 499, /* ĵ Ĵ */
+ 0x0137, 499, /* ķ Ķ */
+ 0x013a, 499, /* ĺ Ĺ */
+ 0x013c, 499, /* ļ Ļ */
+ 0x013e, 499, /* ľ Ľ */
+ 0x0140, 499, /* ŀ Ŀ */
+ 0x0142, 499, /* ł Ł */
+ 0x0144, 499, /* ń Ń */
+ 0x0146, 499, /* ņ Ņ */
+ 0x0148, 499, /* ň Ň */
+ 0x014b, 499, /* ŋ Ŋ */
+ 0x014d, 499, /* ō Ō */
+ 0x014f, 499, /* ŏ Ŏ */
+ 0x0151, 499, /* ő Ő */
+ 0x0153, 499, /* œ Œ */
+ 0x0155, 499, /* ŕ Ŕ */
+ 0x0157, 499, /* ŗ Ŗ */
+ 0x0159, 499, /* ř Ř */
+ 0x015b, 499, /* ś Ś */
+ 0x015d, 499, /* ŝ Ŝ */
+ 0x015f, 499, /* ş Ş */
+ 0x0161, 499, /* š Š */
+ 0x0163, 499, /* ţ Ţ */
+ 0x0165, 499, /* ť Ť */
+ 0x0167, 499, /* ŧ Ŧ */
+ 0x0169, 499, /* ũ Ũ */
+ 0x016b, 499, /* ū Ū */
+ 0x016d, 499, /* ŭ Ŭ */
+ 0x016f, 499, /* ů Ů */
+ 0x0171, 499, /* ű Ű */
+ 0x0173, 499, /* ų Ų */
+ 0x0175, 499, /* ŵ Ŵ */
+ 0x0177, 499, /* ŷ Ŷ */
+ 0x017a, 499, /* ź Ź */
+ 0x017c, 499, /* ż Ż */
+ 0x017e, 499, /* ž Ž */
+ 0x017f, 200, /* ſ S */
+ 0x0183, 499, /* ƃ Ƃ */
+ 0x0185, 499, /* ƅ Ƅ */
+ 0x0188, 499, /* ƈ Ƈ */
+ 0x018c, 499, /* ƌ Ƌ */
+ 0x0192, 499, /* ƒ Ƒ */
+ 0x0199, 499, /* ƙ Ƙ */
+ 0x01a1, 499, /* ơ Ơ */
+ 0x01a3, 499, /* ƣ Ƣ */
+ 0x01a5, 499, /* ƥ Ƥ */
+ 0x01a8, 499, /* ƨ Ƨ */
+ 0x01ad, 499, /* ƭ Ƭ */
+ 0x01b0, 499, /* ư Ư */
+ 0x01b4, 499, /* ƴ Ƴ */
+ 0x01b6, 499, /* ƶ Ƶ */
+ 0x01b9, 499, /* ƹ Ƹ */
+ 0x01bd, 499, /* ƽ Ƽ */
+ 0x01c5, 499, /* Dž DŽ */
+ 0x01c6, 498, /* dž DŽ */
+ 0x01c8, 499, /* Lj LJ */
+ 0x01c9, 498, /* lj LJ */
+ 0x01cb, 499, /* Nj NJ */
+ 0x01cc, 498, /* nj NJ */
+ 0x01ce, 499, /* ǎ Ǎ */
+ 0x01d0, 499, /* ǐ Ǐ */
+ 0x01d2, 499, /* ǒ Ǒ */
+ 0x01d4, 499, /* ǔ Ǔ */
+ 0x01d6, 499, /* ǖ Ǖ */
+ 0x01d8, 499, /* ǘ Ǘ */
+ 0x01da, 499, /* ǚ Ǚ */
+ 0x01dc, 499, /* ǜ Ǜ */
+ 0x01df, 499, /* ǟ Ǟ */
+ 0x01e1, 499, /* ǡ Ǡ */
+ 0x01e3, 499, /* ǣ Ǣ */
+ 0x01e5, 499, /* ǥ Ǥ */
+ 0x01e7, 499, /* ǧ Ǧ */
+ 0x01e9, 499, /* ǩ Ǩ */
+ 0x01eb, 499, /* ǫ Ǫ */
+ 0x01ed, 499, /* ǭ Ǭ */
+ 0x01ef, 499, /* ǯ Ǯ */
+ 0x01f2, 499, /* Dz DZ */
+ 0x01f3, 498, /* dz DZ */
+ 0x01f5, 499, /* ǵ Ǵ */
+ 0x01fb, 499, /* ǻ Ǻ */
+ 0x01fd, 499, /* ǽ Ǽ */
+ 0x01ff, 499, /* ǿ Ǿ */
+ 0x0201, 499, /* ȁ Ȁ */
+ 0x0203, 499, /* ȃ Ȃ */
+ 0x0205, 499, /* ȅ Ȅ */
+ 0x0207, 499, /* ȇ Ȇ */
+ 0x0209, 499, /* ȉ Ȉ */
+ 0x020b, 499, /* ȋ Ȋ */
+ 0x020d, 499, /* ȍ Ȍ */
+ 0x020f, 499, /* ȏ Ȏ */
+ 0x0211, 499, /* ȑ Ȑ */
+ 0x0213, 499, /* ȓ Ȓ */
+ 0x0215, 499, /* ȕ Ȕ */
+ 0x0217, 499, /* ȗ Ȗ */
+ 0x0253, 290, /* ɓ Ɓ */
+ 0x0254, 294, /* ɔ Ɔ */
+ 0x025b, 297, /* ɛ Ɛ */
+ 0x0260, 295, /* ɠ Ɠ */
+ 0x0263, 293, /* ɣ Ɣ */
+ 0x0268, 291, /* ɨ Ɨ */
+ 0x0269, 289, /* ɩ Ɩ */
+ 0x026f, 289, /* ɯ Ɯ */
+ 0x0272, 287, /* ɲ Ɲ */
+ 0x0283, 282, /* ʃ Ʃ */
+ 0x0288, 282, /* ʈ Ʈ */
+ 0x0292, 281, /* ʒ Ʒ */
+ 0x03ac, 462, /* ά Ά */
+ 0x03cc, 436, /* ό Ό */
+ 0x03d0, 438, /* ϐ Β */
+ 0x03d1, 443, /* ϑ Θ */
+ 0x03d5, 453, /* ϕ Φ */
+ 0x03d6, 446, /* ϖ Π */
+ 0x03e3, 499, /* ϣ Ϣ */
+ 0x03e5, 499, /* ϥ Ϥ */
+ 0x03e7, 499, /* ϧ Ϧ */
+ 0x03e9, 499, /* ϩ Ϩ */
+ 0x03eb, 499, /* ϫ Ϫ */
+ 0x03ed, 499, /* ϭ Ϭ */
+ 0x03ef, 499, /* ϯ Ϯ */
+ 0x03f0, 414, /* ϰ Κ */
+ 0x03f1, 420, /* ϱ Ρ */
+ 0x0461, 499, /* ѡ Ѡ */
+ 0x0463, 499, /* ѣ Ѣ */
+ 0x0465, 499, /* ѥ Ѥ */
+ 0x0467, 499, /* ѧ Ѧ */
+ 0x0469, 499, /* ѩ Ѩ */
+ 0x046b, 499, /* ѫ Ѫ */
+ 0x046d, 499, /* ѭ Ѭ */
+ 0x046f, 499, /* ѯ Ѯ */
+ 0x0471, 499, /* ѱ Ѱ */
+ 0x0473, 499, /* ѳ Ѳ */
+ 0x0475, 499, /* ѵ Ѵ */
+ 0x0477, 499, /* ѷ Ѷ */
+ 0x0479, 499, /* ѹ Ѹ */
+ 0x047b, 499, /* ѻ Ѻ */
+ 0x047d, 499, /* ѽ Ѽ */
+ 0x047f, 499, /* ѿ Ѿ */
+ 0x0481, 499, /* ҁ Ҁ */
+ 0x0491, 499, /* ґ Ґ */
+ 0x0493, 499, /* ғ Ғ */
+ 0x0495, 499, /* ҕ Ҕ */
+ 0x0497, 499, /* җ Җ */
+ 0x0499, 499, /* ҙ Ҙ */
+ 0x049b, 499, /* қ Қ */
+ 0x049d, 499, /* ҝ Ҝ */
+ 0x049f, 499, /* ҟ Ҟ */
+ 0x04a1, 499, /* ҡ Ҡ */
+ 0x04a3, 499, /* ң Ң */
+ 0x04a5, 499, /* ҥ Ҥ */
+ 0x04a7, 499, /* ҧ Ҧ */
+ 0x04a9, 499, /* ҩ Ҩ */
+ 0x04ab, 499, /* ҫ Ҫ */
+ 0x04ad, 499, /* ҭ Ҭ */
+ 0x04af, 499, /* ү Ү */
+ 0x04b1, 499, /* ұ Ұ */
+ 0x04b3, 499, /* ҳ Ҳ */
+ 0x04b5, 499, /* ҵ Ҵ */
+ 0x04b7, 499, /* ҷ Ҷ */
+ 0x04b9, 499, /* ҹ Ҹ */
+ 0x04bb, 499, /* һ Һ */
+ 0x04bd, 499, /* ҽ Ҽ */
+ 0x04bf, 499, /* ҿ Ҿ */
+ 0x04c2, 499, /* ӂ Ӂ */
+ 0x04c4, 499, /* ӄ Ӄ */
+ 0x04c8, 499, /* ӈ Ӈ */
+ 0x04cc, 499, /* ӌ Ӌ */
+ 0x04d1, 499, /* ӑ Ӑ */
+ 0x04d3, 499, /* ӓ Ӓ */
+ 0x04d5, 499, /* ӕ Ӕ */
+ 0x04d7, 499, /* ӗ Ӗ */
+ 0x04d9, 499, /* ә Ә */
+ 0x04db, 499, /* ӛ Ӛ */
+ 0x04dd, 499, /* ӝ Ӝ */
+ 0x04df, 499, /* ӟ Ӟ */
+ 0x04e1, 499, /* ӡ Ӡ */
+ 0x04e3, 499, /* ӣ Ӣ */
+ 0x04e5, 499, /* ӥ Ӥ */
+ 0x04e7, 499, /* ӧ Ӧ */
+ 0x04e9, 499, /* ө Ө */
+ 0x04eb, 499, /* ӫ Ӫ */
+ 0x04ef, 499, /* ӯ Ӯ */
+ 0x04f1, 499, /* ӱ Ӱ */
+ 0x04f3, 499, /* ӳ Ӳ */
+ 0x04f5, 499, /* ӵ Ӵ */
+ 0x04f9, 499, /* ӹ Ӹ */
+ 0x1e01, 499, /* ḁ Ḁ */
+ 0x1e03, 499, /* ḃ Ḃ */
+ 0x1e05, 499, /* ḅ Ḅ */
+ 0x1e07, 499, /* ḇ Ḇ */
+ 0x1e09, 499, /* ḉ Ḉ */
+ 0x1e0b, 499, /* ḋ Ḋ */
+ 0x1e0d, 499, /* ḍ Ḍ */
+ 0x1e0f, 499, /* ḏ Ḏ */
+ 0x1e11, 499, /* ḑ Ḑ */
+ 0x1e13, 499, /* ḓ Ḓ */
+ 0x1e15, 499, /* ḕ Ḕ */
+ 0x1e17, 499, /* ḗ Ḗ */
+ 0x1e19, 499, /* ḙ Ḙ */
+ 0x1e1b, 499, /* ḛ Ḛ */
+ 0x1e1d, 499, /* ḝ Ḝ */
+ 0x1e1f, 499, /* ḟ Ḟ */
+ 0x1e21, 499, /* ḡ Ḡ */
+ 0x1e23, 499, /* ḣ Ḣ */
+ 0x1e25, 499, /* ḥ Ḥ */
+ 0x1e27, 499, /* ḧ Ḧ */
+ 0x1e29, 499, /* ḩ Ḩ */
+ 0x1e2b, 499, /* ḫ Ḫ */
+ 0x1e2d, 499, /* ḭ Ḭ */
+ 0x1e2f, 499, /* ḯ Ḯ */
+ 0x1e31, 499, /* ḱ Ḱ */
+ 0x1e33, 499, /* ḳ Ḳ */
+ 0x1e35, 499, /* ḵ Ḵ */
+ 0x1e37, 499, /* ḷ Ḷ */
+ 0x1e39, 499, /* ḹ Ḹ */
+ 0x1e3b, 499, /* ḻ Ḻ */
+ 0x1e3d, 499, /* ḽ Ḽ */
+ 0x1e3f, 499, /* ḿ Ḿ */
+ 0x1e41, 499, /* ṁ Ṁ */
+ 0x1e43, 499, /* ṃ Ṃ */
+ 0x1e45, 499, /* ṅ Ṅ */
+ 0x1e47, 499, /* ṇ Ṇ */
+ 0x1e49, 499, /* ṉ Ṉ */
+ 0x1e4b, 499, /* ṋ Ṋ */
+ 0x1e4d, 499, /* ṍ Ṍ */
+ 0x1e4f, 499, /* ṏ Ṏ */
+ 0x1e51, 499, /* ṑ Ṑ */
+ 0x1e53, 499, /* ṓ Ṓ */
+ 0x1e55, 499, /* ṕ Ṕ */
+ 0x1e57, 499, /* ṗ Ṗ */
+ 0x1e59, 499, /* ṙ Ṙ */
+ 0x1e5b, 499, /* ṛ Ṛ */
+ 0x1e5d, 499, /* ṝ Ṝ */
+ 0x1e5f, 499, /* ṟ Ṟ */
+ 0x1e61, 499, /* ṡ Ṡ */
+ 0x1e63, 499, /* ṣ Ṣ */
+ 0x1e65, 499, /* ṥ Ṥ */
+ 0x1e67, 499, /* ṧ Ṧ */
+ 0x1e69, 499, /* ṩ Ṩ */
+ 0x1e6b, 499, /* ṫ Ṫ */
+ 0x1e6d, 499, /* ṭ Ṭ */
+ 0x1e6f, 499, /* ṯ Ṯ */
+ 0x1e71, 499, /* ṱ Ṱ */
+ 0x1e73, 499, /* ṳ Ṳ */
+ 0x1e75, 499, /* ṵ Ṵ */
+ 0x1e77, 499, /* ṷ Ṷ */
+ 0x1e79, 499, /* ṹ Ṹ */
+ 0x1e7b, 499, /* ṻ Ṻ */
+ 0x1e7d, 499, /* ṽ Ṽ */
+ 0x1e7f, 499, /* ṿ Ṿ */
+ 0x1e81, 499, /* ẁ Ẁ */
+ 0x1e83, 499, /* ẃ Ẃ */
+ 0x1e85, 499, /* ẅ Ẅ */
+ 0x1e87, 499, /* ẇ Ẇ */
+ 0x1e89, 499, /* ẉ Ẉ */
+ 0x1e8b, 499, /* ẋ Ẋ */
+ 0x1e8d, 499, /* ẍ Ẍ */
+ 0x1e8f, 499, /* ẏ Ẏ */
+ 0x1e91, 499, /* ẑ Ẑ */
+ 0x1e93, 499, /* ẓ Ẓ */
+ 0x1e95, 499, /* ẕ Ẕ */
+ 0x1ea1, 499, /* ạ Ạ */
+ 0x1ea3, 499, /* ả Ả */
+ 0x1ea5, 499, /* ấ Ấ */
+ 0x1ea7, 499, /* ầ Ầ */
+ 0x1ea9, 499, /* ẩ Ẩ */
+ 0x1eab, 499, /* ẫ Ẫ */
+ 0x1ead, 499, /* ậ Ậ */
+ 0x1eaf, 499, /* ắ Ắ */
+ 0x1eb1, 499, /* ằ Ằ */
+ 0x1eb3, 499, /* ẳ Ẳ */
+ 0x1eb5, 499, /* ẵ Ẵ */
+ 0x1eb7, 499, /* ặ Ặ */
+ 0x1eb9, 499, /* ẹ Ẹ */
+ 0x1ebb, 499, /* ẻ Ẻ */
+ 0x1ebd, 499, /* ẽ Ẽ */
+ 0x1ebf, 499, /* ế Ế */
+ 0x1ec1, 499, /* ề Ề */
+ 0x1ec3, 499, /* ể Ể */
+ 0x1ec5, 499, /* ễ Ễ */
+ 0x1ec7, 499, /* ệ Ệ */
+ 0x1ec9, 499, /* ỉ Ỉ */
+ 0x1ecb, 499, /* ị Ị */
+ 0x1ecd, 499, /* ọ Ọ */
+ 0x1ecf, 499, /* ỏ Ỏ */
+ 0x1ed1, 499, /* ố Ố */
+ 0x1ed3, 499, /* ồ Ồ */
+ 0x1ed5, 499, /* ổ Ổ */
+ 0x1ed7, 499, /* ỗ Ỗ */
+ 0x1ed9, 499, /* ộ Ộ */
+ 0x1edb, 499, /* ớ Ớ */
+ 0x1edd, 499, /* ờ Ờ */
+ 0x1edf, 499, /* ở Ở */
+ 0x1ee1, 499, /* ỡ Ỡ */
+ 0x1ee3, 499, /* ợ Ợ */
+ 0x1ee5, 499, /* ụ Ụ */
+ 0x1ee7, 499, /* ủ Ủ */
+ 0x1ee9, 499, /* ứ Ứ */
+ 0x1eeb, 499, /* ừ Ừ */
+ 0x1eed, 499, /* ử Ử */
+ 0x1eef, 499, /* ữ Ữ */
+ 0x1ef1, 499, /* ự Ự */
+ 0x1ef3, 499, /* ỳ Ỳ */
+ 0x1ef5, 499, /* ỵ Ỵ */
+ 0x1ef7, 499, /* ỷ Ỷ */
+ 0x1ef9, 499, /* ỹ Ỹ */
+ 0x1f51, 508, /* ὑ Ὑ */
+ 0x1f53, 508, /* ὓ Ὓ */
+ 0x1f55, 508, /* ὕ Ὕ */
+ 0x1f57, 508, /* ὗ Ὗ */
+ 0x1fb3, 509, /* ᾳ ᾼ */
+ 0x1fc3, 509, /* ῃ ῌ */
+ 0x1fe5, 507, /* ῥ Ῥ */
+ 0x1ff3, 509, /* ῳ ῼ */
+};
+
+static Rune __isdigitr[] = {
+ 0x0030, 0x0039,
+ 0x0660, 0x0669,
+ 0x06f0, 0x06f9,
+ 0x07c0, 0x07c9,
+ 0x0966, 0x096f,
+ 0x09e6, 0x09ef,
+ 0x0a66, 0x0a6f,
+ 0x0ae6, 0x0aef,
+ 0x0b66, 0x0b6f,
+ 0x0be6, 0x0bef,
+ 0x0c66, 0x0c6f,
+ 0x0ce6, 0x0cef,
+ 0x0d66, 0x0d6f,
+ 0x0e50, 0x0e59,
+ 0x0ed0, 0x0ed9,
+ 0x0f20, 0x0f29,
+ 0x1040, 0x1049,
+ 0x17e0, 0x17e9,
+ 0x1810, 0x1819,
+ 0x1946, 0x194f,
+ 0x19d0, 0x19d9,
+ 0x1b50, 0x1b59,
+ 0xff10, 0xff19,
+ 0x104a0, 0x104a9,
+ 0x1d7ce, 0x1d7ff,
+};
+
+/*
+ * upper case ranges
+ * 3rd col is conversion excess 500
+ */
+static
+Rune _tolower2[] =
+{
+ 0x0041, 0x005a, 532, /* A-Z a-z */
+ 0x00c0, 0x00d6, 532, /* À-Ö à-ö */
+ 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */
+ 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */
+ 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */
+ 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */
+ 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */
+ 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */
+ 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */
+ 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */
+ 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */
+ 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */
+ 0x0410, 0x042f, 532, /* А-Я а-я */
+ 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */
+ 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */
+ 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */
+ 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */
+ 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */
+ 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */
+ 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */
+ 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */
+ 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */
+ 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */
+ 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */
+ 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */
+ 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */
+ 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */
+ 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */
+ 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */
+ 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */
+ 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */
+ 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */
+ 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */
+ 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */
+ 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */
+ 0xff21, 0xff3a, 532, /* A-Z a-z */
+};
+
+/*
+ * upper case singlets
+ * 2nd col is conversion excess 500
+ */
+static
+Rune _tolower1[] =
+{
+ 0x0100, 501, /* Ā ā */
+ 0x0102, 501, /* Ă ă */
+ 0x0104, 501, /* Ą ą */
+ 0x0106, 501, /* Ć ć */
+ 0x0108, 501, /* Ĉ ĉ */
+ 0x010a, 501, /* Ċ ċ */
+ 0x010c, 501, /* Č č */
+ 0x010e, 501, /* Ď ď */
+ 0x0110, 501, /* Đ đ */
+ 0x0112, 501, /* Ē ē */
+ 0x0114, 501, /* Ĕ ĕ */
+ 0x0116, 501, /* Ė ė */
+ 0x0118, 501, /* Ę ę */
+ 0x011a, 501, /* Ě ě */
+ 0x011c, 501, /* Ĝ ĝ */
+ 0x011e, 501, /* Ğ ğ */
+ 0x0120, 501, /* Ġ ġ */
+ 0x0122, 501, /* Ģ ģ */
+ 0x0124, 501, /* Ĥ ĥ */
+ 0x0126, 501, /* Ħ ħ */
+ 0x0128, 501, /* Ĩ ĩ */
+ 0x012a, 501, /* Ī ī */
+ 0x012c, 501, /* Ĭ ĭ */
+ 0x012e, 501, /* Į į */
+ 0x0130, 301, /* İ i */
+ 0x0132, 501, /* IJ ij */
+ 0x0134, 501, /* Ĵ ĵ */
+ 0x0136, 501, /* Ķ ķ */
+ 0x0139, 501, /* Ĺ ĺ */
+ 0x013b, 501, /* Ļ ļ */
+ 0x013d, 501, /* Ľ ľ */
+ 0x013f, 501, /* Ŀ ŀ */
+ 0x0141, 501, /* Ł ł */
+ 0x0143, 501, /* Ń ń */
+ 0x0145, 501, /* Ņ ņ */
+ 0x0147, 501, /* Ň ň */
+ 0x014a, 501, /* Ŋ ŋ */
+ 0x014c, 501, /* Ō ō */
+ 0x014e, 501, /* Ŏ ŏ */
+ 0x0150, 501, /* Ő ő */
+ 0x0152, 501, /* Œ œ */
+ 0x0154, 501, /* Ŕ ŕ */
+ 0x0156, 501, /* Ŗ ŗ */
+ 0x0158, 501, /* Ř ř */
+ 0x015a, 501, /* Ś ś */
+ 0x015c, 501, /* Ŝ ŝ */
+ 0x015e, 501, /* Ş ş */
+ 0x0160, 501, /* Š š */
+ 0x0162, 501, /* Ţ ţ */
+ 0x0164, 501, /* Ť ť */
+ 0x0166, 501, /* Ŧ ŧ */
+ 0x0168, 501, /* Ũ ũ */
+ 0x016a, 501, /* Ū ū */
+ 0x016c, 501, /* Ŭ ŭ */
+ 0x016e, 501, /* Ů ů */
+ 0x0170, 501, /* Ű ű */
+ 0x0172, 501, /* Ų ų */
+ 0x0174, 501, /* Ŵ ŵ */
+ 0x0176, 501, /* Ŷ ŷ */
+ 0x0178, 379, /* Ÿ ÿ */
+ 0x0179, 501, /* Ź ź */
+ 0x017b, 501, /* Ż ż */
+ 0x017d, 501, /* Ž ž */
+ 0x0181, 710, /* Ɓ ɓ */
+ 0x0182, 501, /* Ƃ ƃ */
+ 0x0184, 501, /* Ƅ ƅ */
+ 0x0186, 706, /* Ɔ ɔ */
+ 0x0187, 501, /* Ƈ ƈ */
+ 0x018b, 501, /* Ƌ ƌ */
+ 0x0190, 703, /* Ɛ ɛ */
+ 0x0191, 501, /* Ƒ ƒ */
+ 0x0193, 705, /* Ɠ ɠ */
+ 0x0194, 707, /* Ɣ ɣ */
+ 0x0196, 711, /* Ɩ ɩ */
+ 0x0197, 709, /* Ɨ ɨ */
+ 0x0198, 501, /* Ƙ ƙ */
+ 0x019c, 711, /* Ɯ ɯ */
+ 0x019d, 713, /* Ɲ ɲ */
+ 0x01a0, 501, /* Ơ ơ */
+ 0x01a2, 501, /* Ƣ ƣ */
+ 0x01a4, 501, /* Ƥ ƥ */
+ 0x01a7, 501, /* Ƨ ƨ */
+ 0x01a9, 718, /* Ʃ ʃ */
+ 0x01ac, 501, /* Ƭ ƭ */
+ 0x01ae, 718, /* Ʈ ʈ */
+ 0x01af, 501, /* Ư ư */
+ 0x01b3, 501, /* Ƴ ƴ */
+ 0x01b5, 501, /* Ƶ ƶ */
+ 0x01b7, 719, /* Ʒ ʒ */
+ 0x01b8, 501, /* Ƹ ƹ */
+ 0x01bc, 501, /* Ƽ ƽ */
+ 0x01c4, 502, /* DŽ dž */
+ 0x01c5, 501, /* Dž dž */
+ 0x01c7, 502, /* LJ lj */
+ 0x01c8, 501, /* Lj lj */
+ 0x01ca, 502, /* NJ nj */
+ 0x01cb, 501, /* Nj nj */
+ 0x01cd, 501, /* Ǎ ǎ */
+ 0x01cf, 501, /* Ǐ ǐ */
+ 0x01d1, 501, /* Ǒ ǒ */
+ 0x01d3, 501, /* Ǔ ǔ */
+ 0x01d5, 501, /* Ǖ ǖ */
+ 0x01d7, 501, /* Ǘ ǘ */
+ 0x01d9, 501, /* Ǚ ǚ */
+ 0x01db, 501, /* Ǜ ǜ */
+ 0x01de, 501, /* Ǟ ǟ */
+ 0x01e0, 501, /* Ǡ ǡ */
+ 0x01e2, 501, /* Ǣ ǣ */
+ 0x01e4, 501, /* Ǥ ǥ */
+ 0x01e6, 501, /* Ǧ ǧ */
+ 0x01e8, 501, /* Ǩ ǩ */
+ 0x01ea, 501, /* Ǫ ǫ */
+ 0x01ec, 501, /* Ǭ ǭ */
+ 0x01ee, 501, /* Ǯ ǯ */
+ 0x01f1, 502, /* DZ dz */
+ 0x01f2, 501, /* Dz dz */
+ 0x01f4, 501, /* Ǵ ǵ */
+ 0x01fa, 501, /* Ǻ ǻ */
+ 0x01fc, 501, /* Ǽ ǽ */
+ 0x01fe, 501, /* Ǿ ǿ */
+ 0x0200, 501, /* Ȁ ȁ */
+ 0x0202, 501, /* Ȃ ȃ */
+ 0x0204, 501, /* Ȅ ȅ */
+ 0x0206, 501, /* Ȇ ȇ */
+ 0x0208, 501, /* Ȉ ȉ */
+ 0x020a, 501, /* Ȋ ȋ */
+ 0x020c, 501, /* Ȍ ȍ */
+ 0x020e, 501, /* Ȏ ȏ */
+ 0x0210, 501, /* Ȑ ȑ */
+ 0x0212, 501, /* Ȓ ȓ */
+ 0x0214, 501, /* Ȕ ȕ */
+ 0x0216, 501, /* Ȗ ȗ */
+ 0x0386, 538, /* Ά ά */
+ 0x038c, 564, /* Ό ό */
+ 0x03e2, 501, /* Ϣ ϣ */
+ 0x03e4, 501, /* Ϥ ϥ */
+ 0x03e6, 501, /* Ϧ ϧ */
+ 0x03e8, 501, /* Ϩ ϩ */
+ 0x03ea, 501, /* Ϫ ϫ */
+ 0x03ec, 501, /* Ϭ ϭ */
+ 0x03ee, 501, /* Ϯ ϯ */
+ 0x0460, 501, /* Ѡ ѡ */
+ 0x0462, 501, /* Ѣ ѣ */
+ 0x0464, 501, /* Ѥ ѥ */
+ 0x0466, 501, /* Ѧ ѧ */
+ 0x0468, 501, /* Ѩ ѩ */
+ 0x046a, 501, /* Ѫ ѫ */
+ 0x046c, 501, /* Ѭ ѭ */
+ 0x046e, 501, /* Ѯ ѯ */
+ 0x0470, 501, /* Ѱ ѱ */
+ 0x0472, 501, /* Ѳ ѳ */
+ 0x0474, 501, /* Ѵ ѵ */
+ 0x0476, 501, /* Ѷ ѷ */
+ 0x0478, 501, /* Ѹ ѹ */
+ 0x047a, 501, /* Ѻ ѻ */
+ 0x047c, 501, /* Ѽ ѽ */
+ 0x047e, 501, /* Ѿ ѿ */
+ 0x0480, 501, /* Ҁ ҁ */
+ 0x0490, 501, /* Ґ ґ */
+ 0x0492, 501, /* Ғ ғ */
+ 0x0494, 501, /* Ҕ ҕ */
+ 0x0496, 501, /* Җ җ */
+ 0x0498, 501, /* Ҙ ҙ */
+ 0x049a, 501, /* Қ қ */
+ 0x049c, 501, /* Ҝ ҝ */
+ 0x049e, 501, /* Ҟ ҟ */
+ 0x04a0, 501, /* Ҡ ҡ */
+ 0x04a2, 501, /* Ң ң */
+ 0x04a4, 501, /* Ҥ ҥ */
+ 0x04a6, 501, /* Ҧ ҧ */
+ 0x04a8, 501, /* Ҩ ҩ */
+ 0x04aa, 501, /* Ҫ ҫ */
+ 0x04ac, 501, /* Ҭ ҭ */
+ 0x04ae, 501, /* Ү ү */
+ 0x04b0, 501, /* Ұ ұ */
+ 0x04b2, 501, /* Ҳ ҳ */
+ 0x04b4, 501, /* Ҵ ҵ */
+ 0x04b6, 501, /* Ҷ ҷ */
+ 0x04b8, 501, /* Ҹ ҹ */
+ 0x04ba, 501, /* Һ һ */
+ 0x04bc, 501, /* Ҽ ҽ */
+ 0x04be, 501, /* Ҿ ҿ */
+ 0x04c1, 501, /* Ӂ ӂ */
+ 0x04c3, 501, /* Ӄ ӄ */
+ 0x04c7, 501, /* Ӈ ӈ */
+ 0x04cb, 501, /* Ӌ ӌ */
+ 0x04d0, 501, /* Ӑ ӑ */
+ 0x04d2, 501, /* Ӓ ӓ */
+ 0x04d4, 501, /* Ӕ ӕ */
+ 0x04d6, 501, /* Ӗ ӗ */
+ 0x04d8, 501, /* Ә ә */
+ 0x04da, 501, /* Ӛ ӛ */
+ 0x04dc, 501, /* Ӝ ӝ */
+ 0x04de, 501, /* Ӟ ӟ */
+ 0x04e0, 501, /* Ӡ ӡ */
+ 0x04e2, 501, /* Ӣ ӣ */
+ 0x04e4, 501, /* Ӥ ӥ */
+ 0x04e6, 501, /* Ӧ ӧ */
+ 0x04e8, 501, /* Ө ө */
+ 0x04ea, 501, /* Ӫ ӫ */
+ 0x04ee, 501, /* Ӯ ӯ */
+ 0x04f0, 501, /* Ӱ ӱ */
+ 0x04f2, 501, /* Ӳ ӳ */
+ 0x04f4, 501, /* Ӵ ӵ */
+ 0x04f8, 501, /* Ӹ ӹ */
+ 0x1e00, 501, /* Ḁ ḁ */
+ 0x1e02, 501, /* Ḃ ḃ */
+ 0x1e04, 501, /* Ḅ ḅ */
+ 0x1e06, 501, /* Ḇ ḇ */
+ 0x1e08, 501, /* Ḉ ḉ */
+ 0x1e0a, 501, /* Ḋ ḋ */
+ 0x1e0c, 501, /* Ḍ ḍ */
+ 0x1e0e, 501, /* Ḏ ḏ */
+ 0x1e10, 501, /* Ḑ ḑ */
+ 0x1e12, 501, /* Ḓ ḓ */
+ 0x1e14, 501, /* Ḕ ḕ */
+ 0x1e16, 501, /* Ḗ ḗ */
+ 0x1e18, 501, /* Ḙ ḙ */
+ 0x1e1a, 501, /* Ḛ ḛ */
+ 0x1e1c, 501, /* Ḝ ḝ */
+ 0x1e1e, 501, /* Ḟ ḟ */
+ 0x1e20, 501, /* Ḡ ḡ */
+ 0x1e22, 501, /* Ḣ ḣ */
+ 0x1e24, 501, /* Ḥ ḥ */
+ 0x1e26, 501, /* Ḧ ḧ */
+ 0x1e28, 501, /* Ḩ ḩ */
+ 0x1e2a, 501, /* Ḫ ḫ */
+ 0x1e2c, 501, /* Ḭ ḭ */
+ 0x1e2e, 501, /* Ḯ ḯ */
+ 0x1e30, 501, /* Ḱ ḱ */
+ 0x1e32, 501, /* Ḳ ḳ */
+ 0x1e34, 501, /* Ḵ ḵ */
+ 0x1e36, 501, /* Ḷ ḷ */
+ 0x1e38, 501, /* Ḹ ḹ */
+ 0x1e3a, 501, /* Ḻ ḻ */
+ 0x1e3c, 501, /* Ḽ ḽ */
+ 0x1e3e, 501, /* Ḿ ḿ */
+ 0x1e40, 501, /* Ṁ ṁ */
+ 0x1e42, 501, /* Ṃ ṃ */
+ 0x1e44, 501, /* Ṅ ṅ */
+ 0x1e46, 501, /* Ṇ ṇ */
+ 0x1e48, 501, /* Ṉ ṉ */
+ 0x1e4a, 501, /* Ṋ ṋ */
+ 0x1e4c, 501, /* Ṍ ṍ */
+ 0x1e4e, 501, /* Ṏ ṏ */
+ 0x1e50, 501, /* Ṑ ṑ */
+ 0x1e52, 501, /* Ṓ ṓ */
+ 0x1e54, 501, /* Ṕ ṕ */
+ 0x1e56, 501, /* Ṗ ṗ */
+ 0x1e58, 501, /* Ṙ ṙ */
+ 0x1e5a, 501, /* Ṛ ṛ */
+ 0x1e5c, 501, /* Ṝ ṝ */
+ 0x1e5e, 501, /* Ṟ ṟ */
+ 0x1e60, 501, /* Ṡ ṡ */
+ 0x1e62, 501, /* Ṣ ṣ */
+ 0x1e64, 501, /* Ṥ ṥ */
+ 0x1e66, 501, /* Ṧ ṧ */
+ 0x1e68, 501, /* Ṩ ṩ */
+ 0x1e6a, 501, /* Ṫ ṫ */
+ 0x1e6c, 501, /* Ṭ ṭ */
+ 0x1e6e, 501, /* Ṯ ṯ */
+ 0x1e70, 501, /* Ṱ ṱ */
+ 0x1e72, 501, /* Ṳ ṳ */
+ 0x1e74, 501, /* Ṵ ṵ */
+ 0x1e76, 501, /* Ṷ ṷ */
+ 0x1e78, 501, /* Ṹ ṹ */
+ 0x1e7a, 501, /* Ṻ ṻ */
+ 0x1e7c, 501, /* Ṽ ṽ */
+ 0x1e7e, 501, /* Ṿ ṿ */
+ 0x1e80, 501, /* Ẁ ẁ */
+ 0x1e82, 501, /* Ẃ ẃ */
+ 0x1e84, 501, /* Ẅ ẅ */
+ 0x1e86, 501, /* Ẇ ẇ */
+ 0x1e88, 501, /* Ẉ ẉ */
+ 0x1e8a, 501, /* Ẋ ẋ */
+ 0x1e8c, 501, /* Ẍ ẍ */
+ 0x1e8e, 501, /* Ẏ ẏ */
+ 0x1e90, 501, /* Ẑ ẑ */
+ 0x1e92, 501, /* Ẓ ẓ */
+ 0x1e94, 501, /* Ẕ ẕ */
+ 0x1ea0, 501, /* Ạ ạ */
+ 0x1ea2, 501, /* Ả ả */
+ 0x1ea4, 501, /* Ấ ấ */
+ 0x1ea6, 501, /* Ầ ầ */
+ 0x1ea8, 501, /* Ẩ ẩ */
+ 0x1eaa, 501, /* Ẫ ẫ */
+ 0x1eac, 501, /* Ậ ậ */
+ 0x1eae, 501, /* Ắ ắ */
+ 0x1eb0, 501, /* Ằ ằ */
+ 0x1eb2, 501, /* Ẳ ẳ */
+ 0x1eb4, 501, /* Ẵ ẵ */
+ 0x1eb6, 501, /* Ặ ặ */
+ 0x1eb8, 501, /* Ẹ ẹ */
+ 0x1eba, 501, /* Ẻ ẻ */
+ 0x1ebc, 501, /* Ẽ ẽ */
+ 0x1ebe, 501, /* Ế ế */
+ 0x1ec0, 501, /* Ề ề */
+ 0x1ec2, 501, /* Ể ể */
+ 0x1ec4, 501, /* Ễ ễ */
+ 0x1ec6, 501, /* Ệ ệ */
+ 0x1ec8, 501, /* Ỉ ỉ */
+ 0x1eca, 501, /* Ị ị */
+ 0x1ecc, 501, /* Ọ ọ */
+ 0x1ece, 501, /* Ỏ ỏ */
+ 0x1ed0, 501, /* Ố ố */
+ 0x1ed2, 501, /* Ồ ồ */
+ 0x1ed4, 501, /* Ổ ổ */
+ 0x1ed6, 501, /* Ỗ ỗ */
+ 0x1ed8, 501, /* Ộ ộ */
+ 0x1eda, 501, /* Ớ ớ */
+ 0x1edc, 501, /* Ờ ờ */
+ 0x1ede, 501, /* Ở ở */
+ 0x1ee0, 501, /* Ỡ ỡ */
+ 0x1ee2, 501, /* Ợ ợ */
+ 0x1ee4, 501, /* Ụ ụ */
+ 0x1ee6, 501, /* Ủ ủ */
+ 0x1ee8, 501, /* Ứ ứ */
+ 0x1eea, 501, /* Ừ ừ */
+ 0x1eec, 501, /* Ử ử */
+ 0x1eee, 501, /* Ữ ữ */
+ 0x1ef0, 501, /* Ự ự */
+ 0x1ef2, 501, /* Ỳ ỳ */
+ 0x1ef4, 501, /* Ỵ ỵ */
+ 0x1ef6, 501, /* Ỷ ỷ */
+ 0x1ef8, 501, /* Ỹ ỹ */
+ 0x1f59, 492, /* Ὑ ὑ */
+ 0x1f5b, 492, /* Ὓ ὓ */
+ 0x1f5d, 492, /* Ὕ ὕ */
+ 0x1f5f, 492, /* Ὗ ὗ */
+ 0x1fbc, 491, /* ᾼ ᾳ */
+ 0x1fcc, 491, /* ῌ ῃ */
+ 0x1fec, 493, /* Ῥ ῥ */
+ 0x1ffc, 491, /* ῼ ῳ */
+};
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+static
+Rune _totitle1[] =
+{
+ 0x01c4, 501, /* DŽ Dž */
+ 0x01c6, 499, /* dž Dž */
+ 0x01c7, 501, /* LJ Lj */
+ 0x01c9, 499, /* lj Lj */
+ 0x01ca, 501, /* NJ Nj */
+ 0x01cc, 499, /* nj Nj */
+ 0x01f1, 501, /* DZ Dz */
+ 0x01f3, 499, /* dz Dz */
+};
+
+static
+Rune*
+bsearch(Rune c, Rune *t, int n, int ne)
+{
+ Rune *p;
+ int m;
+
+ while(n > 1) {
+ m = n/2;
+ p = t + m*ne;
+ if(c >= p[0]) {
+ t = p;
+ n = n-m;
+ } else
+ n = m;
+ }
+ if(n && c >= t[0])
+ return t;
+ return 0;
+}
+
+Rune
+tolowerrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, _tolower2, nelem(_tolower2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, _tolower1, nelem(_tolower1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune
+toupperrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, _toupper2, nelem(_toupper2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, _toupper1, nelem(_toupper1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune
+totitlerune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, _totitle1, nelem(_totitle1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+int
+islowerrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, _toupper2, nelem(_toupper2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, _toupper1, nelem(_toupper1)/2, 2);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+isupperrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, _tolower2, nelem(_tolower2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, _tolower1, nelem(_tolower1)/2, 2);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+isalpharune(Rune c)
+{
+ Rune *p;
+
+ if(isupperrune(c) || islowerrune(c))
+ return 1;
+ p = bsearch(c, _alpha2, nelem(_alpha2)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, _alpha1, nelem(_alpha1), 1);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+istitlerune(Rune c)
+{
+ return isupperrune(c) && islowerrune(c);
+}
+
+int
+isspacerune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, _space2, nelem(_space2)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ return 0;
+}
+
+int
+isdigitrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __isdigitr, nelem(__isdigitr)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ return 0;
+}
diff --git a/sys/src/libc/port/sin.c b/sys/src/libc/port/sin.c
new file mode 100755
index 000000000..7072dd406
--- /dev/null
+++ b/sys/src/libc/port/sin.c
@@ -0,0 +1,68 @@
+/*
+ C program for floating point sin/cos.
+ Calls modf.
+ There are no error exits.
+ Coefficients are #3370 from Hart & Cheney (18.80D).
+*/
+
+#include <u.h>
+#include <libc.h>
+
+#define p0 .1357884097877375669092680e8
+#define p1 -.4942908100902844161158627e7
+#define p2 .4401030535375266501944918e6
+#define p3 -.1384727249982452873054457e5
+#define p4 .1459688406665768722226959e3
+#define q0 .8644558652922534429915149e7
+#define q1 .4081792252343299749395779e6
+#define q2 .9463096101538208180571257e4
+#define q3 .1326534908786136358911494e3
+
+static
+double
+sinus(double arg, int quad)
+{
+ double e, f, ysq, x, y, temp1, temp2;
+ int k;
+
+ x = arg;
+ if(x < 0) {
+ x = -x;
+ quad += 2;
+ }
+ x *= 1/PIO2; /* underflow? */
+ if(x > 32764) {
+ y = modf(x, &e);
+ e += quad;
+ modf(0.25*e, &f);
+ quad = e - 4*f;
+ } else {
+ k = x;
+ y = x - k;
+ quad += k;
+ quad &= 3;
+ }
+ if(quad & 1)
+ y = 1-y;
+ if(quad > 1)
+ y = -y;
+
+ ysq = y*y;
+ temp1 = ((((p4*ysq+p3)*ysq+p2)*ysq+p1)*ysq+p0)*y;
+ temp2 = ((((ysq+q3)*ysq+q2)*ysq+q1)*ysq+q0);
+ return temp1/temp2;
+}
+
+double
+cos(double arg)
+{
+ if(arg < 0)
+ arg = -arg;
+ return sinus(arg, 1);
+}
+
+double
+sin(double arg)
+{
+ return sinus(arg, 0);
+}
diff --git a/sys/src/libc/port/sinh.c b/sys/src/libc/port/sinh.c
new file mode 100755
index 000000000..115d42c0e
--- /dev/null
+++ b/sys/src/libc/port/sinh.c
@@ -0,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * sinh(arg) returns the hyperbolic sine of its floating-
+ * point argument.
+ *
+ * The exponential function is called for arguments
+ * greater in magnitude than 0.5.
+ *
+ * A series is used for arguments smaller in magnitude than 0.5.
+ * The coefficients are #2029 from Hart & Cheney. (20.36D)
+ *
+ * cosh(arg) is computed from the exponential function for
+ * all arguments.
+ */
+
+static double p0 = -0.6307673640497716991184787251e+6;
+static double p1 = -0.8991272022039509355398013511e+5;
+static double p2 = -0.2894211355989563807284660366e+4;
+static double p3 = -0.2630563213397497062819489e+2;
+static double q0 = -0.6307673640497716991212077277e+6;
+static double q1 = 0.1521517378790019070696485176e+5;
+static double q2 = -0.173678953558233699533450911e+3;
+
+double
+sinh(double arg)
+{
+ double temp, argsq;
+ int sign;
+
+ sign = 0;
+ if(arg < 0) {
+ arg = -arg;
+ sign++;
+ }
+ if(arg > 21) {
+ temp = exp(arg)/2;
+ goto out;
+ }
+ if(arg > 0.5) {
+ temp = (exp(arg) - exp(-arg))/2;
+ goto out;
+ }
+ argsq = arg*arg;
+ temp = (((p3*argsq+p2)*argsq+p1)*argsq+p0)*arg;
+ temp /= (((argsq+q2)*argsq+q1)*argsq+q0);
+out:
+ if(sign)
+ temp = -temp;
+ return temp;
+}
+
+double
+cosh(double arg)
+{
+ if(arg < 0)
+ arg = - arg;
+ if(arg > 21)
+ return exp(arg)/2;
+ return (exp(arg) + exp(-arg))/2;
+}
diff --git a/sys/src/libc/port/sqrt.c b/sys/src/libc/port/sqrt.c
new file mode 100755
index 000000000..a92efac88
--- /dev/null
+++ b/sys/src/libc/port/sqrt.c
@@ -0,0 +1,54 @@
+/*
+ sqrt returns the square root of its floating
+ point argument. Newton's method.
+
+ calls frexp
+*/
+
+#include <u.h>
+#include <libc.h>
+
+double
+sqrt(double arg)
+{
+ double x, temp;
+ int exp, i;
+
+ if(arg <= 0) {
+ if(arg < 0)
+ return NaN();
+ return 0;
+ }
+ if(isInf(arg, 1))
+ return arg;
+ x = frexp(arg, &exp);
+ while(x < 0.5) {
+ x *= 2;
+ exp--;
+ }
+ /*
+ * NOTE
+ * this wont work on 1's comp
+ */
+ if(exp & 1) {
+ x *= 2;
+ exp--;
+ }
+ temp = 0.5 * (1.0+x);
+
+ while(exp > 60) {
+ temp *= (1L<<30);
+ exp -= 60;
+ }
+ while(exp < -60) {
+ temp /= (1L<<30);
+ exp += 60;
+ }
+ if(exp >= 0)
+ temp *= 1L << (exp/2);
+ else
+ temp /= 1L << (-exp/2);
+ for(i=0; i<=4; i++)
+ temp = 0.5*(temp + arg/temp);
+ return temp;
+}
diff --git a/sys/src/libc/port/strcat.c b/sys/src/libc/port/strcat.c
new file mode 100755
index 000000000..137dc8195
--- /dev/null
+++ b/sys/src/libc/port/strcat.c
@@ -0,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strcat(char *s1, char *s2)
+{
+
+ strcpy(strchr(s1, 0), s2);
+ return s1;
+}
diff --git a/sys/src/libc/port/strchr.c b/sys/src/libc/port/strchr.c
new file mode 100755
index 000000000..1e9aab597
--- /dev/null
+++ b/sys/src/libc/port/strchr.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strchr(char *s, int c)
+{
+ char c0 = c;
+ char c1;
+
+ if(c == 0) {
+ while(*s++)
+ ;
+ return s-1;
+ }
+
+ while(c1 = *s++)
+ if(c1 == c0)
+ return s-1;
+ return 0;
+}
diff --git a/sys/src/libc/port/strcmp.c b/sys/src/libc/port/strcmp.c
new file mode 100755
index 000000000..9d0c0b398
--- /dev/null
+++ b/sys/src/libc/port/strcmp.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+int
+strcmp(char *s1, char *s2)
+{
+ unsigned c1, c2;
+
+ for(;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ if(c1 == 0)
+ return 0;
+ }
+}
diff --git a/sys/src/libc/port/strcpy.c b/sys/src/libc/port/strcpy.c
new file mode 100755
index 000000000..233a9c1b0
--- /dev/null
+++ b/sys/src/libc/port/strcpy.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#define N 10000
+
+char*
+strcpy(char *s1, char *s2)
+{
+ char *os1;
+
+ os1 = s1;
+ while(!memccpy(s1, s2, 0, N)) {
+ s1 += N;
+ s2 += N;
+ }
+ return os1;
+}
diff --git a/sys/src/libc/port/strcspn.c b/sys/src/libc/port/strcspn.c
new file mode 100755
index 000000000..8349af335
--- /dev/null
+++ b/sys/src/libc/port/strcspn.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+#define N 256
+
+long
+strcspn(char *s, char *b)
+{
+ char map[N], *os;
+
+ memset(map, 0, N);
+ for(;;) {
+ map[*(uchar*)b] = 1;
+ if(*b++ == 0)
+ break;
+ }
+ os = s;
+ while(map[*(uchar*)s++] == 0)
+ ;
+ return s - os - 1;
+}
diff --git a/sys/src/libc/port/strdup.c b/sys/src/libc/port/strdup.c
new file mode 100755
index 000000000..3d5fe523f
--- /dev/null
+++ b/sys/src/libc/port/strdup.c
@@ -0,0 +1,14 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strdup(char *s)
+{
+ char *ns;
+
+ ns = malloc(strlen(s) + 1);
+ if(ns == 0)
+ return 0;
+
+ return strcpy(ns, s);
+}
diff --git a/sys/src/libc/port/strecpy.c b/sys/src/libc/port/strecpy.c
new file mode 100755
index 000000000..a44dffdd8
--- /dev/null
+++ b/sys/src/libc/port/strecpy.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strecpy(char *to, char *e, char *from)
+{
+ if(to >= e)
+ return to;
+ to = memccpy(to, from, '\0', e - to);
+ if(to == nil){
+ to = e - 1;
+ *to = '\0';
+ }else{
+ to--;
+ }
+ return to;
+}
diff --git a/sys/src/libc/port/strlen.c b/sys/src/libc/port/strlen.c
new file mode 100755
index 000000000..9a3c372a5
--- /dev/null
+++ b/sys/src/libc/port/strlen.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+long
+strlen(char *s)
+{
+
+ return strchr(s, 0) - s;
+}
diff --git a/sys/src/libc/port/strncat.c b/sys/src/libc/port/strncat.c
new file mode 100755
index 000000000..9d793e42d
--- /dev/null
+++ b/sys/src/libc/port/strncat.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strncat(char *s1, char *s2, long n)
+{
+ char *os1;
+
+ os1 = s1;
+ while(*s1++)
+ ;
+ s1--;
+ while(*s1++ = *s2++)
+ if(--n < 0) {
+ s1[-1] = 0;
+ break;
+ }
+ return os1;
+}
diff --git a/sys/src/libc/port/strncmp.c b/sys/src/libc/port/strncmp.c
new file mode 100755
index 000000000..b61db6d5d
--- /dev/null
+++ b/sys/src/libc/port/strncmp.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+int
+strncmp(char *s1, char *s2, long n)
+{
+ unsigned c1, c2;
+
+ while(n > 0) {
+ c1 = *s1++;
+ c2 = *s2++;
+ n--;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ if(c1 == 0)
+ break;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/strncpy.c b/sys/src/libc/port/strncpy.c
new file mode 100755
index 000000000..bd5acdedb
--- /dev/null
+++ b/sys/src/libc/port/strncpy.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strncpy(char *s1, char *s2, long n)
+{
+ int i;
+ char *os1;
+
+ os1 = s1;
+ for(i = 0; i < n; i++)
+ if((*s1++ = *s2++) == 0) {
+ while(++i < n)
+ *s1++ = 0;
+ return os1;
+ }
+ return os1;
+}
diff --git a/sys/src/libc/port/strpbrk.c b/sys/src/libc/port/strpbrk.c
new file mode 100755
index 000000000..bbd91eafb
--- /dev/null
+++ b/sys/src/libc/port/strpbrk.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#define N 256
+
+char*
+strpbrk(char *cs, char *cb)
+{
+ char map[N];
+ uchar *s=(uchar*)cs, *b=(uchar*)cb;
+
+ memset(map, 0, N);
+ for(;;) {
+ map[*b] = 1;
+ if(*b++ == 0)
+ break;
+ }
+ while(map[*s++] == 0)
+ ;
+ if(*--s)
+ return (char*)s;
+ return 0;
+}
diff --git a/sys/src/libc/port/strrchr.c b/sys/src/libc/port/strrchr.c
new file mode 100755
index 000000000..18fd38634
--- /dev/null
+++ b/sys/src/libc/port/strrchr.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strrchr(char *s, int c)
+{
+ char *r;
+
+ if(c == 0)
+ return strchr(s, 0);
+ r = 0;
+ while(s = strchr(s, c))
+ r = s++;
+ return r;
+}
diff --git a/sys/src/libc/port/strspn.c b/sys/src/libc/port/strspn.c
new file mode 100755
index 000000000..8744fd0ec
--- /dev/null
+++ b/sys/src/libc/port/strspn.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+#define N 256
+
+long
+strspn(char *s, char *b)
+{
+ char map[N], *os;
+
+ memset(map, 0, N);
+ while(*b)
+ map[*(uchar *)b++] = 1;
+ os = s;
+ while(map[*(uchar *)s++])
+ ;
+ return s - os - 1;
+}
diff --git a/sys/src/libc/port/strstr.c b/sys/src/libc/port/strstr.c
new file mode 100755
index 000000000..4541ccd04
--- /dev/null
+++ b/sys/src/libc/port/strstr.c
@@ -0,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+char*
+strstr(char *s1, char *s2)
+{
+ char *p, *pa, *pb;
+ int c0, c;
+
+ c0 = *s2;
+ if(c0 == 0)
+ return s1;
+ s2++;
+ for(p=strchr(s1, c0); p; p=strchr(p+1, c0)) {
+ pa = p;
+ for(pb=s2;; pb++) {
+ c = *pb;
+ if(c == 0)
+ return p;
+ if(c != *++pa)
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/strtod.c b/sys/src/libc/port/strtod.c
new file mode 100755
index 000000000..9ea252856
--- /dev/null
+++ b/sys/src/libc/port/strtod.c
@@ -0,0 +1,520 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/*
+ * This routine will convert to arbitrary precision
+ * floating point entirely in multi-precision fixed.
+ * The answer is the closest floating point number to
+ * the given decimal number. Exactly half way are
+ * rounded ala ieee rules.
+ * Method is to scale input decimal between .500 and .999...
+ * with external power of 2, then binary search for the
+ * closest mantissa to this decimal number.
+ * Nmant is is the required precision. (53 for ieee dp)
+ * Nbits is the max number of bits/word. (must be <= 28)
+ * Prec is calculated - the number of words of fixed mantissa.
+ */
+enum
+{
+ Nbits = 28, // bits safely represented in a ulong
+ Nmant = 53, // bits of precision required
+ Bias = 1022,
+ Prec = (Nmant+Nbits+1)/Nbits, // words of Nbits each to represent mantissa
+ Sigbit = 1<<(Prec*Nbits-Nmant), // first significant bit of Prec-th word
+ Ndig = 1500,
+ One = (ulong)(1<<Nbits),
+ Half = (ulong)(One>>1),
+ Maxe = 310,
+ Fsign = 1<<0, // found -
+ Fesign = 1<<1, // found e-
+ Fdpoint = 1<<2, // found .
+
+ S0 = 0, // _ _S0 +S1 #S2 .S3
+ S1, // _+ #S2 .S3
+ S2, // _+# #S2 .S4 eS5
+ S3, // _+. #S4
+ S4, // _+#.# #S4 eS5
+ S5, // _+#.#e +S6 #S7
+ S6, // _+#.#e+ #S7
+ S7, // _+#.#e+# #S7
+};
+
+static int xcmp(char*, char*);
+static int fpcmp(char*, ulong*);
+static void frnorm(ulong*);
+static void divascii(char*, int*, int*, int*);
+static void mulascii(char*, int*, int*, int*);
+static void divby(char*, int*, int);
+
+typedef struct Tab Tab;
+struct Tab
+{
+ int bp;
+ int siz;
+ char* cmp;
+};
+
+double
+strtod(char *as, char **aas)
+{
+ int na, ona, ex, dp, bp, c, i, flag, state;
+ ulong low[Prec], hig[Prec], mid[Prec], num, den;
+ double d;
+ char *s, a[Ndig];
+
+ flag = 0; // Fsign, Fesign, Fdpoint
+ na = 0; // number of digits of a[]
+ dp = 0; // na of decimal point
+ ex = 0; // exonent
+
+ state = S0;
+ for(s=as;; s++) {
+ c = *s;
+ if(c >= '0' && c <= '9') {
+ switch(state) {
+ case S0:
+ case S1:
+ case S2:
+ state = S2;
+ break;
+ case S3:
+ case S4:
+ state = S4;
+ break;
+
+ case S5:
+ case S6:
+ case S7:
+ state = S7;
+ ex = ex*10 + (c-'0');
+ continue;
+ }
+ if(na == 0 && c == '0') {
+ dp--;
+ continue;
+ }
+ if(na < Ndig-50)
+ a[na++] = c;
+ continue;
+ }
+ switch(c) {
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ if(state == S0)
+ continue;
+ break;
+ case '-':
+ if(state == S0)
+ flag |= Fsign;
+ else
+ flag |= Fesign;
+ case '+':
+ if(state == S0)
+ state = S1;
+ else
+ if(state == S5)
+ state = S6;
+ else
+ break; // syntax
+ continue;
+ case '.':
+ flag |= Fdpoint;
+ dp = na;
+ if(state == S0 || state == S1) {
+ state = S3;
+ continue;
+ }
+ if(state == S2) {
+ state = S4;
+ continue;
+ }
+ break;
+ case 'e':
+ case 'E':
+ if(state == S2 || state == S4) {
+ state = S5;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ /*
+ * clean up return char-pointer
+ */
+ switch(state) {
+ case S0:
+ if(xcmp(s, "nan") == 0) {
+ if(aas != nil)
+ *aas = s+3;
+ goto retnan;
+ }
+ case S1:
+ if(xcmp(s, "infinity") == 0) {
+ if(aas != nil)
+ *aas = s+8;
+ goto retinf;
+ }
+ if(xcmp(s, "inf") == 0) {
+ if(aas != nil)
+ *aas = s+3;
+ goto retinf;
+ }
+ case S3:
+ if(aas != nil)
+ *aas = as;
+ goto ret0; // no digits found
+ case S6:
+ s--; // back over +-
+ case S5:
+ s--; // back over e
+ break;
+ }
+ if(aas != nil)
+ *aas = s;
+
+ if(flag & Fdpoint)
+ while(na > 0 && a[na-1] == '0')
+ na--;
+ if(na == 0)
+ goto ret0; // zero
+ a[na] = 0;
+ if(!(flag & Fdpoint))
+ dp = na;
+ if(flag & Fesign)
+ ex = -ex;
+ dp += ex;
+ if(dp < -Maxe-Nmant/3) /* actually -Nmant*log(2)/log(10), but Nmant/3 close enough */
+ goto ret0; // underflow by exp
+ else
+ if(dp > +Maxe)
+ goto retinf; // overflow by exp
+
+ /*
+ * normalize the decimal ascii number
+ * to range .[5-9][0-9]* e0
+ */
+ bp = 0; // binary exponent
+ while(dp > 0)
+ divascii(a, &na, &dp, &bp);
+ while(dp < 0 || a[0] < '5')
+ mulascii(a, &na, &dp, &bp);
+ a[na] = 0;
+
+ /*
+ * very small numbers are represented using
+ * bp = -Bias+1. adjust accordingly.
+ */
+ if(bp < -Bias+1){
+ ona = na;
+ divby(a, &na, -bp-Bias+1);
+ if(na < ona){
+ memmove(a+ona-na, a, na);
+ memset(a, '0', ona-na);
+ na = ona;
+ }
+ a[na] = 0;
+ bp = -Bias+1;
+ }
+
+ /* close approx by naive conversion */
+ num = 0;
+ den = 1;
+ for(i=0; i<9 && (c=a[i]); i++) {
+ num = num*10 + (c-'0');
+ den *= 10;
+ }
+ low[0] = umuldiv(num, One, den);
+ hig[0] = umuldiv(num+1, One, den);
+ for(i=1; i<Prec; i++) {
+ low[i] = 0;
+ hig[i] = One-1;
+ }
+
+ /* binary search for closest mantissa */
+ for(;;) {
+ /* mid = (hig + low) / 2 */
+ c = 0;
+ for(i=0; i<Prec; i++) {
+ mid[i] = hig[i] + low[i];
+ if(c)
+ mid[i] += One;
+ c = mid[i] & 1;
+ mid[i] >>= 1;
+ }
+ frnorm(mid);
+
+ /* compare */
+ c = fpcmp(a, mid);
+ if(c > 0) {
+ c = 1;
+ for(i=0; i<Prec; i++)
+ if(low[i] != mid[i]) {
+ c = 0;
+ low[i] = mid[i];
+ }
+ if(c)
+ break; // between mid and hig
+ continue;
+ }
+ if(c < 0) {
+ for(i=0; i<Prec; i++)
+ hig[i] = mid[i];
+ continue;
+ }
+
+ /* only hard part is if even/odd roundings wants to go up */
+ c = mid[Prec-1] & (Sigbit-1);
+ if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
+ mid[Prec-1] -= c;
+ break; // exactly mid
+ }
+
+ /* normal rounding applies */
+ c = mid[Prec-1] & (Sigbit-1);
+ mid[Prec-1] -= c;
+ if(c >= Sigbit/2) {
+ mid[Prec-1] += Sigbit;
+ frnorm(mid);
+ }
+ d = 0;
+ for(i=0; i<Prec; i++)
+ d = d*One + mid[i];
+ if(flag & Fsign)
+ d = -d;
+ d = ldexp(d, bp - Prec*Nbits);
+ return d;
+
+ret0:
+ return 0;
+
+retnan:
+ return NaN();
+
+retinf:
+ if(flag & Fsign)
+ return Inf(-1);
+ return Inf(+1);
+}
+
+static void
+frnorm(ulong *f)
+{
+ int i, c;
+
+ c = 0;
+ for(i=Prec-1; i>0; i--) {
+ f[i] += c;
+ c = f[i] >> Nbits;
+ f[i] &= One-1;
+ }
+ f[0] += c;
+}
+
+static int
+fpcmp(char *a, ulong* f)
+{
+ ulong tf[Prec];
+ int i, d, c;
+
+ for(i=0; i<Prec; i++)
+ tf[i] = f[i];
+
+ for(;;) {
+ /* tf *= 10 */
+ for(i=0; i<Prec; i++)
+ tf[i] = tf[i]*10;
+ frnorm(tf);
+ d = (tf[0] >> Nbits) + '0';
+ tf[0] &= One-1;
+
+ /* compare next digit */
+ c = *a;
+ if(c == 0) {
+ if('0' < d)
+ return -1;
+ if(tf[0] != 0)
+ goto cont;
+ for(i=1; i<Prec; i++)
+ if(tf[i] != 0)
+ goto cont;
+ return 0;
+ }
+ if(c > d)
+ return +1;
+ if(c < d)
+ return -1;
+ a++;
+ cont:;
+ }
+}
+
+static void
+_divby(char *a, int *na, int b)
+{
+ int n, c;
+ char *p;
+
+ p = a;
+ n = 0;
+ while(n>>b == 0) {
+ c = *a++;
+ if(c == 0) {
+ while(n) {
+ c = n*10;
+ if(c>>b)
+ break;
+ n = c;
+ }
+ goto xx;
+ }
+ n = n*10 + c-'0';
+ (*na)--;
+ }
+ for(;;) {
+ c = n>>b;
+ n -= c<<b;
+ *p++ = c + '0';
+ c = *a++;
+ if(c == 0)
+ break;
+ n = n*10 + c-'0';
+ }
+ (*na)++;
+xx:
+ while(n) {
+ n = n*10;
+ c = n>>b;
+ n -= c<<b;
+ *p++ = c + '0';
+ (*na)++;
+ }
+ *p = 0;
+}
+
+static void
+divby(char *a, int *na, int b)
+{
+ while(b > 9){
+ _divby(a, na, 9);
+ a[*na] = 0;
+ b -= 9;
+ }
+ if(b > 0)
+ _divby(a, na, b);
+}
+
+static Tab tab1[] =
+{
+ 1, 0, "",
+ 3, 1, "7",
+ 6, 2, "63",
+ 9, 3, "511",
+ 13, 4, "8191",
+ 16, 5, "65535",
+ 19, 6, "524287",
+ 23, 7, "8388607",
+ 26, 8, "67108863",
+ 27, 9, "134217727",
+};
+
+static void
+divascii(char *a, int *na, int *dp, int *bp)
+{
+ int b, d;
+ Tab *t;
+
+ d = *dp;
+ if(d >= nelem(tab1))
+ d = nelem(tab1)-1;
+ t = tab1 + d;
+ b = t->bp;
+ if(memcmp(a, t->cmp, t->siz) > 0)
+ d--;
+ *dp -= d;
+ *bp += b;
+ divby(a, na, b);
+}
+
+static void
+mulby(char *a, char *p, char *q, int b)
+{
+ int n, c;
+
+ n = 0;
+ *p = 0;
+ for(;;) {
+ q--;
+ if(q < a)
+ break;
+ c = *q - '0';
+ c = (c<<b) + n;
+ n = c/10;
+ c -= n*10;
+ p--;
+ *p = c + '0';
+ }
+ while(n) {
+ c = n;
+ n = c/10;
+ c -= n*10;
+ p--;
+ *p = c + '0';
+ }
+}
+
+static Tab tab2[] =
+{
+ 1, 1, "", // dp = 0-0
+ 3, 3, "125",
+ 6, 5, "15625",
+ 9, 7, "1953125",
+ 13, 10, "1220703125",
+ 16, 12, "152587890625",
+ 19, 14, "19073486328125",
+ 23, 17, "11920928955078125",
+ 26, 19, "1490116119384765625",
+ 27, 19, "7450580596923828125", // dp 8-9
+};
+
+static void
+mulascii(char *a, int *na, int *dp, int *bp)
+{
+ char *p;
+ int d, b;
+ Tab *t;
+
+ d = -*dp;
+ if(d >= nelem(tab2))
+ d = nelem(tab2)-1;
+ t = tab2 + d;
+ b = t->bp;
+ if(memcmp(a, t->cmp, t->siz) < 0)
+ d--;
+ p = a + *na;
+ *bp -= b;
+ *dp += d;
+ *na += d;
+ mulby(a, p+d, p, b);
+}
+
+static int
+xcmp(char *a, char *b)
+{
+ int c1, c2;
+
+ while(c1 = *b++) {
+ c2 = *a++;
+ if(isupper(c2))
+ c2 = tolower(c2);
+ if(c1 != c2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/sys/src/libc/port/strtok.c b/sys/src/libc/port/strtok.c
new file mode 100755
index 000000000..cb8d5fdf5
--- /dev/null
+++ b/sys/src/libc/port/strtok.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+
+#define N 256
+
+char*
+strtok(char *s, char *b)
+{
+ static char *under_rock;
+ char map[N], *os;
+
+ memset(map, 0, N);
+ while(*b)
+ map[*(uchar*)b++] = 1;
+ if(s == 0)
+ s = under_rock;
+ while(map[*(uchar*)s++])
+ ;
+ if(*--s == 0)
+ return 0;
+ os = s;
+ while(map[*(uchar*)s] == 0)
+ if(*s++ == 0) {
+ under_rock = s-1;
+ return os;
+ }
+ *s++ = 0;
+ under_rock = s;
+ return os;
+}
diff --git a/sys/src/libc/port/strtol.c b/sys/src/libc/port/strtol.c
new file mode 100755
index 000000000..e512910a8
--- /dev/null
+++ b/sys/src/libc/port/strtol.c
@@ -0,0 +1,101 @@
+#include <u.h>
+#include <libc.h>
+
+#define LONG_MAX 2147483647L
+#define LONG_MIN -2147483648L
+
+long
+strtol(char *nptr, char **endptr, int base)
+{
+ char *p;
+ long n, nn, m;
+ int c, ovfl, v, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ for(;; p++) {
+ switch(*p) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\v':
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Sign
+ */
+ if(*p=='-' || *p=='+')
+ if(*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if(base==0) {
+ base = 10;
+ if(*p == '0') {
+ base = 8;
+ if(p[1]=='x' || p[1]=='X') {
+ p += 2;
+ base = 16;
+ }
+ }
+ } else
+ if(base==16 && *p=='0'){
+ if(p[1]=='x' || p[1]=='X')
+ p += 2;
+ } else
+ if(base<0 || 36<base)
+ goto Return;
+
+ /*
+ * Non-empty sequence of digits
+ */
+ m = LONG_MAX/base;
+ for(;; p++,ndig++){
+ c = *p;
+ v = base;
+ if('0'<=c && c<='9')
+ v = c - '0';
+ else
+ if('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else
+ if('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if(v >= base)
+ break;
+ if(n > m)
+ ovfl = 1;
+ nn = n*base + v;
+ if(nn < n)
+ ovfl = 1;
+ n = nn;
+ }
+
+Return:
+ if(ndig == 0)
+ p = nptr;
+ if(endptr)
+ *endptr = p;
+ if(ovfl){
+ if(neg)
+ return LONG_MIN;
+ return LONG_MAX;
+ }
+ if(neg)
+ return -n;
+ return n;
+}
diff --git a/sys/src/libc/port/strtoll.c b/sys/src/libc/port/strtoll.c
new file mode 100755
index 000000000..444728ae5
--- /dev/null
+++ b/sys/src/libc/port/strtoll.c
@@ -0,0 +1,101 @@
+#include <u.h>
+#include <libc.h>
+
+#define VLONG_MAX ~(1LL<<63)
+#define VLONG_MIN (1LL<<63)
+
+vlong
+strtoll(char *nptr, char **endptr, int base)
+{
+ char *p;
+ vlong n, nn, m;
+ int c, ovfl, v, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ for(;; p++) {
+ switch(*p) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\v':
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Sign
+ */
+ if(*p=='-' || *p=='+')
+ if(*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if(base==0){
+ base = 10;
+ if(*p == '0') {
+ base = 8;
+ if(p[1]=='x' || p[1]=='X') {
+ p += 2;
+ base = 16;
+ }
+ }
+ } else
+ if(base==16 && *p=='0') {
+ if(p[1]=='x' || p[1]=='X')
+ p += 2;
+ } else
+ if(base<0 || 36<base)
+ goto Return;
+
+ /*
+ * Non-empty sequence of digits
+ */
+ m = VLONG_MAX/base;
+ for(;; p++,ndig++) {
+ c = *p;
+ v = base;
+ if('0'<=c && c<='9')
+ v = c - '0';
+ else
+ if('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else
+ if('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if(v >= base)
+ break;
+ if(n > m)
+ ovfl = 1;
+ nn = n*base + v;
+ if(nn < n)
+ ovfl = 1;
+ n = nn;
+ }
+
+Return:
+ if(ndig == 0)
+ p = nptr;
+ if(endptr)
+ *endptr = p;
+ if(ovfl){
+ if(neg)
+ return VLONG_MIN;
+ return VLONG_MAX;
+ }
+ if(neg)
+ return -n;
+ return n;
+}
diff --git a/sys/src/libc/port/strtoul.c b/sys/src/libc/port/strtoul.c
new file mode 100755
index 000000000..6a12fdd94
--- /dev/null
+++ b/sys/src/libc/port/strtoul.c
@@ -0,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+
+#define ULONG_MAX 4294967295UL
+
+ulong
+strtoul(char *nptr, char **endptr, int base)
+{
+ char *p;
+ ulong n, nn, m;
+ int c, ovfl, neg, v, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ for(;;p++){
+ switch(*p){
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\v':
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Sign
+ */
+ if(*p=='-' || *p=='+')
+ if(*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if(base==0){
+ if(*p != '0')
+ base = 10;
+ else{
+ base = 8;
+ if(p[1]=='x' || p[1]=='X')
+ base = 16;
+ }
+ }
+ if(base<2 || 36<base)
+ goto Return;
+ if(base==16 && *p=='0'){
+ if(p[1]=='x' || p[1]=='X')
+ if(('0' <= p[2] && p[2] <= '9')
+ ||('a' <= p[2] && p[2] <= 'f')
+ ||('A' <= p[2] && p[2] <= 'F'))
+ p += 2;
+ }
+ /*
+ * Non-empty sequence of digits
+ */
+ n = 0;
+ m = ULONG_MAX/base;
+ for(;; p++,ndig++){
+ c = *p;
+ v = base;
+ if('0'<=c && c<='9')
+ v = c - '0';
+ else if('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else if('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if(v >= base)
+ break;
+ if(n > m)
+ ovfl = 1;
+ nn = n*base + v;
+ if(nn < n)
+ ovfl = 1;
+ n = nn;
+ }
+
+ Return:
+ if(ndig == 0)
+ p = nptr;
+ if(endptr)
+ *endptr = p;
+ if(ovfl)
+ return ULONG_MAX;
+ if(neg)
+ return -n;
+ return n;
+}
diff --git a/sys/src/libc/port/strtoull.c b/sys/src/libc/port/strtoull.c
new file mode 100755
index 000000000..85ab7c5fc
--- /dev/null
+++ b/sys/src/libc/port/strtoull.c
@@ -0,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+
+#define UVLONG_MAX (1LL<<63)
+
+uvlong
+strtoull(char *nptr, char **endptr, int base)
+{
+ char *p;
+ uvlong n, nn, m;
+ int c, ovfl, v, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ for(;; p++) {
+ switch(*p) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\v':
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Sign
+ */
+ if(*p == '-' || *p == '+')
+ if(*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if(base == 0) {
+ base = 10;
+ if(*p == '0') {
+ base = 8;
+ if(p[1] == 'x' || p[1] == 'X'){
+ p += 2;
+ base = 16;
+ }
+ }
+ } else
+ if(base == 16 && *p == '0') {
+ if(p[1] == 'x' || p[1] == 'X')
+ p += 2;
+ } else
+ if(base < 0 || 36 < base)
+ goto Return;
+
+ /*
+ * Non-empty sequence of digits
+ */
+ m = UVLONG_MAX/base;
+ for(;; p++,ndig++) {
+ c = *p;
+ v = base;
+ if('0' <= c && c <= '9')
+ v = c - '0';
+ else
+ if('a' <= c && c <= 'z')
+ v = c - 'a' + 10;
+ else
+ if('A' <= c && c <= 'Z')
+ v = c - 'A' + 10;
+ if(v >= base)
+ break;
+ if(n > m)
+ ovfl = 1;
+ nn = n*base + v;
+ if(nn < n)
+ ovfl = 1;
+ n = nn;
+ }
+
+Return:
+ if(ndig == 0)
+ p = nptr;
+ if(endptr)
+ *endptr = p;
+ if(ovfl)
+ return UVLONG_MAX;
+ if(neg)
+ return -n;
+ return n;
+}
diff --git a/sys/src/libc/port/tan.c b/sys/src/libc/port/tan.c
new file mode 100755
index 000000000..b562e282d
--- /dev/null
+++ b/sys/src/libc/port/tan.c
@@ -0,0 +1,67 @@
+/*
+ floating point tangent
+
+ A series is used after range reduction.
+ Coefficients are #4285 from Hart & Cheney. (19.74D)
+ */
+
+#include <u.h>
+#include <libc.h>
+
+static double p0 = -0.1306820264754825668269611177e+5;
+static double p1 = 0.1055970901714953193602353981e+4;
+static double p2 = -0.1550685653483266376941705728e+2;
+static double p3 = 0.3422554387241003435328470489e-1;
+static double p4 = 0.3386638642677172096076369e-4;
+static double q0 = -0.1663895238947119001851464661e+5;
+static double q1 = 0.4765751362916483698926655581e+4;
+static double q2 = -0.1555033164031709966900124574e+3;
+
+double
+tan(double arg)
+{
+ double temp, e, x, xsq;
+ int flag, sign, i;
+
+ flag = 0;
+ sign = 0;
+ if(arg < 0){
+ arg = -arg;
+ sign++;
+ }
+ arg = 2*arg/PIO2; /* overflow? */
+ x = modf(arg, &e);
+ i = e;
+ switch(i%4) {
+ case 1:
+ x = 1 - x;
+ flag = 1;
+ break;
+
+ case 2:
+ sign = !sign;
+ flag = 1;
+ break;
+
+ case 3:
+ x = 1 - x;
+ sign = !sign;
+ break;
+
+ case 0:
+ break;
+ }
+
+ xsq = x*x;
+ temp = ((((p4*xsq+p3)*xsq+p2)*xsq+p1)*xsq+p0)*x;
+ temp = temp/(((xsq+q2)*xsq+q1)*xsq+q0);
+
+ if(flag) {
+ if(temp == 0)
+ return NaN();
+ temp = 1/temp;
+ }
+ if(sign)
+ temp = -temp;
+ return temp;
+}
diff --git a/sys/src/libc/port/tanh.c b/sys/src/libc/port/tanh.c
new file mode 100755
index 000000000..32e3a35ac
--- /dev/null
+++ b/sys/src/libc/port/tanh.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ tanh(arg) computes the hyperbolic tangent of its floating
+ point argument.
+
+ sinh and cosh are called except for large arguments, which
+ would cause overflow improperly.
+ */
+
+double
+tanh(double arg)
+{
+
+ if(arg < 0) {
+ arg = -arg;
+ if(arg > 21)
+ return -1;
+ return -sinh(arg)/cosh(arg);
+ }
+ if(arg > 21)
+ return 1;
+ return sinh(arg)/cosh(arg);
+}
diff --git a/sys/src/libc/port/tokenize.c b/sys/src/libc/port/tokenize.c
new file mode 100755
index 000000000..de234d440
--- /dev/null
+++ b/sys/src/libc/port/tokenize.c
@@ -0,0 +1,107 @@
+#include <u.h>
+#include <libc.h>
+
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s, char *sep)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(*s != '\0'){
+ *s = '\0';
+ if(t == s)
+ t++;
+ }
+ return t;
+}
+
+static char*
+etoken(char *t, char *sep)
+{
+ int quoting;
+
+ /* move to end of next token */
+ quoting = 0;
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t += 2;
+ }
+ return t;
+}
+
+int
+gettokens(char *s, char **args, int maxargs, char *sep)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && utfrune(sep, *s)!=nil)
+ *s++ = '\0';
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = etoken(s, sep);
+ }
+
+ return nargs;
+}
+
+int
+tokenize(char *s, char **args, int maxargs)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && utfrune(qsep, *s)!=nil)
+ s++;
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = qtoken(s, qsep);
+ }
+
+ return nargs;
+}
diff --git a/sys/src/libc/port/toupper.c b/sys/src/libc/port/toupper.c
new file mode 100755
index 000000000..d5fcf7b19
--- /dev/null
+++ b/sys/src/libc/port/toupper.c
@@ -0,0 +1,19 @@
+#include <ctype.h>
+
+int
+toupper(int c)
+{
+
+ if(c < 'a' || c > 'z')
+ return c;
+ return _toupper(c);
+}
+
+int
+tolower(int c)
+{
+
+ if(c < 'A' || c > 'Z')
+ return c;
+ return _tolower(c);
+}
diff --git a/sys/src/libc/port/u16.c b/sys/src/libc/port/u16.c
new file mode 100755
index 000000000..4e1637120
--- /dev/null
+++ b/sys/src/libc/port/u16.c
@@ -0,0 +1,53 @@
+#include <u.h>
+#include <libc.h>
+static char t16e[] = "0123456789ABCDEF";
+
+int
+dec16(uchar *out, int lim, char *in, int n)
+{
+ int c, w = 0, i = 0;
+ uchar *start = out;
+ uchar *eout = out + lim;
+
+ while(n-- > 0){
+ c = *in++;
+ if('0' <= c && c <= '9')
+ c = c - '0';
+ else if('a' <= c && c <= 'z')
+ c = c - 'a' + 10;
+ else if('A' <= c && c <= 'Z')
+ c = c - 'A' + 10;
+ else
+ continue;
+ w = (w<<4) + c;
+ i++;
+ if(i == 2){
+ if(out + 1 > eout)
+ goto exhausted;
+ *out++ = w;
+ w = 0;
+ i = 0;
+ }
+ }
+exhausted:
+ return out - start;
+}
+
+int
+enc16(char *out, int lim, uchar *in, int n)
+{
+ uint c;
+ char *eout = out + lim;
+ char *start = out;
+
+ while(n-- > 0){
+ c = *in++;
+ if(out + 2 >= eout)
+ goto exhausted;
+ *out++ = t16e[c>>4];
+ *out++ = t16e[c&0xf];
+ }
+exhausted:
+ *out = 0;
+ return out - start;
+}
diff --git a/sys/src/libc/port/u32.c b/sys/src/libc/port/u32.c
new file mode 100755
index 000000000..7423984a2
--- /dev/null
+++ b/sys/src/libc/port/u32.c
@@ -0,0 +1,110 @@
+#include <u.h>
+#include <libc.h>
+
+int
+dec32(uchar *dest, int ndest, char *src, int nsrc)
+{
+ char *s, *tab;
+ uchar *start;
+ int i, u[8];
+
+ if(ndest+1 < (5*nsrc+7)/8)
+ return -1;
+ start = dest;
+ tab = "23456789abcdefghijkmnpqrstuvwxyz";
+ while(nsrc>=8){
+ for(i=0; i<8; i++){
+ s = strchr(tab,(int)src[i]);
+ u[i] = s ? s-tab : 0;
+ }
+ *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+ *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+ *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+ *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+ *dest++ = ((0x7 & u[6])<<5) | u[7];
+ src += 8;
+ nsrc -= 8;
+ }
+ if(nsrc > 0){
+ if(nsrc == 1 || nsrc == 3 || nsrc == 6)
+ return -1;
+ for(i=0; i<nsrc; i++){
+ s = strchr(tab,(int)src[i]);
+ u[i] = s ? s-tab : 0;
+ }
+ *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+ if(nsrc == 2)
+ goto out;
+ *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+ if(nsrc == 4)
+ goto out;
+ *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+ if(nsrc == 5)
+ goto out;
+ *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+ }
+out:
+ return dest-start;
+}
+
+int
+enc32(char *dest, int ndest, uchar *src, int nsrc)
+{
+ char *tab, *start;
+ int j;
+
+ if(ndest <= (8*nsrc+4)/5 )
+ return -1;
+ start = dest;
+ tab = "23456789abcdefghijkmnpqrstuvwxyz";
+ while(nsrc>=5){
+ j = (0x1f & (src[0]>>3));
+ *dest++ = tab[j];
+ j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6));
+ *dest++ = tab[j];
+ j = (0x1f & (src[1]>>1));
+ *dest++ = tab[j];
+ j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4));
+ *dest++ = tab[j];
+ j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7));
+ *dest++ = tab[j];
+ j = (0x1f & (src[3]>>2));
+ *dest++ = tab[j];
+ j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5));
+ *dest++ = tab[j];
+ j = (0x1f & (src[4]));
+ *dest++ = tab[j];
+ src += 5;
+ nsrc -= 5;
+ }
+ if(nsrc){
+ j = (0x1f & (src[0]>>3));
+ *dest++ = tab[j];
+ j = (0x1c & (src[0]<<2));
+ if(nsrc == 1)
+ goto out;
+ j |= (0x03 & (src[1]>>6));
+ *dest++ = tab[j];
+ j = (0x1f & (src[1]>>1));
+ if(nsrc == 2)
+ goto out;
+ *dest++ = tab[j];
+ j = (0x10 & (src[1]<<4));
+ if(nsrc == 3)
+ goto out;
+ j |= (0x0f & (src[2]>>4));
+ *dest++ = tab[j];
+ j = (0x1e & (src[2]<<1));
+ if(nsrc == 4)
+ goto out;
+ j |= (0x01 & (src[3]>>7));
+ *dest++ = tab[j];
+ j = (0x1f & (src[3]>>2));
+ *dest++ = tab[j];
+ j = (0x18 & (src[3]<<3));
+out:
+ *dest++ = tab[j];
+ }
+ *dest = 0;
+ return dest-start;
+}
diff --git a/sys/src/libc/port/u64.c b/sys/src/libc/port/u64.c
new file mode 100755
index 000000000..bf86c634c
--- /dev/null
+++ b/sys/src/libc/port/u64.c
@@ -0,0 +1,127 @@
+#include <u.h>
+#include <libc.h>
+
+enum {
+ INVAL= 255
+};
+
+static uchar t64d[256] = {
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, 62,INVAL,INVAL,INVAL, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL
+};
+static char t64e[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int
+dec64(uchar *out, int lim, char *in, int n)
+{
+ ulong b24;
+ uchar *start = out;
+ uchar *e = out + lim;
+ int i, c;
+
+ b24 = 0;
+ i = 0;
+ while(n-- > 0){
+
+ c = t64d[*(uchar*)in++];
+ if(c == INVAL)
+ continue;
+ switch(i){
+ case 0:
+ b24 = c<<18;
+ break;
+ case 1:
+ b24 |= c<<12;
+ break;
+ case 2:
+ b24 |= c<<6;
+ break;
+ case 3:
+ if(out + 3 > e)
+ goto exhausted;
+
+ b24 |= c;
+ *out++ = b24>>16;
+ *out++ = b24>>8;
+ *out++ = b24;
+ i = -1;
+ break;
+ }
+ i++;
+ }
+ switch(i){
+ case 2:
+ if(out + 1 > e)
+ goto exhausted;
+ *out++ = b24>>16;
+ break;
+ case 3:
+ if(out + 2 > e)
+ goto exhausted;
+ *out++ = b24>>16;
+ *out++ = b24>>8;
+ break;
+ }
+exhausted:
+ return out - start;
+}
+
+int
+enc64(char *out, int lim, uchar *in, int n)
+{
+ int i;
+ ulong b24;
+ char *start = out;
+ char *e = out + lim;
+
+ for(i = n/3; i > 0; i--){
+ b24 = (*in++)<<16;
+ b24 |= (*in++)<<8;
+ b24 |= *in++;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = t64e[(b24>>6)&0x3f];
+ *out++ = t64e[(b24)&0x3f];
+ }
+
+ switch(n%3){
+ case 2:
+ b24 = (*in++)<<16;
+ b24 |= (*in)<<8;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = t64e[(b24>>6)&0x3f];
+ *out++ = '=';
+ break;
+ case 1:
+ b24 = (*in)<<16;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = '=';
+ *out++ = '=';
+ break;
+ }
+exhausted:
+ *out = 0;
+ return out - start;
+}
diff --git a/sys/src/libc/port/utfecpy.c b/sys/src/libc/port/utfecpy.c
new file mode 100755
index 000000000..565368bdf
--- /dev/null
+++ b/sys/src/libc/port/utfecpy.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+utfecpy(char *to, char *e, char *from)
+{
+ char *end;
+
+ if(to >= e)
+ return to;
+ end = memccpy(to, from, '\0', e - to);
+ if(end == nil){
+ end = e;
+ while(end>to && (*--end&0xC0)==0x80)
+ ;
+ *end = '\0';
+ }else{
+ end--;
+ }
+ return end;
+}
diff --git a/sys/src/libc/port/utflen.c b/sys/src/libc/port/utflen.c
new file mode 100755
index 000000000..4356fb1c2
--- /dev/null
+++ b/sys/src/libc/port/utflen.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+
+int
+utflen(char *s)
+{
+ int c;
+ long n;
+ Rune rune;
+
+ n = 0;
+ for(;;) {
+ c = *(uchar*)s;
+ if(c < Runeself) {
+ if(c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+}
diff --git a/sys/src/libc/port/utfnlen.c b/sys/src/libc/port/utfnlen.c
new file mode 100755
index 000000000..43e9c4aae
--- /dev/null
+++ b/sys/src/libc/port/utfnlen.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+int
+utfnlen(char *s, long m)
+{
+ int c;
+ long n;
+ Rune rune;
+ char *es;
+
+ es = s + m;
+ for(n = 0; s < es; n++) {
+ c = *(uchar*)s;
+ if(c < Runeself){
+ if(c == '\0')
+ break;
+ s++;
+ continue;
+ }
+ if(!fullrune(s, es-s))
+ break;
+ s += chartorune(&rune, s);
+ }
+ return n;
+}
diff --git a/sys/src/libc/port/utfrrune.c b/sys/src/libc/port/utfrrune.c
new file mode 100755
index 000000000..c52791f88
--- /dev/null
+++ b/sys/src/libc/port/utfrrune.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+utfrrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ char *s1;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strrchr(s, c);
+
+ s1 = 0;
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return s1;
+ if(c1 == c)
+ s1 = s;
+ s++;
+ continue;
+ }
+ c1 = chartorune(&r, s);
+ if(r == c)
+ s1 = s;
+ s += c1;
+ }
+}
diff --git a/sys/src/libc/port/utfrune.c b/sys/src/libc/port/utfrune.c
new file mode 100755
index 000000000..8d975855d
--- /dev/null
+++ b/sys/src/libc/port/utfrune.c
@@ -0,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+utfrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ int n;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strchr(s, c);
+
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return 0;
+ if(c1 == c)
+ return s;
+ s++;
+ continue;
+ }
+ n = chartorune(&r, s);
+ if(r == c)
+ return s;
+ s += n;
+ }
+}
diff --git a/sys/src/libc/port/utfutf.c b/sys/src/libc/port/utfutf.c
new file mode 100755
index 000000000..48d1ee831
--- /dev/null
+++ b/sys/src/libc/port/utfutf.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+char*
+utfutf(char *s1, char *s2)
+{
+ char *p;
+ long f, n1, n2;
+ Rune r;
+
+ n1 = chartorune(&r, s2);
+ f = r;
+ if(f <= Runesync) /* represents self */
+ return strstr(s1, s2);
+
+ n2 = strlen(s2);
+ for(p=s1; p=utfrune(p, f); p+=n1)
+ if(strncmp(p, s2, n2) == 0)
+ return p;
+ return 0;
+}
diff --git a/sys/src/libc/power/argv0.s b/sys/src/libc/power/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/power/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/power/atom.s b/sys/src/libc/power/atom.s
new file mode 100755
index 000000000..86776e6ed
--- /dev/null
+++ b/sys/src/libc/power/atom.s
@@ -0,0 +1,65 @@
+TEXT ainc(SB),$0 /* long ainc(long *); */
+ MOVW R3, R4
+xincloop:
+ LWAR (R4), R3
+ ADD $1, R3
+ DCBT (R4) /* fix 405 errata cpu_210 */
+ STWCCC R3, (R4)
+ BNE xincloop
+ RETURN
+
+TEXT adec(SB),$0 /* long adec(long *); */
+ MOVW R3, R4
+xdecloop:
+ LWAR (R4), R3
+ ADD $-1, R3
+ DCBT (R4) /* fix 405 errata cpu_210 */
+ STWCCC R3, (R4)
+ BNE xdecloop
+ RETURN
+
+TEXT loadlink(SB), $0
+
+ LWAR (R3), R3
+ RETURN
+
+TEXT storecond(SB), $0
+
+ MOVW val+4(FP), R4
+ DCBT (R3) /* fix 405 errata cpu_210 */
+ STWCCC R4, (R3)
+ BNE storecondfail
+ MOVW $1, R3
+ RETURN
+storecondfail:
+ MOVW $0, R3
+ RETURN
+
+/*
+ * int cas32(u32int *p, u32int ov, u32int nv);
+ * int cas(uint *p, int ov, int nv);
+ * int casp(void **p, void *ov, void *nv);
+ * int casl(ulong *p, ulong ov, ulong nv);
+ */
+
+TEXT cas32+0(SB),0,$0
+TEXT cas+0(SB),0,$0
+TEXT casp+0(SB),0,$0
+TEXT casl+0(SB),0,$0
+ MOVW ov+4(FP),R4
+ MOVW nv+8(FP),R8
+ LWAR (R3),R5
+ CMP R5,R4
+ BNE fail
+ DCBT (R3) /* fix 405 errata cpu_210 */
+ STWCCC R8,(R3)
+ BNE fail1
+ MOVW $1,R3
+ RETURN
+fail:
+ DCBT (R3) /* fix 405 errata cpu_210 */
+ STWCCC R5,(R3) /* give up exclusive access */
+fail1:
+ MOVW R0,R3
+ RETURN
+ END
diff --git a/sys/src/libc/power/cycles.s b/sys/src/libc/power/cycles.s
new file mode 100755
index 000000000..441171136
--- /dev/null
+++ b/sys/src/libc/power/cycles.s
@@ -0,0 +1,17 @@
+#define TBRL 268
+#define TBRU 269 /* Time base Upper/Lower (Reading) */
+
+/*
+ * time stamp counter; _cycles since power up
+ * Runs at fasthz/4 cycles per second (m->clkin>>3)
+ */
+TEXT cycles(SB),1,$0
+loop:
+ MOVW SPR(TBRU),R7
+ MOVW SPR(TBRL),R8
+ MOVW SPR(TBRU),R5
+ CMP R5,R7
+ BNE loop
+ MOVW R7,0(R3)
+ MOVW R8,4(R3)
+ RETURN
diff --git a/sys/src/libc/power/getcallerpc.s b/sys/src/libc/power/getcallerpc.s
new file mode 100755
index 000000000..62c3ee233
--- /dev/null
+++ b/sys/src/libc/power/getcallerpc.s
@@ -0,0 +1,4 @@
+TEXT getcallerpc(SB),1,$-4
+ MOVW 0(R1), R3
+ RETURN
+
diff --git a/sys/src/libc/power/getfcr.s b/sys/src/libc/power/getfcr.s
new file mode 100755
index 000000000..b61d52e68
--- /dev/null
+++ b/sys/src/libc/power/getfcr.s
@@ -0,0 +1,28 @@
+TEXT getfcr(SB), $8
+ MOVFL FPSCR, F3
+ FMOVD F3, f-8(SP)
+ MOVW -4(SP), R3
+ RETURN
+
+TEXT getfsr(SB), $8
+ MOVFL FPSCR, F3
+ FMOVD F3, f-8(SP)
+ MOVW -4(SP), R3
+ RETURN
+
+TEXT setfcr(SB), $8
+ SYNC
+ MOVW R3, -4(SP)
+ FMOVD -8(SP), F3
+ MOVFL F3, FPSCR
+ ISYNC
+ RETURN
+
+TEXT setfsr(SB), $8
+ SYNC
+ MOVW R3, -4(SP)
+ FMOVD -8(SP), F3
+ MOVFL F3, FPSCR
+ ISYNC
+ RETURN
+
diff --git a/sys/src/libc/power/main9.s b/sys/src/libc/power/main9.s
new file mode 100755
index 000000000..46085adf5
--- /dev/null
+++ b/sys/src/libc/power/main9.s
@@ -0,0 +1,25 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVW $setSB(SB), R2
+ MOVW R3, _tos(SB)
+
+ MOVW $p-64(SP), R4
+ MOVW R4, _privates+0(SB)
+ MOVW $16, R4
+ MOVW R4, _nprivates+0(SB)
+
+ MOVW inargc-4(FP), R3
+ MOVW $inargv+0(FP), R4
+ MOVW R3, 4(R1)
+ MOVW R4, 8(R1)
+ BL main(SB)
+loop:
+ MOVW $_exitstr<>(SB), R3
+ MOVW R3, 4(R1)
+ BL exits(SB)
+ BR loop
+
+DATA _exitstr<>+0(SB)/4, $"main"
+GLOBL _exitstr<>+0(SB), $5
diff --git a/sys/src/libc/power/main9p.s b/sys/src/libc/power/main9p.s
new file mode 100755
index 000000000..76723cc10
--- /dev/null
+++ b/sys/src/libc/power/main9p.s
@@ -0,0 +1,37 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVW $setSB(SB), R2
+ MOVW R3, _tos(SB)
+
+ MOVW $p-64(SP), R4
+ MOVW R4, _privates+0(SB)
+ MOVW $16, R4
+ MOVW R4, _nprivates+0(SB)
+
+ BL _profmain(SB)
+ MOVW _tos(SB), R3
+ MOVW 4(R3), R4
+ MOVW R4, 0(R3)
+ MOVW inargc-4(FP), R3
+ MOVW $inargv+0(FP), R4
+ MOVW R3, 4(R1)
+ MOVW R4, 8(R1)
+ BL main(SB)
+loop:
+ MOVW $exits<>(SB), R3
+ MOVW R3, 4(R1)
+ BL exits(SB)
+ MOVW $_profin(SB), R3 /* force loading of profile */
+ BR loop
+
+TEXT _savearg(SB), 1, $0
+ RETURN
+
+TEXT _callpc(SB), 1, $0
+ MOVW argp-4(FP), R3
+ RETURN
+
+DATA exits<>+0(SB)/4, $"main"
+GLOBL exits<>+0(SB), $5
diff --git a/sys/src/libc/power/memccpy.s b/sys/src/libc/power/memccpy.s
new file mode 100755
index 000000000..4a4a34449
--- /dev/null
+++ b/sys/src/libc/power/memccpy.s
@@ -0,0 +1,23 @@
+ TEXT memccpy(SB), $0
+#define BDNZ BC 16,0,
+ MOVW R3, s1+0(FP)
+ MOVW n+12(FP), R7
+ MOVW s2+4(FP), R4
+ MOVBZ c+11(FP), R5
+ CMP R7, $0
+ BEQ nf
+ MOVW R7, CTR
+ SUB $1, R3
+ SUB $1, R4
+l1:
+ MOVBZU 1(R4), R6
+ CMP R6, R5
+ MOVBZU R6, 1(R3)
+ BEQ eq
+ BDNZ l1
+nf:
+ MOVW $0, R3
+ RETURN
+eq:
+ ADD $1, R3
+ RETURN
diff --git a/sys/src/libc/power/memcmp.s b/sys/src/libc/power/memcmp.s
new file mode 100755
index 000000000..f524fa9d3
--- /dev/null
+++ b/sys/src/libc/power/memcmp.s
@@ -0,0 +1,110 @@
+ TEXT memcmp(SB), $0
+#define BDNZ BC 16,0,
+ MOVW R3, s1+0(FP) /* R3 is pointer1 */
+
+/*
+ * performance:
+ * 67mb/sec aligned; 16mb/sec unaligned
+ */
+
+ MOVW n+8(FP), R4 /* R4 is count */
+ MOVW s2+4(FP), R5 /* R5 is pointer2 */
+
+/*
+ * let LSW do the work for 4 characters or less; aligned and unaligned
+ */
+ CMP R4, $0
+ BLE eq
+ CMP R4, $4
+ BLE out
+
+ XOR R3, R5, R9
+ ANDCC $3, R9
+ BNE l4 /* pointers misaligned; use LSW loop */
+
+/*
+ * do enough bytes to align pointers
+ */
+ ANDCC $3,R3, R9
+ BEQ l2
+ SUBC R9, $4, R9
+ MOVW R9, XER
+ LSW (R3), R10
+ ADD R9, R3
+ LSW (R5), R14
+ ADD R9, R5
+ SUB R9, R4
+ CMPU R10, R14
+ BNE ne
+
+/*
+ * compare 16 at a time
+ */
+l2:
+ SRAWCC $4, R4, R9
+ BLE l4
+ MOVW R9, CTR
+ SUB $4, R3
+ SUB $4, R5
+l3:
+ MOVWU 4(R3), R10
+ MOVWU 4(R5), R12
+ MOVWU 4(R3), R11
+ MOVWU 4(R5), R13
+ CMPU R10, R12
+ BNE ne
+ MOVWU 4(R3), R10
+ MOVWU 4(R5), R12
+ CMPU R11, R13
+ BNE ne
+ MOVWU 4(R3), R11
+ MOVWU 4(R5), R13
+ CMPU R10, R12
+ BNE ne
+ CMPU R11, R13
+ BNE ne
+ BDNZ l3
+ ADD $4, R3
+ ADD $4, R5
+ RLWNMCC $0, R4, $15, R4 /* residue */
+ BEQ eq
+
+/*
+ * do remaining words with LSW; also does unaligned case
+ */
+l4:
+ SRAWCC $2, R4, R9
+ BLE out
+ MOVW R9, CTR
+l5:
+ LSW (R3), $4, R10
+ ADD $4, R3
+ LSW (R5), $4, R11
+ ADD $4, R5
+ CMPU R10, R11
+ BNE ne
+ BDNZ l5
+ RLWNMCC $0, R4, $3, R4 /* residue */
+ BEQ eq
+
+/*
+ * do remaining bytes with final LSW
+ */
+out:
+ MOVW R4, XER
+ LSW (R3), R10
+ LSW (R5), R11
+ CMPU R10, R11
+ BNE ne
+
+eq:
+ MOVW $0, R3
+ RETURN
+
+ne:
+ MOVW $1, R3
+ BGE ret
+ MOVW $-1,R3
+ret:
+ RETURN
+ END
diff --git a/sys/src/libc/power/memmove.s b/sys/src/libc/power/memmove.s
new file mode 100755
index 000000000..34c1e3c5f
--- /dev/null
+++ b/sys/src/libc/power/memmove.s
@@ -0,0 +1,170 @@
+#define BDNZ BC 16,0,
+ TEXT memmove(SB), $0
+ BR move
+
+ TEXT memcpy(SB), $0
+move:
+
+/*
+ * performance:
+ * (tba)
+ */
+
+ MOVW R3, s1+0(FP)
+ MOVW n+8(FP), R9 /* R9 is count */
+ MOVW R3, R10 /* R10 is to-pointer */
+ CMP R9, $0
+ BEQ ret
+ BLT trap
+ MOVW s2+4(FP), R11 /* R11 is from-pointer */
+
+/*
+ * if no more than 16 bytes, just use one lsw/stsw
+ */
+ CMP R9, $16
+ BLE fout
+
+ ADD R9,R11, R13 /* R13 is end from-pointer */
+ ADD R9,R10, R12 /* R12 is end to-pointer */
+
+/*
+ * easiest test is copy backwards if
+ * destination string has higher mem address
+ */
+ CMPU R10, R11
+ BGT back
+
+/*
+ * test if both pointers
+ * are similarly word aligned
+ */
+ XOR R10,R11, R7
+ ANDCC $3,R7
+ BNE fbad
+
+/*
+ * move a few bytes to align pointers
+ */
+ ANDCC $3,R10,R7
+ BEQ f2
+ SUBC R7, $4, R7
+ SUB R7, R9
+ MOVW R7, XER
+ LSW (R11), R16
+ ADD R7, R11
+ STSW R16, (R10)
+ ADD R7, R10
+
+/*
+ * turn R14 into doubleword count
+ * copy 16 bytes at a time while there's room.
+ */
+f2:
+ SRAWCC $4, R9, R14
+ BLE fout
+ MOVW R14, CTR
+ SUB $4, R11
+ SUB $4, R10
+f3:
+ MOVWU 4(R11), R16
+ MOVWU 4(R11), R17
+ MOVWU 4(R11), R18
+ MOVWU 4(R11), R19
+ MOVWU R16, 4(R10)
+ MOVWU R17, 4(R10)
+ MOVWU R18, 4(R10)
+ MOVWU R19, 4(R10)
+ BDNZ f3
+ RLWNMCC $0, R9, $15, R9 /* residue */
+ BEQ ret
+ ADD $4, R11
+ ADD $4, R10
+
+/*
+ * move up to 16 bytes through R16 .. R19; aligned and unaligned
+ */
+fout:
+ MOVW R9, XER
+ LSW (R11), R16
+ STSW R16, (R10)
+ BR ret
+
+/*
+ * loop for unaligned copy, then copy up to 15 remaining bytes
+ */
+fbad:
+ SRAWCC $4, R9, R14
+ BLE f6
+ MOVW R14, CTR
+f5:
+ LSW (R11), $16, R16
+ ADD $16, R11
+ STSW R16, $16, (R10)
+ ADD $16, R10
+ BDNZ f5
+ RLWNMCC $0, R9, $15, R9 /* residue */
+ BEQ ret
+f6:
+ MOVW R9, XER
+ LSW (R11), R16
+ STSW R16, (R10)
+ BR ret
+
+/*
+ * whole thing repeated for backwards
+ */
+back:
+ CMP R9, $4
+ BLT bout
+
+ XOR R12,R13, R7
+ ANDCC $3,R7
+ BNE bout
+b1:
+ ANDCC $3,R13, R7
+ BEQ b2
+ MOVBZU -1(R13), R16
+ MOVBZU R16, -1(R12)
+ SUB $1, R9
+ BR b1
+b2:
+ SRAWCC $4, R9, R14
+ BLE b4
+ MOVW R14, CTR
+b3:
+ MOVWU -4(R13), R16
+ MOVWU -4(R13), R17
+ MOVWU -4(R13), R18
+ MOVWU -4(R13), R19
+ MOVWU R16, -4(R12)
+ MOVWU R17, -4(R12)
+ MOVWU R18, -4(R12)
+ MOVWU R19, -4(R12)
+ BDNZ b3
+ RLWNMCC $0, R9, $15, R9 /* residue */
+ BEQ ret
+b4:
+ SRAWCC $2, R9, R14
+ BLE bout
+ MOVW R14, CTR
+b5:
+ MOVWU -4(R13), R16
+ MOVWU R16, -4(R12)
+ BDNZ b5
+ RLWNMCC $0, R9, $3, R9 /* residue */
+ BEQ ret
+
+bout:
+ CMPU R13, R11
+ BLE ret
+ MOVBZU -1(R13), R16
+ MOVBZU R16, -1(R12)
+ BR bout
+
+trap:
+ MOVW $0, R0
+ MOVW 0(R0), R0
+
+ret:
+ MOVW s1+0(FP), R3
+ RETURN
diff --git a/sys/src/libc/power/memset.s b/sys/src/libc/power/memset.s
new file mode 100755
index 000000000..fa6e8d920
--- /dev/null
+++ b/sys/src/libc/power/memset.s
@@ -0,0 +1,73 @@
+ TEXT memset(SB),$0
+#define BDNZ BC 16,0,
+ MOVW R3, p+0(FP) /* R3 is pointer */
+
+/*
+ * performance:
+ * about 100mbytes/sec (8k blocks) on a 603/105 without L2 cache
+ * drops to 40mbytes/sec (10k blocks) and 28mbytes/sec with 32k blocks
+ */
+
+ MOVW n+8(FP), R4 /* R4 is count */
+ CMP R4, $0
+ BLE ret
+ MOVW c+4(FP), R5 /* R5 is char */
+
+/*
+ * create 16 copies of c in R5 .. R8
+ */
+ RLWNM $0, R5, $0xff, R5
+ RLWMI $8, R5, $0xff00, R5
+ RLWMI $16, R5, $0xffff0000, R5
+ MOVW R5, R6
+ MOVW R5, R7
+ MOVW R5, R8
+
+/*
+ * let STSW do the work for 16 characters or less; aligned and unaligned
+ */
+ CMP R4, $16
+ BLE out
+
+/*
+ * store enough bytes to align pointer
+ */
+ ANDCC $7,R3, R9
+ BEQ l2
+ SUBC R9, $8, R9
+ MOVW R9, XER
+ STSW R5, (R3)
+ ADD R9, R3
+ SUB R9, R4
+
+/*
+ * store 16 at a time while there's room
+ * STSW was used here originally, but it's `completion serialised'
+ */
+l2:
+ SRAWCC $4, R4, R9
+ BLE out
+ MOVW R9, CTR
+l3:
+ MOVW R5, 0(R3)
+ ADD $8, R3, R10
+ MOVW R6, 4(R3)
+ MOVW R7, 0(R10)
+ ADD $8, R10, R3
+ MOVW R8, 4(R10)
+ BDNZ l3
+ RLWNMCC $0, R4, $15, R4 /* residue */
+ BEQ ret
+
+/*
+ * store up to 16 bytes from R5 .. R8; aligned and unaligned
+ */
+
+out:
+ MOVW R4, XER
+ STSW R5, (R3)
+
+ret:
+ MOVW 0(FP), R3
+ RETURN
+ END
diff --git a/sys/src/libc/power/mkfile b/sys/src/libc/power/mkfile
new file mode 100755
index 000000000..c02f8a98d
--- /dev/null
+++ b/sys/src/libc/power/mkfile
@@ -0,0 +1,37 @@
+objtype=power
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ atom.s\
+ cycles.s\
+ getcallerpc.s\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memccpy.s\
+ memcmp.s\
+ memmove.s\
+ memset.s\
+ setjmp.s\
+ strcmp.s\
+ strncmp.s\
+ tas.s\
+ vlop.s
+
+CFILES=\
+ notejmp.c\
+ sqrt.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/power/notejmp.c b/sys/src/libc/power/notejmp.c
new file mode 100755
index 000000000..5394a4d75
--- /dev/null
+++ b/sys/src/libc/power/notejmp.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+
+int __noterestore(void);
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ /*
+ * song and dance to get around the kernel smashing r3 in noted
+ */
+ r->r4 = ret;
+ if(ret == 0)
+ r->r4 = 1;
+ r->r5 = j[JMPBUFPC] - JMPBUFDPC;
+ r->pc = (ulong)__noterestore;
+ r->sp = j[JMPBUFSP];
+ noted(NCONT);
+}
diff --git a/sys/src/libc/power/setjmp.s b/sys/src/libc/power/setjmp.s
new file mode 100755
index 000000000..f3f2f44ae
--- /dev/null
+++ b/sys/src/libc/power/setjmp.s
@@ -0,0 +1,26 @@
+TEXT setjmp(SB), 1, $-4
+ MOVW LR, R4
+ MOVW R1, (R3)
+ MOVW R4, 4(R3)
+ MOVW $0, R3
+ RETURN
+
+TEXT longjmp(SB), 1, $-4
+ MOVW R3, R4
+ MOVW r+4(FP), R3
+ CMP R3, $0
+ BNE ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVW $1, R3 /* bless their pointed heads */
+ok: MOVW (R4), R1
+ MOVW 4(R4), R4
+ MOVW R4, LR
+ BR (LR)
+
+/*
+ * trampoline functions because the kernel smashes r1
+ * in the uregs given to notejmp
+ */
+TEXT __noterestore(SB), 1, $-4
+ MOVW R4, R3
+ MOVW R5, LR
+ BR (LR)
diff --git a/sys/src/libc/power/sqrt.c b/sys/src/libc/power/sqrt.c
new file mode 100755
index 000000000..fa27c35ef
--- /dev/null
+++ b/sys/src/libc/power/sqrt.c
@@ -0,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+
+static long sqtab[64] =
+{
+ 0x6cdb2, 0x726d4, 0x77ea3, 0x7d52f, 0x82a85, 0x87eb1, 0x8d1c0, 0x923bd,
+ 0x974b2, 0x9c4a8, 0xa13a9, 0xa61be, 0xaaeee, 0xafb41, 0xb46bf, 0xb916e,
+ 0xbdb55, 0xc247a, 0xc6ce3, 0xcb495, 0xcfb95, 0xd41ea, 0xd8796, 0xdcca0,
+ 0xe110c, 0xe54dd, 0xe9818, 0xedac0, 0xf1cd9, 0xf5e67, 0xf9f6e, 0xfdfef,
+ 0x01fe0, 0x05ee6, 0x09cfd, 0x0da30, 0x11687, 0x1520c, 0x18cc8, 0x1c6c1,
+ 0x20000, 0x2388a, 0x27068, 0x2a79e, 0x2de32, 0x3142b, 0x3498c, 0x37e5b,
+ 0x3b29d, 0x3e655, 0x41989, 0x44c3b, 0x47e70, 0x4b02b, 0x4e16f, 0x51241,
+ 0x542a2, 0x57296, 0x5a220, 0x5d142, 0x60000, 0x62e5a, 0x65c55, 0x689f2,
+};
+
+double
+sqrt(double arg)
+{
+ int e, ms;
+ double a, t;
+ union
+ {
+ double d;
+ struct
+ {
+ long ms;
+ long ls;
+ };
+ } u;
+
+ u.d = arg;
+ ms = u.ms;
+
+ /*
+ * sign extend the mantissa with
+ * exponent. result should be > 0 for
+ * normal case.
+ */
+ e = ms >> 20;
+ if(e <= 0) {
+ if(e == 0)
+ return 0;
+ return NaN();
+ }
+
+ /*
+ * pick up arg/4 by adjusting exponent
+ */
+ u.ms = ms - (2 << 20);
+ a = u.d;
+
+ /*
+ * use 5 bits of mantissa and 1 bit
+ * of exponent to form table index.
+ * insert exponent/2 - 1.
+ */
+ e = (((e - 1023) >> 1) + 1022) << 20;
+ u.ms = *(long*)((char*)sqtab + ((ms >> 13) & 0xfc)) | e;
+ u.ls = 0;
+
+ /*
+ * three laps of newton
+ */
+ e = 1 << 20;
+ t = u.d;
+ u.d = t + a/t;
+ u.ms -= e; /* u.d /= 2; */
+ t = u.d;
+ u.d = t + a/t;
+ u.ms -= e; /* u.d /= 2; */
+ t = u.d;
+
+ return t + a/t;
+}
+
+/*
+ * this is the program that generated the table.
+ * it calls sqrt by some other means.
+ *
+ * void
+ * main(void)
+ * {
+ * int i;
+ * union U
+ * {
+ * double d;
+ * struct
+ * {
+ * long ms;
+ * long ls;
+ * };
+ * } u;
+ *
+ * for(i=0; i<64; i++) {
+ * u.ms = (i<<15) | 0x3fe04000;
+ * u.ls = 0;
+ * u.d = sqrt(u.d);
+ * print(" 0x%.5lux,", u.ms & 0xfffff);
+ * }
+ * print("\n");
+ * exits(0);
+ * }
+ */
diff --git a/sys/src/libc/power/strcmp.s b/sys/src/libc/power/strcmp.s
new file mode 100755
index 000000000..0aef5b29c
--- /dev/null
+++ b/sys/src/libc/power/strcmp.s
@@ -0,0 +1,21 @@
+TEXT strcmp(SB), $0
+
+ MOVW s2+4(FP), R4
+
+ SUB $1, R3
+ SUB $1, R4
+l1:
+ MOVBZU 1(R3), R5
+ MOVBZU 1(R4), R6
+ CMP R5, R6
+ BNE ne
+ CMP R5, $0
+ BNE l1
+ MOVW $0, R3
+ RETURN
+ne:
+ MOVW $1, R3
+ BGT ret
+ MOVW $-1, R3
+ret:
+ RETURN
diff --git a/sys/src/libc/power/strncmp.s b/sys/src/libc/power/strncmp.s
new file mode 100755
index 000000000..c55962faa
--- /dev/null
+++ b/sys/src/libc/power/strncmp.s
@@ -0,0 +1,29 @@
+TEXT strncmp(SB), $0
+#define BDNZ BC 16,0,
+
+ MOVW s2+4(FP), R4
+ MOVW n+8(FP), R7
+
+ CMP R7, $0
+ MOVW R7, CTR
+ BLE eq
+
+ SUB $1, R3
+ SUB $1, R4
+l1:
+ MOVBZU 1(R3), R5
+ MOVBZU 1(R4), R6
+ CMP R5, R6
+ BNE ne
+ CMP R5, $0
+ BEQ eq
+ BDNZ l1
+eq:
+ MOVW $0, R3
+ RETURN
+ne:
+ MOVW $1, R3
+ BGT ret
+ MOVW $-1, R3
+ret:
+ RETURN
diff --git a/sys/src/libc/power/tas.s b/sys/src/libc/power/tas.s
new file mode 100755
index 000000000..246b18056
--- /dev/null
+++ b/sys/src/libc/power/tas.s
@@ -0,0 +1,14 @@
+TEXT _tas(SB), 1, $-4
+ MOVW R3, R4
+ MOVW $0xdeaddead,R5
+tas1:
+/* DCBF (R4) fix for 603x bug */
+ SYNC
+ LWAR (R4), R3
+ CMP R3, $0
+ BNE tas0
+ DCBT (R4) /* fix 405 errata cpu_210 */
+ STWCCC R5, (R4)
+ BNE tas1
+tas0:
+ RETURN
diff --git a/sys/src/libc/power/vlop.s b/sys/src/libc/power/vlop.s
new file mode 100755
index 000000000..9085da247
--- /dev/null
+++ b/sys/src/libc/power/vlop.s
@@ -0,0 +1,132 @@
+#define BDNZ BC 16,0,
+
+/*
+ * 64/64 division adapted from powerpc compiler writer's handbook
+ *
+ * (R3:R4) = (R3:R4) / (R5:R6) (64b) = (64b / 64b)
+ * quo dvd dvs
+ *
+ * Remainder is left in R7:R8
+ *
+ * Code comment notation:
+ * msw = most-significant (high-order) word, i.e. bits 0..31
+ * lsw = least-significant (low-order) word, i.e. bits 32..63
+ * LZ = Leading Zeroes
+ * SD = Significant Digits
+ *
+ * R3:R4 = dvd (input dividend); quo (output quotient)
+ * R5:R6 = dvs (input divisor)
+ *
+ * R7:R8 = tmp; rem (output remainder)
+ */
+
+TEXT _divu64(SB), $0
+ MOVW a+0(FP), R3
+ MOVW a+4(FP), R4
+ MOVW b+8(FP), R5
+ MOVW b+12(FP), R6
+
+ /* count the number of leading 0s in the dividend */
+ CMP R3, $0 /* dvd.msw == 0? R3, */
+ CNTLZW R3, R11 /* R11 = dvd.msw.LZ */
+ CNTLZW R4, R9 /* R9 = dvd.lsw.LZ */
+ BNE lab1 /* if(dvd.msw != 0) dvd.LZ = dvd.msw.LZ */
+ ADD $32, R9, R11 /* dvd.LZ = dvd.lsw.LZ + 32 */
+
+lab1:
+ /* count the number of leading 0s in the divisor */
+ CMP R5, $0 /* dvd.msw == 0? */
+ CNTLZW R5, R9 /* R9 = dvs.msw.LZ */
+ CNTLZW R6, R10 /* R10 = dvs.lsw.LZ */
+ BNE lab2 /* if(dvs.msw != 0) dvs.LZ = dvs.msw.LZ */
+ ADD $32, R10, R9 /* dvs.LZ = dvs.lsw.LZ + 32 */
+
+lab2:
+ /* determine shift amounts to minimize the number of iterations */
+ CMP R11, R9 /* compare dvd.LZ to dvs.LZ */
+ SUBC R11, $64, R10 /* R10 = dvd.SD */
+ BGT lab9 /* if(dvs > dvd) quotient = 0 */
+ ADD $1, R9 /* ++dvs.LZ (or --dvs.SD) */
+ SUBC R9, $64, R9 /* R9 = dvs.SD */
+ ADD R9, R11 /* (dvd.LZ + dvs.SD) = left shift of dvd for */
+ /* initial dvd */
+ SUB R9, R10, R9 /* (dvd.SD - dvs.SD) = right shift of dvd for */
+ /* initial tmp */
+ MOVW R9, CTR /* number of iterations = dvd.SD - dvs.SD */
+
+ /* R7:R8 = R3:R4 >> R9 */
+ CMP R9, $32
+ ADD $-32, R9, R7
+ BLT lab3 /* if(R9 < 32) jump to lab3 */
+ SRW R7, R3, R8 /* tmp.lsw = dvd.msw >> (R9 - 32) */
+ MOVW $0, R7 /* tmp.msw = 0 */
+ BR lab4
+lab3:
+ SRW R9, R4, R8 /* R8 = dvd.lsw >> R9 */
+ SUBC R9, $32, R7
+ SLW R7, R3, R7 /* R7 = dvd.msw << 32 - R9 */
+ OR R7, R8 /* tmp.lsw = R8 | R7 */
+ SRW R9, R3, R7 /* tmp.msw = dvd.msw >> R9 */
+
+lab4:
+ /* R3:R4 = R3:R4 << R11 */
+ CMP R11,$32
+ ADDC $-32, R11, R9
+ BLT lab5 /* (R11 < 32)? */
+ SLW R9, R4, R3 /* dvd.msw = dvs.lsw << R9 */
+ MOVW $0, R4 /* dvd.lsw = 0 */
+ BR lab6
+
+lab5:
+ SLW R11, R3 /* R3 = dvd.msw << R11 */
+ SUBC R11, $32, R9
+ SRW R9, R4, R9 /* R9 = dvd.lsw >> 32 - R11 */
+ OR R9, R3 /* dvd.msw = R3 | R9 */
+ SLW R11, R4 /* dvd.lsw = dvd.lsw << R11 */
+
+lab6:
+ /* restoring division shift and subtract loop */
+ MOVW $-1, R10
+ ADDC $0, R7 /* clear carry bit before loop starts */
+lab7:
+ /* tmp:dvd is considered one large register */
+ /* each portion is shifted left 1 bit by adding it to itself */
+ /* adde sums the carry from the previous and creates a new carry */
+ ADDE R4,R4 /* shift dvd.lsw left 1 bit */
+ ADDE R3,R3 /* shift dvd.msw to left 1 bit */
+ ADDE R8,R8 /* shift tmp.lsw to left 1 bit */
+ ADDE R7,R7 /* shift tmp.msw to left 1 bit */
+ SUBC R6, R8, R11 /* tmp.lsw - dvs.lsw */
+ SUBECC R5, R7, R9 /* tmp.msw - dvs.msw */
+ BLT lab8 /* if(result < 0) clear carry bit */
+ MOVW R11, R8 /* move lsw */
+ MOVW R9, R7 /* move msw */
+ ADDC $1, R10, R11 /* set carry bit */
+lab8:
+ BDNZ lab7
+
+ ADDE R4,R4 /* quo.lsw (lsb = CA) */
+ ADDE R3,R3 /* quo.msw (lsb from lsw) */
+
+lab10:
+ MOVW qp+16(FP), R9
+ MOVW rp+20(FP), R10
+ CMP R9, $0
+ BEQ lab11
+ MOVW R3, 0(R9)
+ MOVW R4, 4(R9)
+lab11:
+ CMP R10, $0
+ BEQ lab12
+ MOVW R7, 0(R10)
+ MOVW R8, 4(R10)
+lab12:
+ RETURN
+
+lab9:
+ /* Quotient is 0 (dvs > dvd) */
+ MOVW R4, R8 /* rmd.lsw = dvd.lsw */
+ MOVW R3, R7 /* rmd.msw = dvd.msw */
+ MOVW $0, R4 /* dvd.lsw = 0 */
+ MOVW $0, R3 /* dvd.msw = 0 */
+ BR lab10
diff --git a/sys/src/libc/power/vlrt.c b/sys/src/libc/power/vlrt.c
new file mode 100755
index 000000000..681a3b49b
--- /dev/null
+++ b/sys/src/libc/power/vlrt.c
@@ -0,0 +1,254 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ ulong hi;
+ ulong lo;
+};
+
+void abort(void);
+void _divu64(Vlong, Vlong, Vlong*, Vlong*);
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ _divu64(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ _divu64(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ _divu64(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ _divu64(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u = *ret;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
diff --git a/sys/src/libc/sparc/argv0.s b/sys/src/libc/sparc/argv0.s
new file mode 100755
index 000000000..8d9f9b29b
--- /dev/null
+++ b/sys/src/libc/sparc/argv0.s
@@ -0,0 +1,4 @@
+GLOBL argv0(SB), $4
+GLOBL _tos(SB), $4
+GLOBL _privates(SB), $4
+GLOBL _nprivates(SB), $4
diff --git a/sys/src/libc/sparc/getcallerpc.s b/sys/src/libc/sparc/getcallerpc.s
new file mode 100755
index 000000000..05d70f118
--- /dev/null
+++ b/sys/src/libc/sparc/getcallerpc.s
@@ -0,0 +1,3 @@
+TEXT getcallerpc(SB), $0
+ MOVW 0(R1), R7
+ RETURN
diff --git a/sys/src/libc/sparc/getfcr.s b/sys/src/libc/sparc/getfcr.s
new file mode 100755
index 000000000..0059ada56
--- /dev/null
+++ b/sys/src/libc/sparc/getfcr.s
@@ -0,0 +1,27 @@
+TEXT getfsr(SB), $0
+ SUB $4, R1
+ MOVW FSR, (R1)
+ MOVW (R1), R7
+ ADD $4, R1
+ RETURN
+
+TEXT setfsr(SB), $0
+ SUB $4, R1
+ MOVW R7, (R1)
+ MOVW (R1), FSR
+ ADD $4, R1
+ RETURN
+
+TEXT setfcr(SB), $0
+ SUB $4, R1
+ MOVW R7, (R1)
+ MOVW (R1), FSR
+ ADD $4, R1
+ RETURN
+
+TEXT getfcr(SB), $0
+ SUB $4, R1
+ MOVW FSR, (R1)
+ MOVW (R1), R7
+ ADD $4, R1
+ RETURN
diff --git a/sys/src/libc/sparc/main9.s b/sys/src/libc/sparc/main9.s
new file mode 100755
index 000000000..e5f459638
--- /dev/null
+++ b/sys/src/libc/sparc/main9.s
@@ -0,0 +1,31 @@
+#define NPRIVATES 16
+
+TEXT _main(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVW $setSB(SB), R2
+ MOVW R7, _tos(SB)
+
+ MOVW $p-64(SP),R7
+ MOVW R7,_privates+0(SB)
+ MOVW $16,R7
+ MOVW R7,_nprivates+0(SB)
+/*
+ MOVW _fpsr+0(SB), FSR
+ FMOVD $0.5, F26
+ FSUBD F26, F26, F24
+ FADDD F26, F26, F28
+ FADDD F28, F28, F30
+*/
+ MOVW inargc-4(FP), R7
+ MOVW $inargv+0(FP), R8
+ MOVW R8, 8(R1)
+ JMPL main(SB)
+
+loop:
+ MOVW $_exits<>(SB), R7
+ JMPL exits(SB)
+ MOVW $_mul(SB), R8 /* force loading of muldiv */
+ JMP loop
+
+DATA _exits<>+0(SB)/5, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/sparc/main9p.s b/sys/src/libc/sparc/main9p.s
new file mode 100755
index 000000000..db464f485
--- /dev/null
+++ b/sys/src/libc/sparc/main9p.s
@@ -0,0 +1,44 @@
+#define NPRIVATES 16
+
+TEXT _mainp(SB), 1, $(16 + NPRIVATES*4)
+
+ MOVW $setSB(SB), R2
+ MOVW R7, _tos(SB)
+
+ MOVW $p-64(SP),R7
+ MOVW R7,_privates+0(SB)
+ MOVW $16,R7
+ MOVW R7,_nprivates+0(SB)
+/*
+ MOVW _fpsr+0(SB), FSR
+ FMOVD $0.5, F26
+ FSUBD F26, F26, F24
+ FADDD F26, F26, F28
+ FADDD F28, F28, F30
+*/
+
+ JMPL _profmain(SB)
+ MOVW __prof+4(SB), R7
+ MOVW R7, __prof+0(SB)
+
+ MOVW inargc-4(FP), R7
+ MOVW $inargv+0(FP), R8
+ MOVW R8, 8(R1)
+ JMPL main(SB)
+
+loop:
+ MOVW $_exits<>(SB), R7
+ JMPL exits(SB)
+ MOVW $_mul(SB), R8 /* force loading of muldiv */
+ MOVW $_profin(SB), R9 /* force loading of profile */
+ JMP loop
+
+TEXT _savearg(SB), 1, $0
+ RETURN
+
+TEXT _callpc(SB), 1, $0
+ MOVW argp-4(FP), R7
+ RETURN
+
+DATA _exits<>+0(SB)/4, $"main"
+GLOBL _exits<>+0(SB), $5
diff --git a/sys/src/libc/sparc/memccpy.s b/sys/src/libc/sparc/memccpy.s
new file mode 100755
index 000000000..a2ebd7d11
--- /dev/null
+++ b/sys/src/libc/sparc/memccpy.s
@@ -0,0 +1,27 @@
+ TEXT memccpy(SB), $0
+
+MOVW R7, 0(FP)
+ MOVW n+12(FP), R7
+ SUBCC R0,R7, R0
+ BE ret
+ MOVW s1+0(FP), R9
+ MOVW s2+4(FP), R8
+ MOVBU c+11(FP), R10
+ ADD R7,R8, R11
+
+l1: MOVBU (R8), R12
+ ADD $1, R8
+ MOVBU R12, (R9)
+ ADD $1, R9
+ SUBCC R10,R12, R0
+ BE eq
+ SUBCC R8,R11, R0
+ BNE l1
+ MOVW R0, R7
+ RETURN
+
+eq:
+ MOVW R9, R7
+
+ret:
+ RETURN
diff --git a/sys/src/libc/sparc/memchr.s b/sys/src/libc/sparc/memchr.s
new file mode 100755
index 000000000..81e67f6f2
--- /dev/null
+++ b/sys/src/libc/sparc/memchr.s
@@ -0,0 +1,26 @@
+ TEXT memchr(SB), $0
+
+MOVW R7, 0(FP)
+ MOVW n+8(FP), R7
+ SUBCC R0,R7, R0
+ BE ret
+ MOVW s1+0(FP), R8
+ MOVBU c+7(FP), R9
+ ADD R7,R8, R11
+
+l1:
+ MOVBU (R8), R10
+ SUBCC R9,R10, R0
+ ADD $1, R8
+ BE eq
+ SUBCC R8,R11, R0
+ BNE l1
+
+ MOVW R0, R7
+ RETURN
+
+eq:
+ SUB $1,R8, R7
+
+ret:
+ RETURN
diff --git a/sys/src/libc/sparc/memcmp.s b/sys/src/libc/sparc/memcmp.s
new file mode 100755
index 000000000..f5d680a40
--- /dev/null
+++ b/sys/src/libc/sparc/memcmp.s
@@ -0,0 +1,120 @@
+ TEXT memcmp(SB), $0
+
+/*
+ * performance:
+ * (tba)
+ */
+
+ MOVW R7, 0(FP)
+ MOVW n+8(FP), R9 /* R9 is count */
+ MOVW s1+0(FP), R10 /* R10 is pointer1 */
+ MOVW s2+4(FP), R11 /* R11 is pointer2 */
+ ADD R9,R10, R12 /* R12 is end pointer1 */
+
+/*
+ * if not at least 4 chars,
+ * dont even mess around.
+ * 3 chars to guarantee any
+ * rounding up to a word
+ * boundary and 4 characters
+ * to get at least maybe one
+ * full word cmp.
+ */
+ SUBCC $4,R9, R0
+ BL out
+
+/*
+ * test if both pointers
+ * are similarly word alligned
+ */
+ XOR R10,R11, R7
+ ANDCC $3,R7, R0
+ BNE out
+
+/*
+ * byte at a time to word allign
+ */
+l1:
+ ANDCC $3,R10, R0
+ BE l2
+ MOVBU 0(R10), R16
+ MOVBU 0(R11), R17
+ ADD $1, R10
+ SUBCC R16,R17, R0
+ BNE ne
+ ADD $1, R11
+ JMP l1
+
+/*
+ * turn R9 into end pointer1-15
+ * cmp 16 at a time while theres room
+ */
+l2:
+ SUB $15,R12, R9
+l3:
+ SUBCC R10,R9, R0
+ BLEU l4
+ MOVW 0(R10), R16
+ MOVW 0(R11), R17
+ MOVW 4(R10), R18
+ SUBCC R16,R17, R0
+ BNE ne
+ MOVW 4(R11), R19
+ MOVW 8(R10), R16
+ SUBCC R18,R19, R0
+ BNE ne
+ MOVW 8(R11), R17
+ MOVW 12(R10), R18
+ SUBCC R16,R17, R0
+ BNE ne
+ MOVW 12(R11), R19
+ ADD $16, R10
+ SUBCC R18,R19, R0
+ BNE ne
+ SUBCC R16,R17, R0
+ BNE ne
+ ADD $16, R11
+ JMP l3
+
+/*
+ * turn R9 into end pointer1-3
+ * cmp 4 at a time while theres room
+ */
+l4:
+ SUB $3,R12, R9
+l5:
+ SUBCC R10,R9, R0
+ BLEU out
+ MOVW 0(R10), R16
+ MOVW 0(R11), R17
+ ADD $4, R10
+ SUBCC R16,R17, R0 /* only works because big endian */
+ BNE ne
+ ADD $4, R11
+ JMP l5
+
+/*
+ * last loop, cmp byte at a time
+ */
+out:
+ SUBCC R10,R12, R0
+ BE zero
+ MOVBU 0(R10), R16
+ MOVBU 0(R11), R17
+ ADD $1, R10
+ SUBCC R16,R17, R0
+ BNE ne
+ ADD $1, R11
+ JMP out
+
+ne:
+ BGU plus
+ MOVW $1, R7
+ RETURN
+plus:
+ MOVW $-1, R7
+ RETURN
+
+zero:
+ MOVW R0, R7
+ RETURN
diff --git a/sys/src/libc/sparc/memmove.s b/sys/src/libc/sparc/memmove.s
new file mode 100755
index 000000000..1295209ac
--- /dev/null
+++ b/sys/src/libc/sparc/memmove.s
@@ -0,0 +1,162 @@
+ TEXT memmove(SB), $0
+ JMP move
+
+ TEXT memcpy(SB), $0
+move:
+
+/*
+ * performance:
+ * (tba)
+ */
+
+ MOVW R7, s1+0(FP)
+ MOVW n+8(FP), R9 /* R9 is count */
+ MOVW R7, R10 /* R10 is to-pointer */
+ SUBCC R0,R9, R0
+ BGE ok
+ MOVW 0(R0), R0
+
+ok:
+ MOVW s2+4(FP), R11 /* R11 is from-pointer */
+ ADD R9,R11, R13 /* R13 is end from-pointer */
+ ADD R9,R10, R12 /* R12 is end to-pointer */
+
+/*
+ * easiest test is copy backwards if
+ * destination string has higher mem address
+ */
+ SUBCC R11,R10, R0
+ BGU back
+
+/*
+ * if not at least 8 chars,
+ * dont even mess around.
+ * 7 chars to guarantee any
+ * rounding up to a word
+ * boundary and 8 characters
+ * to get at least maybe one
+ * full word store.
+ */
+ SUBCC $8,R9, R0
+ BL fout
+
+/*
+ * test if both pointers
+ * are similarly word aligned
+ */
+ XOR R10,R11, R7
+ ANDCC $7,R7, R0
+ BNE fout
+
+/*
+ * byte at a time to double align
+ */
+f1:
+ ANDCC $7,R10, R0
+ BE f2
+ MOVB 0(R11), R16
+ ADD $1, R11
+ MOVB R16, 0(R10)
+ ADD $1, R10
+ JMP f1
+
+/*
+ * turn R9 into to-end pointer-15
+ * copy 16 at a time while theres room.
+ * R12 is smaller than R13 --
+ * there are problems if R13 is 0.
+ */
+f2:
+ SUB $15,R12, R9
+f3:
+ SUBCC R10,R9, R0
+ BLEU f4
+ MOVD 0(R11), R16
+ MOVD R16, 0(R10)
+ MOVD 8(R11), R16
+ ADD $16, R11
+ MOVD R16, 8(R10)
+ ADD $16, R10
+ JMP f3
+
+/*
+ * turn R9 into to-end pointer-3
+ * copy 4 at a time while theres room
+ */
+f4:
+ SUB $3,R12, R9
+f5:
+ SUBCC R10,R9, R0
+ BLEU fout
+ MOVW 0(R11), R16
+ ADD $4, R11
+ MOVW R16, 0(R10)
+ ADD $4, R10
+ JMP f5
+
+/*
+ * last loop, copy byte at a time
+ */
+fout:
+ SUBCC R11,R13, R0
+ BLEU ret
+ MOVB 0(R11), R16
+ ADD $1, R11
+ MOVB R16, 0(R10)
+ ADD $1, R10
+ JMP fout
+
+/*
+ * whole thing repeated for backwards
+ */
+back:
+ SUBCC $8,R9, R0
+ BL bout
+
+ XOR R12,R13, R7
+ ANDCC $7,R7, R0
+ BNE bout
+b1:
+ ANDCC $7,R13, R0
+ BE b2
+ MOVB -1(R13), R16
+ SUB $1, R13
+ MOVB R16, -1(R12)
+ SUB $1, R12
+ JMP b1
+b2:
+ ADD $15,R11, R9
+b3:
+ SUBCC R9,R13, R0
+ BLEU b4
+
+ MOVD -8(R13), R16
+ MOVD R16, -8(R12)
+ MOVD -16(R13), R16
+ SUB $16, R13
+ MOVD R16, -16(R12);
+ SUB $16, R12
+ JMP b3
+b4:
+ ADD $3,R11, R9
+b5:
+ SUBCC R9,R13, R0
+ BLEU bout
+ MOVW -4(R13), R16
+ SUB $4, R13
+ MOVW R16, -4(R12)
+ SUB $4, R12
+ JMP b5
+
+bout:
+ SUBCC R11,R13, R0
+ BLEU ret
+ MOVB -1(R13), R16
+ SUB $1, R13
+ MOVB R16, -1(R12)
+ SUB $1, R12
+ JMP bout
+
+ret:
+ MOVW s1+0(FP), R7
+ RETURN
diff --git a/sys/src/libc/sparc/memset.s b/sys/src/libc/sparc/memset.s
new file mode 100755
index 000000000..8c7b26c86
--- /dev/null
+++ b/sys/src/libc/sparc/memset.s
@@ -0,0 +1,88 @@
+ TEXT memset(SB),$0
+
+/*
+ * performance:
+ * (tba)
+ */
+
+MOVW R7, 0(FP)
+ MOVW n+8(FP), R9 /* R9 is count */
+ MOVW p+0(FP), R10 /* R10 is pointer */
+ MOVW c+4(FP), R11 /* R11 is char */
+ ADD R9,R10, R12 /* R12 is end pointer */
+
+/*
+ * if not at least 4 chars,
+ * dont even mess around.
+ * 3 chars to guarantee any
+ * rounding up to a word
+ * boundary and 4 characters
+ * to get at least maybe one
+ * full word store.
+ */
+ SUBCC $4,R9, R0
+ BL out
+
+/*
+ * turn R11 into a word of characters
+ */
+ AND $0xff, R11
+ SLL $8,R11, R7
+ OR R7, R11
+ SLL $16,R11, R7
+ OR R7, R11
+
+/*
+ * store one byte at a time until pointer
+ * is alligned on a word boundary
+ */
+l1:
+ ANDCC $3,R10, R0
+ BE l2
+ MOVB R11, 0(R10)
+ ADD $1, R10
+ JMP l1
+
+/*
+ * turn R9 into end pointer-15
+ * store 16 at a time while theres room
+ */
+l2:
+ ADD $-15,R12, R9
+ SUBCC R10,R9, R0
+ BLEU l4
+l3:
+ MOVW R11, 0(R10)
+ MOVW R11, 4(R10)
+ ADD $16, R10
+ SUBCC R10,R9, R0
+ MOVW R11, -8(R10)
+ MOVW R11, -4(R10)
+ BGU l3
+
+/*
+ * turn R9 into end pointer-3
+ * store 4 at a time while theres room
+ */
+l4:
+ ADD $-3,R12, R9
+l5:
+ SUBCC R10,R9, R0
+ BLEU out
+ MOVW R11, 0(R10)
+ ADD $4, R10
+ JMP l5
+
+/*
+ * last loop, store byte at a time
+ */
+out:
+ SUBCC R10,R12, R0
+ BLEU ret
+ MOVB R11, 0(R10)
+ ADD $1, R10
+ JMP out
+
+ret:
+ MOVW s1+0(FP), R7
+ RETURN
diff --git a/sys/src/libc/sparc/mkfile b/sys/src/libc/sparc/mkfile
new file mode 100755
index 000000000..af2b18944
--- /dev/null
+++ b/sys/src/libc/sparc/mkfile
@@ -0,0 +1,39 @@
+objtype=sparc
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libc.a
+SFILES=\
+ argv0.s\
+ getcallerpc.$O\
+ getfcr.s\
+ main9.s\
+ main9p.s\
+ memccpy.s\
+ memchr.s\
+ memcmp.s\
+ memmove.s\
+ memset.s\
+ muldivrt.s\
+ setjmp.s\
+ strchr.s\
+ strcmp.s\
+ strcpy.s\
+ tas.s\
+ vlop.s
+
+CFILES=\
+ cycles.c\
+ notejmp.c\
+ sqrt.c\
+ vlrt.c\
+
+HFILES=/sys/include/libc.h
+
+OFILES=${CFILES:%.c=%.$O} ${SFILES:%.s=%.$O}
+
+UPDATE=mkfile\
+ $HFILES\
+ $CFILES\
+ $SFILES\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/libc/sparc/muldivrt.s b/sys/src/libc/sparc/muldivrt.s
new file mode 100755
index 000000000..ac811629f
--- /dev/null
+++ b/sys/src/libc/sparc/muldivrt.s
@@ -0,0 +1,310 @@
+/*
+ * ulong
+ * _udiv(ulong num, ulong den)
+ * {
+ * int i;
+ * ulong quo;
+ *
+ * if(den == 0)
+ * *(ulong*)-1 = 0;
+ * quo = num;
+ * if(quo > 1<<(32-1))
+ * quo = 1<<(32-1);
+ * for(i=0; den<quo; i++)
+ * den <<= 1;
+ * quo = 0;
+ * for(; i>=0; i--) {
+ * quo <<= 1;
+ * if(num >= den) {
+ * num -= den;
+ * quo |= 1;
+ * }
+ * den >>= 1;
+ * }
+ * return quo::num;
+ * }
+ */
+
+#define NOPROF 1
+
+/*
+ * calling sequence:
+ * num: 4(R1)
+ * den: 8(R1)
+ * returns
+ * quo: 4(R1)
+ * rem: 8(R1)
+ */
+TEXT _udivmod(SB), NOPROF, $-4
+
+ MOVW $(1<<31), R11
+ MOVW 4(R1), R13 /* numerator */
+ MOVW 8(R1), R10 /* denominator */
+ CMP R10, R0
+ BNE udm20
+ MOVW R0, -1(R0) /* fault -- divide by zero */
+udm20:
+ MOVW R13, R12
+ CMP R13, R11
+ BLEU udm34
+ MOVW R11, R12
+udm34:
+ MOVW R0, R11
+udm38:
+ CMP R10, R12
+ BCC udm54
+ SLL $1, R10
+ ADD $1, R11
+ BA udm38
+udm54:
+ MOVW R0, R12
+udm58:
+ CMP R11, R0
+ BL udm8c
+ SLL $1, R12
+ CMP R13, R10
+ BCS udm7c
+ SUB R10, R13
+ OR $1, R12
+udm7c:
+ SRL $1, R10
+ SUB $1, R11
+ BA udm58
+udm8c:
+ MOVW R12, 4(R1) /* quotent */
+ MOVW R13, 8(R1) /* remainder */
+ JMPL 8(R15)
+
+/*
+ * save working registers
+ * and bring in num/den parameters
+ */
+TEXT _unsarg(SB), NOPROF, $-4
+ MOVW R10, 12(R1)
+ MOVW R11, 16(R1)
+ MOVW R12, 20(R1)
+ MOVW R13, 24(R1)
+
+ MOVW R14, 4(R1)
+ MOVW 32(R1), R14
+ MOVW R14, 8(R1)
+
+ JMPL 8(R15)
+
+/*
+ * save working registers
+ * and bring in absolute value
+ * of num/den parameters
+ */
+TEXT _absarg(SB), NOPROF, $-4
+ MOVW R10, 12(R1)
+ MOVW R11, 16(R1)
+ MOVW R12, 20(R1)
+ MOVW R13, 24(R1)
+
+ MOVW R14, 28(R1)
+ CMP R14, R0
+ BGE ab1
+ SUB R14, R0, R14
+ab1:
+ MOVW R14, 4(R1) /* numerator */
+
+ MOVW 32(R1), R14
+ CMP R14, R0
+ BGE ab2
+ SUB R14, R0, R14
+ab2:
+ MOVW R14, 8(R1) /* denominator */
+ JMPL 8(R15)
+
+/*
+ * restore registers and
+ * return to original caller
+ * answer is in R14
+ */
+TEXT _retarg(SB), NOPROF, $-4
+ MOVW 12(R1), R10
+ MOVW 16(R1), R11
+ MOVW 20(R1), R12
+ MOVW 24(R1), R13
+ MOVW 0(R1), R15
+
+ ADD $28, R1
+ JMP 8(R15) /* back to main sequence */
+
+/*
+ * calling sequence
+ * num: R14
+ * den: 8(R1)
+ * returns
+ * quo: R14
+ */
+TEXT _div(SB), NOPROF, $-4
+ SUB $28, R1 /* 4 reg save, 2 parameters, link */
+ MOVW R15, 0(R1)
+
+ JMPL _absarg(SB)
+ JMPL _udivmod(SB)
+ MOVW 4(R1), R14
+
+ MOVW 28(R1), R10 /* clean up the sign */
+ MOVW 32(R1), R11
+ XORCC R11, R10, R0
+ BGE div1
+ SUB R14, R0, R14
+div1:
+
+ JMPL _retarg(SB)
+ JMP 8(R15) /* not executed */
+
+/*
+ * calling sequence
+ * num: R14
+ * den: 8(R1)
+ * returns
+ * quo: R14
+ */
+TEXT _divl(SB), NOPROF, $-4
+ SUB $((4+2+1)*4), R1 /* 4 reg save, 2 parameters, link */
+ MOVW R15, 0(R1)
+
+ JMPL _unsarg(SB)
+ JMPL _udivmod(SB)
+ MOVW 4(R1), R14
+
+ JMPL _retarg(SB)
+ JMP 8(R15) /* not executed */
+
+/*
+ * calling sequence
+ * num: R14
+ * den: 8(R1)
+ * returns
+ * rem: R14
+ */
+TEXT _mod(SB), NOPROF, $-4
+ SUB $28, R1 /* 4 reg save, 2 parameters, link */
+
+ MOVW R15, 0(R1)
+ JMPL _absarg(SB)
+ JMPL _udivmod(SB)
+ MOVW 8(R1), R14
+
+ MOVW 28(R1), R10 /* clean up the sign */
+ CMP R10, R0
+ BGE mod1
+ SUB R14, R0, R14
+mod1:
+
+ JMPL _retarg(SB)
+ JMP 8(R15) /* not executed */
+
+/*
+ * calling sequence
+ * num: R14
+ * den: 8(R1)
+ * returns
+ * rem: R14
+ */
+TEXT _modl(SB), NOPROF, $-4
+ SUB $28, R1 /* 4 reg save, 2 parameters, link */
+
+
+ MOVW R15, 0(R1)
+ JMPL _unsarg(SB)
+ JMPL _udivmod(SB)
+ MOVW 8(R1), R14
+
+ JMPL _retarg(SB)
+ JMP 8(R15) /* not executed */
+
+/*
+ * special calling sequence:
+ * arg1 in R14
+ * arg2 in 4(R1), will save R9
+ * nothing in 0(R1), will save R8
+ * result in R14
+ */
+TEXT _mul+0(SB), NOPROF, $-4
+
+ /*
+ * exchange stack and registers
+ */
+ MOVW R8, 0(R1)
+ MOVW 4(R1), R8
+ MOVW R9, 4(R1)
+
+ CMP R14, R8
+ BLE mul1
+ MOVW R14, R9
+ MOVW R8, R14
+ MOVW R9, R8
+mul1:
+ MOVW R14, Y
+ ANDNCC $0xFFF, R14, R0
+ BE mul_shortway
+ ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */
+
+ /* long multiply */
+ MULSCC R8, R9, R9 /* 0 */
+ MULSCC R8, R9, R9 /* 1 */
+ MULSCC R8, R9, R9 /* 2 */
+ MULSCC R8, R9, R9 /* 3 */
+ MULSCC R8, R9, R9 /* 4 */
+ MULSCC R8, R9, R9 /* 5 */
+ MULSCC R8, R9, R9 /* 6 */
+ MULSCC R8, R9, R9 /* 7 */
+ MULSCC R8, R9, R9 /* 8 */
+ MULSCC R8, R9, R9 /* 9 */
+ MULSCC R8, R9, R9 /* 10 */
+ MULSCC R8, R9, R9 /* 11 */
+ MULSCC R8, R9, R9 /* 12 */
+ MULSCC R8, R9, R9 /* 13 */
+ MULSCC R8, R9, R9 /* 14 */
+ MULSCC R8, R9, R9 /* 15 */
+ MULSCC R8, R9, R9 /* 16 */
+ MULSCC R8, R9, R9 /* 17 */
+ MULSCC R8, R9, R9 /* 18 */
+ MULSCC R8, R9, R9 /* 19 */
+ MULSCC R8, R9, R9 /* 20 */
+ MULSCC R8, R9, R9 /* 21 */
+ MULSCC R8, R9, R9 /* 22 */
+ MULSCC R8, R9, R9 /* 23 */
+ MULSCC R8, R9, R9 /* 24 */
+ MULSCC R8, R9, R9 /* 25 */
+ MULSCC R8, R9, R9 /* 26 */
+ MULSCC R8, R9, R9 /* 27 */
+ MULSCC R8, R9, R9 /* 28 */
+ MULSCC R8, R9, R9 /* 29 */
+ MULSCC R8, R9, R9 /* 30 */
+ MULSCC R8, R9, R9 /* 31 */
+ MULSCC R0, R9, R9 /* 32; shift only */
+
+ MOVW Y, R14 /* get low part */
+ BA mul_return
+
+mul_shortway:
+ ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */
+ MULSCC R8, R9, R9 /* 0 */
+ MULSCC R8, R9, R9 /* 1 */
+ MULSCC R8, R9, R9 /* 2 */
+ MULSCC R8, R9, R9 /* 3 */
+ MULSCC R8, R9, R9 /* 4 */
+ MULSCC R8, R9, R9 /* 5 */
+ MULSCC R8, R9, R9 /* 6 */
+ MULSCC R8, R9, R9 /* 7 */
+ MULSCC R8, R9, R9 /* 8 */
+ MULSCC R8, R9, R9 /* 9 */
+ MULSCC R8, R9, R9 /* 10 */
+ MULSCC R8, R9, R9 /* 11 */
+ MULSCC R0, R9, R9 /* 12; shift only */
+
+ MOVW Y, R8
+ SLL $12, R9
+ SRL $20, R8
+ OR R8, R9, R14
+
+mul_return:
+ MOVW 0(R1), R8
+ MOVW 4(R1), R9
+ JMP 8(R15)
diff --git a/sys/src/libc/sparc/notejmp.c b/sys/src/libc/sparc/notejmp.c
new file mode 100755
index 000000000..56caf98e6
--- /dev/null
+++ b/sys/src/libc/sparc/notejmp.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <ureg.h>
+
+int __noterestore(void);
+
+void
+notejmp(void *vr, jmp_buf j, int ret)
+{
+ struct Ureg *r = vr;
+
+ /*
+ * song and dance to get around the kernel smashing r7 in noted
+ */
+ r->r8 = ret;
+ if(ret == 0)
+ r->r8 = 1;
+ r->r9 = j[JMPBUFPC] - JMPBUFDPC;
+ r->pc = (ulong)__noterestore;
+ r->npc = (ulong)__noterestore + 4;
+ r->sp = j[JMPBUFSP];
+ noted(NCONT);
+}
diff --git a/sys/src/libc/sparc/setjmp.s b/sys/src/libc/sparc/setjmp.s
new file mode 100755
index 000000000..da8e7e63b
--- /dev/null
+++ b/sys/src/libc/sparc/setjmp.s
@@ -0,0 +1,25 @@
+TEXT setjmp(SB), 1, $0
+
+ MOVW R1, (R7)
+ MOVW R15, 4(R7)
+ MOVW $0, R7
+ RETURN
+
+TEXT longjmp(SB), 1, $0
+
+ MOVW R7, R8
+ MOVW r+4(FP), R7
+ CMP R7, R0
+ BNE ok /* ansi: "longjmp(0) => longjmp(1)" */
+ MOVW $1, R7 /* bless their pointed heads */
+ok: MOVW (R8), R1
+ MOVW 4(R8), R15
+ RETURN
+
+/*
+ * trampoline functions because the kernel smashes r7
+ * in the uregs given to notejmp
+ */
+TEXT __noterestore(SB), 1, $-4
+ MOVW R8, R7
+ JMP (R9)
diff --git a/sys/src/libc/sparc/sqrt.c b/sys/src/libc/sparc/sqrt.c
new file mode 100755
index 000000000..fa27c35ef
--- /dev/null
+++ b/sys/src/libc/sparc/sqrt.c
@@ -0,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+
+static long sqtab[64] =
+{
+ 0x6cdb2, 0x726d4, 0x77ea3, 0x7d52f, 0x82a85, 0x87eb1, 0x8d1c0, 0x923bd,
+ 0x974b2, 0x9c4a8, 0xa13a9, 0xa61be, 0xaaeee, 0xafb41, 0xb46bf, 0xb916e,
+ 0xbdb55, 0xc247a, 0xc6ce3, 0xcb495, 0xcfb95, 0xd41ea, 0xd8796, 0xdcca0,
+ 0xe110c, 0xe54dd, 0xe9818, 0xedac0, 0xf1cd9, 0xf5e67, 0xf9f6e, 0xfdfef,
+ 0x01fe0, 0x05ee6, 0x09cfd, 0x0da30, 0x11687, 0x1520c, 0x18cc8, 0x1c6c1,
+ 0x20000, 0x2388a, 0x27068, 0x2a79e, 0x2de32, 0x3142b, 0x3498c, 0x37e5b,
+ 0x3b29d, 0x3e655, 0x41989, 0x44c3b, 0x47e70, 0x4b02b, 0x4e16f, 0x51241,
+ 0x542a2, 0x57296, 0x5a220, 0x5d142, 0x60000, 0x62e5a, 0x65c55, 0x689f2,
+};
+
+double
+sqrt(double arg)
+{
+ int e, ms;
+ double a, t;
+ union
+ {
+ double d;
+ struct
+ {
+ long ms;
+ long ls;
+ };
+ } u;
+
+ u.d = arg;
+ ms = u.ms;
+
+ /*
+ * sign extend the mantissa with
+ * exponent. result should be > 0 for
+ * normal case.
+ */
+ e = ms >> 20;
+ if(e <= 0) {
+ if(e == 0)
+ return 0;
+ return NaN();
+ }
+
+ /*
+ * pick up arg/4 by adjusting exponent
+ */
+ u.ms = ms - (2 << 20);
+ a = u.d;
+
+ /*
+ * use 5 bits of mantissa and 1 bit
+ * of exponent to form table index.
+ * insert exponent/2 - 1.
+ */
+ e = (((e - 1023) >> 1) + 1022) << 20;
+ u.ms = *(long*)((char*)sqtab + ((ms >> 13) & 0xfc)) | e;
+ u.ls = 0;
+
+ /*
+ * three laps of newton
+ */
+ e = 1 << 20;
+ t = u.d;
+ u.d = t + a/t;
+ u.ms -= e; /* u.d /= 2; */
+ t = u.d;
+ u.d = t + a/t;
+ u.ms -= e; /* u.d /= 2; */
+ t = u.d;
+
+ return t + a/t;
+}
+
+/*
+ * this is the program that generated the table.
+ * it calls sqrt by some other means.
+ *
+ * void
+ * main(void)
+ * {
+ * int i;
+ * union U
+ * {
+ * double d;
+ * struct
+ * {
+ * long ms;
+ * long ls;
+ * };
+ * } u;
+ *
+ * for(i=0; i<64; i++) {
+ * u.ms = (i<<15) | 0x3fe04000;
+ * u.ls = 0;
+ * u.d = sqrt(u.d);
+ * print(" 0x%.5lux,", u.ms & 0xfffff);
+ * }
+ * print("\n");
+ * exits(0);
+ * }
+ */
diff --git a/sys/src/libc/sparc/strchr.s b/sys/src/libc/sparc/strchr.s
new file mode 100755
index 000000000..192beab13
--- /dev/null
+++ b/sys/src/libc/sparc/strchr.s
@@ -0,0 +1,73 @@
+ TEXT strchr(SB), $0
+
+MOVW R7, 0(FP)
+ MOVB c+7(FP), R10
+ MOVW s+0(FP), R9
+
+ SUBCC R0,R10, R0
+ BE l2
+
+/*
+ * char is not null
+ */
+l1:
+ MOVB (R9), R7
+ ADD $1, R9
+ SUBCC R0,R7, R0
+ BE ret
+ SUBCC R7,R10, R0
+ BNE l1
+ JMP rm1
+
+/*
+ * char is null
+ * align to word
+ */
+l2:
+ ANDCC $3,R9, R0
+ BE l3
+ MOVB (R9), R7
+ ADD $1, R9
+ SUBCC R0,R7, R0
+ BNE l2
+ JMP rm1
+
+/*
+ * develop byte masks
+ */
+l3:
+ MOVW $0xff, R17
+ SLL $8,R17, R16
+ SLL $16,R17, R13
+ SLL $24,R17, R12
+
+l4:
+ MOVW (R9), R11
+ ADD $4, R9
+ ANDCC R12,R11, R0
+ BE b0
+ ANDCC R13,R11, R0
+ BE b1
+ ANDCC R16,R11, R0
+ BE b2
+ ANDCC R17,R11, R0
+ BNE l4
+
+rm1:
+ SUB $1,R9, R7
+ JMP ret
+
+b2:
+ SUB $2,R9, R7
+ JMP ret
+
+b1:
+ SUB $3,R9, R7
+ JMP ret
+
+b0:
+ SUB $4,R9, R7
+ JMP ret
+
+ret:
+ RETURN
diff --git a/sys/src/libc/sparc/strcmp.s b/sys/src/libc/sparc/strcmp.s
new file mode 100755
index 000000000..e9539ebf8
--- /dev/null
+++ b/sys/src/libc/sparc/strcmp.s
@@ -0,0 +1,27 @@
+TEXT strcmp(SB), $0
+
+ MOVW s2+4(FP), R10
+
+l1:
+ MOVB 0(R7), R8
+ MOVB 0(R10), R9
+ ADD $1, R7
+ ADD $1, R10
+
+ CMP R8, R9
+ BNE l2
+
+ CMP R8, $0
+ BNE l1
+
+ MOVW R0, R7
+ RETURN
+
+l2:
+ BLEU l3
+ MOVW $1, R7
+ RETURN
+
+l3:
+ MOVW $-1, R7
+ RETURN
diff --git a/sys/src/libc/sparc/strcpy.s b/sys/src/libc/sparc/strcpy.s
new file mode 100755
index 000000000..6fd6aef05
--- /dev/null
+++ b/sys/src/libc/sparc/strcpy.s
@@ -0,0 +1,84 @@
+ TEXT strcpy(SB), $0
+
+MOVW R7, 0(FP)
+ MOVW s1+0(FP), R9 /* R9 is to pointer */
+ MOVW s2+4(FP), R10 /* R10 is from pointer */
+
+/*
+ * test if both pointers
+ * are similarly word aligned
+ */
+ XOR R9,R10, R7
+ ANDCC $3,R7, R0
+ BNE una
+
+/*
+ * make byte masks
+ */
+ MOVW $0xff, R17
+ SLL $8,R17, R16
+ SLL $16,R17, R13
+ SLL $24,R17, R12
+
+/*
+ * byte at a time to word align
+ */
+al1:
+ ANDCC $3,R10, R0
+ BE al2
+ MOVB (R10), R11
+ ADD $1, R10
+ MOVB R11, (R9)
+ ADD $1, R9
+ SUBCC R0,R11, R0
+ BNE al1
+ JMP out
+
+/*
+ * word at a time
+ */
+al2:
+ ADD $4, R9
+ MOVW (R10), R11 /* fetch */
+ ADD $4, R10
+ ANDCC R12,R11, R0 /* is it byte 0 */
+ BE b0
+ ANDCC R13,R11, R0 /* is it byte 1 */
+ BE b1
+ ANDCC R16,R11, R0 /* is it byte 2 */
+ BE b2
+ MOVW R11, -4(R9) /* store */
+ ANDCC R17,R11, R0 /* is it byte 3 */
+ BNE al2
+
+ JMP out
+
+b0:
+ MOVB R0, -4(R9)
+ JMP out
+
+b1:
+ SRL $24, R11
+ MOVB R11, -4(R9)
+ MOVB R0, -3(R9)
+ JMP out
+
+b2:
+ SRL $24,R11, R7
+ MOVB R7, -4(R9)
+ SRL $16, R11
+ MOVB R11, -3(R9)
+ MOVB R0, -2(R9)
+ JMP out
+
+una:
+ MOVB (R10), R11
+ ADD $1, R10
+ MOVB R11, (R9)
+ ADD $1, R9
+ SUBCC R0,R11, R0
+ BNE una
+
+out:
+ MOVW s1+0(FP),R7
+ RETURN
diff --git a/sys/src/libc/sparc/tas.s b/sys/src/libc/sparc/tas.s
new file mode 100755
index 000000000..15fd0b648
--- /dev/null
+++ b/sys/src/libc/sparc/tas.s
@@ -0,0 +1,7 @@
+/*
+ * tas uses LDSTUB
+ */
+ TEXT _tas(SB),$-4
+
+ TAS (R7),R7
+ RETURN
diff --git a/sys/src/libc/sparc/vlop.s b/sys/src/libc/sparc/vlop.s
new file mode 100755
index 000000000..ac36b4143
--- /dev/null
+++ b/sys/src/libc/sparc/vlop.s
@@ -0,0 +1,112 @@
+TEXT _mulv(SB), $0
+ MOVW u1+8(FP), R8
+ MOVW u2+16(FP), R13
+
+ MOVW R13, R16 /* save low parts for later */
+ MOVW R8, R12
+
+ /*
+ * unsigned 32x32 => 64 multiply
+ */
+ CMP R13, R8
+ BLE mul1
+ MOVW R12, R13
+ MOVW R16, R8
+mul1:
+ MOVW R13, Y
+ ANDNCC $0xFFF, R13, R0
+ BE mul_shortway
+ ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */
+
+ /* long multiply */
+ MULSCC R8, R9, R9 /* 0 */
+ MULSCC R8, R9, R9 /* 1 */
+ MULSCC R8, R9, R9 /* 2 */
+ MULSCC R8, R9, R9 /* 3 */
+ MULSCC R8, R9, R9 /* 4 */
+ MULSCC R8, R9, R9 /* 5 */
+ MULSCC R8, R9, R9 /* 6 */
+ MULSCC R8, R9, R9 /* 7 */
+ MULSCC R8, R9, R9 /* 8 */
+ MULSCC R8, R9, R9 /* 9 */
+ MULSCC R8, R9, R9 /* 10 */
+ MULSCC R8, R9, R9 /* 11 */
+ MULSCC R8, R9, R9 /* 12 */
+ MULSCC R8, R9, R9 /* 13 */
+ MULSCC R8, R9, R9 /* 14 */
+ MULSCC R8, R9, R9 /* 15 */
+ MULSCC R8, R9, R9 /* 16 */
+ MULSCC R8, R9, R9 /* 17 */
+ MULSCC R8, R9, R9 /* 18 */
+ MULSCC R8, R9, R9 /* 19 */
+ MULSCC R8, R9, R9 /* 20 */
+ MULSCC R8, R9, R9 /* 21 */
+ MULSCC R8, R9, R9 /* 22 */
+ MULSCC R8, R9, R9 /* 23 */
+ MULSCC R8, R9, R9 /* 24 */
+ MULSCC R8, R9, R9 /* 25 */
+ MULSCC R8, R9, R9 /* 26 */
+ MULSCC R8, R9, R9 /* 27 */
+ MULSCC R8, R9, R9 /* 28 */
+ MULSCC R8, R9, R9 /* 29 */
+ MULSCC R8, R9, R9 /* 30 */
+ MULSCC R8, R9, R9 /* 31 */
+ MULSCC R0, R9, R9 /* 32; shift only; r9 is high part */
+
+ /*
+ * need to correct top word if top bit set
+ */
+ CMP R8, R0
+ BGE mul_tstlow
+ ADD R13, R9 /* adjust the high parts */
+
+mul_tstlow:
+ MOVW Y, R13 /* get low part */
+ BA mul_done
+
+mul_shortway:
+ ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */
+ MULSCC R8, R9, R9 /* 0 */
+ MULSCC R8, R9, R9 /* 1 */
+ MULSCC R8, R9, R9 /* 2 */
+ MULSCC R8, R9, R9 /* 3 */
+ MULSCC R8, R9, R9 /* 4 */
+ MULSCC R8, R9, R9 /* 5 */
+ MULSCC R8, R9, R9 /* 6 */
+ MULSCC R8, R9, R9 /* 7 */
+ MULSCC R8, R9, R9 /* 8 */
+ MULSCC R8, R9, R9 /* 9 */
+ MULSCC R8, R9, R9 /* 10 */
+ MULSCC R8, R9, R9 /* 11 */
+ MULSCC R0, R9, R9 /* 12; shift only; r9 is high part */
+
+ MOVW Y, R8 /* make low part of partial low part & high part */
+ SLL $12, R9, R13
+ SRL $20, R8
+ OR R8, R13
+
+ SRA $20, R9 /* high part */
+
+mul_done:
+
+ /*
+ * mul by high halves if needed
+ */
+ MOVW R13, 4(R7)
+ MOVW u2+12(FP), R11
+ CMP R11, R0
+ BE nomul1
+ MUL R11, R12
+ ADD R12, R9
+
+nomul1:
+ MOVW u1+4(FP), R11
+ CMP R11, R0
+ BE nomul2
+ MUL R11, R16
+ ADD R16, R9
+
+nomul2:
+
+ MOVW R9, 0(R7)
+ RETURN
diff --git a/sys/src/libc/sparc/vlrt.c b/sys/src/libc/sparc/vlrt.c
new file mode 100755
index 000000000..b71c92f73
--- /dev/null
+++ b/sys/src/libc/sparc/vlrt.c
@@ -0,0 +1,722 @@
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef signed char schar;
+
+#define SIGN(n) (1UL<<(n-1))
+
+typedef struct Vlong Vlong;
+struct Vlong
+{
+ union
+ {
+ struct
+ {
+ ulong hi;
+ ulong lo;
+ };
+ struct
+ {
+ ushort hims;
+ ushort hils;
+ ushort loms;
+ ushort lols;
+ };
+ };
+};
+
+void abort(void);
+
+/* needed by profiler; can't be profiled */
+#pragma profile off
+void
+_addv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo + b.lo;
+ hi = a.hi + b.hi;
+ if(lo < a.lo)
+ hi++;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+void
+_subv(Vlong *r, Vlong a, Vlong b)
+{
+ ulong lo, hi;
+
+ lo = a.lo - b.lo;
+ hi = a.hi - b.hi;
+ if(lo > a.lo)
+ hi--;
+ r->lo = lo;
+ r->hi = hi;
+}
+
+#pragma profile on
+
+void
+_d2v(Vlong *y, double d)
+{
+ union { double d; struct Vlong; } x;
+ ulong xhi, xlo, ylo, yhi;
+ int sh;
+
+ x.d = d;
+
+ xhi = (x.hi & 0xfffff) | 0x100000;
+ xlo = x.lo;
+ sh = 1075 - ((x.hi >> 20) & 0x7ff);
+
+ ylo = 0;
+ yhi = 0;
+ if(sh >= 0) {
+ /* v = (hi||lo) >> sh */
+ if(sh < 32) {
+ if(sh == 0) {
+ ylo = xlo;
+ yhi = xhi;
+ } else {
+ ylo = (xlo >> sh) | (xhi << (32-sh));
+ yhi = xhi >> sh;
+ }
+ } else {
+ if(sh == 32) {
+ ylo = xhi;
+ } else
+ if(sh < 64) {
+ ylo = xhi >> (sh-32);
+ }
+ }
+ } else {
+ /* v = (hi||lo) << -sh */
+ sh = -sh;
+ if(sh <= 10) {
+ ylo = xlo << sh;
+ yhi = (xhi << sh) | (xlo >> (32-sh));
+ } else {
+ /* overflow */
+ yhi = d; /* causes something awful */
+ }
+ }
+ if(x.hi & SIGN(32)) {
+ if(ylo != 0) {
+ ylo = -ylo;
+ yhi = ~yhi;
+ } else
+ yhi = -yhi;
+ }
+
+ y->hi = yhi;
+ y->lo = ylo;
+}
+
+void
+_f2v(Vlong *y, float f)
+{
+
+ _d2v(y, f);
+}
+
+double
+_v2d(Vlong x)
+{
+ if(x.hi & SIGN(32)) {
+ if(x.lo) {
+ x.lo = -x.lo;
+ x.hi = ~x.hi;
+ } else
+ x.hi = -x.hi;
+ return -((long)x.hi*4294967296. + x.lo);
+ }
+ return (long)x.hi*4294967296. + x.lo;
+}
+
+float
+_v2f(Vlong x)
+{
+ return _v2d(x);
+}
+
+static void
+dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r)
+{
+ ulong numlo, numhi, denhi, denlo, quohi, quolo, t;
+ int i;
+
+ numhi = num.hi;
+ numlo = num.lo;
+ denhi = den.hi;
+ denlo = den.lo;
+
+ /*
+ * get a divide by zero
+ */
+ if(denlo==0 && denhi==0) {
+ numlo = numlo / denlo;
+ }
+
+ /*
+ * set up the divisor and find the number of iterations needed
+ */
+ if(numhi >= SIGN(32)) {
+ quohi = SIGN(32);
+ quolo = 0;
+ } else {
+ quohi = numhi;
+ quolo = numlo;
+ }
+ i = 0;
+ while(denhi < quohi || (denhi == quohi && denlo < quolo)) {
+ denhi = (denhi<<1) | (denlo>>31);
+ denlo <<= 1;
+ i++;
+ }
+
+ quohi = 0;
+ quolo = 0;
+ for(; i >= 0; i--) {
+ quohi = (quohi<<1) | (quolo>>31);
+ quolo <<= 1;
+ if(numhi > denhi || (numhi == denhi && numlo >= denlo)) {
+ t = numlo;
+ numlo -= denlo;
+ if(numlo > t)
+ numhi--;
+ numhi -= denhi;
+ quolo |= 1;
+ }
+ denlo = (denlo>>1) | (denhi<<31);
+ denhi >>= 1;
+ }
+
+ if(q) {
+ q->lo = quolo;
+ q->hi = quohi;
+ }
+ if(r) {
+ r->lo = numlo;
+ r->hi = numhi;
+ }
+}
+
+void
+_divvu(Vlong *q, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ q->hi = 0;
+ q->lo = n.lo / d.lo;
+ return;
+ }
+ dodiv(n, d, q, 0);
+}
+
+void
+_modvu(Vlong *r, Vlong n, Vlong d)
+{
+
+ if(n.hi == 0 && d.hi == 0) {
+ r->hi = 0;
+ r->lo = n.lo % d.lo;
+ return;
+ }
+ dodiv(n, d, 0, r);
+}
+
+static void
+vneg(Vlong *v)
+{
+
+ if(v->lo == 0) {
+ v->hi = -v->hi;
+ return;
+ }
+ v->lo = -v->lo;
+ v->hi = ~v->hi;
+}
+
+void
+_divv(Vlong *q, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ q->lo = (long)n.lo / (long)d.lo;
+ q->hi = ((long)q->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, q, 0);
+ if(nneg != dneg)
+ vneg(q);
+}
+
+void
+_modv(Vlong *r, Vlong n, Vlong d)
+{
+ long nneg, dneg;
+
+ if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) {
+ r->lo = (long)n.lo % (long)d.lo;
+ r->hi = ((long)r->lo) >> 31;
+ return;
+ }
+ nneg = n.hi >> 31;
+ if(nneg)
+ vneg(&n);
+ dneg = d.hi >> 31;
+ if(dneg)
+ vneg(&d);
+ dodiv(n, d, 0, r);
+ if(nneg)
+ vneg(r);
+}
+
+void
+_rshav(Vlong *r, Vlong a, int b)
+{
+ long t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = t>>31;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = t>>31;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_rshlv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.hi;
+ if(b >= 32) {
+ r->hi = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->lo = 0;
+ return;
+ }
+ r->lo = t >> (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->hi = t;
+ r->lo = a.lo;
+ return;
+ }
+ r->hi = t >> b;
+ r->lo = (t << (32-b)) | (a.lo >> b);
+}
+
+void
+_lshv(Vlong *r, Vlong a, int b)
+{
+ ulong t;
+
+ t = a.lo;
+ if(b >= 32) {
+ r->lo = 0;
+ if(b >= 64) {
+ /* this is illegal re C standard */
+ r->hi = 0;
+ return;
+ }
+ r->hi = t << (b-32);
+ return;
+ }
+ if(b <= 0) {
+ r->lo = t;
+ r->hi = a.hi;
+ return;
+ }
+ r->lo = t << b;
+ r->hi = (t >> (32-b)) | (a.hi << b);
+}
+
+void
+_andv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi & b.hi;
+ r->lo = a.lo & b.lo;
+}
+
+void
+_orv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi | b.hi;
+ r->lo = a.lo | b.lo;
+}
+
+void
+_xorv(Vlong *r, Vlong a, Vlong b)
+{
+ r->hi = a.hi ^ b.hi;
+ r->lo = a.lo ^ b.lo;
+}
+
+void
+_vpp(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+}
+
+void
+_vmm(Vlong *l, Vlong *r)
+{
+
+ l->hi = r->hi;
+ l->lo = r->lo;
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+}
+
+void
+_ppv(Vlong *l, Vlong *r)
+{
+
+ r->lo++;
+ if(r->lo == 0)
+ r->hi++;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_mmv(Vlong *l, Vlong *r)
+{
+
+ if(r->lo == 0)
+ r->hi--;
+ r->lo--;
+ l->hi = r->hi;
+ l->lo = r->lo;
+}
+
+void
+_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv)
+{
+ Vlong t, u;
+
+ u = *ret;
+ switch(type) {
+ default:
+ abort();
+ break;
+
+ case 1: /* schar */
+ t.lo = *(schar*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(schar*)lv = u.lo;
+ break;
+
+ case 2: /* uchar */
+ t.lo = *(uchar*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uchar*)lv = u.lo;
+ break;
+
+ case 3: /* short */
+ t.lo = *(short*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(short*)lv = u.lo;
+ break;
+
+ case 4: /* ushort */
+ t.lo = *(ushort*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ushort*)lv = u.lo;
+ break;
+
+ case 9: /* int */
+ t.lo = *(int*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(int*)lv = u.lo;
+ break;
+
+ case 10: /* uint */
+ t.lo = *(uint*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(uint*)lv = u.lo;
+ break;
+
+ case 5: /* long */
+ t.lo = *(long*)lv;
+ t.hi = t.lo >> 31;
+ fn(&u, t, rv);
+ *(long*)lv = u.lo;
+ break;
+
+ case 6: /* ulong */
+ t.lo = *(ulong*)lv;
+ t.hi = 0;
+ fn(&u, t, rv);
+ *(ulong*)lv = u.lo;
+ break;
+
+ case 7: /* vlong */
+ case 8: /* uvlong */
+ fn(&u, *(Vlong*)lv, rv);
+ *(Vlong*)lv = u;
+ break;
+ }
+ *ret = u;
+}
+
+void
+_p2v(Vlong *ret, void *p)
+{
+ long t;
+
+ t = (ulong)p;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sl2v(Vlong *ret, long sl)
+{
+ long t;
+
+ t = sl;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ul2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_si2v(Vlong *ret, int si)
+{
+ long t;
+
+ t = si;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_ui2v(Vlong *ret, uint ui)
+{
+ long t;
+
+ t = ui;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sh2v(Vlong *ret, long sh)
+{
+ long t;
+
+ t = (sh << 16) >> 16;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uh2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xffff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+void
+_sc2v(Vlong *ret, long uc)
+{
+ long t;
+
+ t = (uc << 24) >> 24;
+ ret->lo = t;
+ ret->hi = t >> 31;
+}
+
+void
+_uc2v(Vlong *ret, ulong ul)
+{
+ long t;
+
+ t = ul & 0xff;
+ ret->lo = t;
+ ret->hi = 0;
+}
+
+long
+_v2sc(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xff;
+ return (t << 24) >> 24;
+}
+
+long
+_v2uc(Vlong rv)
+{
+
+ return rv.lo & 0xff;
+}
+
+long
+_v2sh(Vlong rv)
+{
+ long t;
+
+ t = rv.lo & 0xffff;
+ return (t << 16) >> 16;
+}
+
+long
+_v2uh(Vlong rv)
+{
+
+ return rv.lo & 0xffff;
+}
+
+long
+_v2sl(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ul(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2si(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+long
+_v2ui(Vlong rv)
+{
+
+ return rv.lo;
+}
+
+int
+_testv(Vlong rv)
+{
+ return rv.lo || rv.hi;
+}
+
+int
+_eqv(Vlong lv, Vlong rv)
+{
+ return lv.lo == rv.lo && lv.hi == rv.hi;
+}
+
+int
+_nev(Vlong lv, Vlong rv)
+{
+ return lv.lo != rv.lo || lv.hi != rv.hi;
+}
+
+int
+_ltv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi < (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_gtv(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_gev(Vlong lv, Vlong rv)
+{
+ return (long)lv.hi > (long)rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}
+
+int
+_lov(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo < rv.lo);
+}
+
+int
+_lsv(Vlong lv, Vlong rv)
+{
+ return lv.hi < rv.hi ||
+ (lv.hi == rv.hi && lv.lo <= rv.lo);
+}
+
+int
+_hiv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo > rv.lo);
+}
+
+int
+_hsv(Vlong lv, Vlong rv)
+{
+ return lv.hi > rv.hi ||
+ (lv.hi == rv.hi && lv.lo >= rv.lo);
+}